从渗透测试报告到安全加固落地:一次 Web 项目整改实录

一次例行渗透测试,撕开了多少口子?

去年底,我们团队接手了一个中型 Web 项目的安全整改工作。项目上线已有两年,功能迭代频繁,但安全建设几乎空白。甲方在年度安全审计中委托第三方渗透测试团队做了一次全面评估,报告出来那天,开发群里安静了很久。

报告一共列出 17 个安全问题,其中高危 5 个、中危 7 个、低危 5 个。高危项集中在三类:SQL 注入存储型 XSSCSRF。更棘手的是,这些问题分布在核心业务链路——用户登录、订单查询、后台管理——几乎每一条都直接影响数据和资金安全。

这篇文章复盘的就是从拿到报告到完成加固的完整过程,包括我们踩过的坑和最终沉淀下来的方案。

第一轮排查:问题比报告写的更多

渗透测试报告给出的往往是 PoC 级别的验证,但真正整改时,你需要做的第一件事是举一反三

以 SQL 注入为例。报告中指出的是订单查询接口的 orderId 参数存在注入,测试人员用 1' OR '1'='1 直接绕过了查询条件。我们顺着这条线排查后发现,项目中至少有 23 处拼接 SQL 的写法,分布在 DAO 层、报表模块和几个"临时"数据导出脚本里。这些代码有一个共同特征:用字符串拼接构造 SQL,参数直接来自前端

XSS 的情况类似。报告只演示了评论区的存储型 XSS,但实际上用户昵称、商品描述、客服回复模板等多个输入点都没有做输出编码。前端用 innerHTML 渲染的地方多达十几处。

CSRF 则更隐蔽——项目使用的是 Cookie-Session 认证,但所有状态变更接口(修改密码、下单、提现)都没有校验 CSRF Token,也没有设置 SameSite 属性。

SQL 注入整改:从"打补丁"到"换地基"

最初的思路是哪里报错改哪里。但改到第五个接口时,我们发现这种"打补丁"方式不可持续——同样的漏洞模式会在新代码中反复出现。

最终我们采取了分层策略:

第一层:全面参数化查询。 所有数据库访问统一走参数化接口。Java 项目中使用 PreparedStatement,MyBatis 中强制使用 #{} 而非 ${}。对于动态表名、动态排序字段这类无法参数化的场景,采用白名单校验——只允许预定义的字段名通过。

第二层:引入 ORM 规范。 将核心业务模块迁移到 MyBatis-Plus,利用其内置的参数绑定机制,从框架层面杜绝拼接。

第三层:代码扫描卡点。 在 CI 流程中接入静态代码扫描工具(如 SonarQube),对 ${} 拼接、字符串拼接 SQL 等模式设置阻断规则。新代码一旦触发,直接构建失败。

整改后,我们做了一次全量回归测试,用 sqlmap 对 30 多个接口逐一扫描,确认无可利用注入点。

XSS 防护:输入过滤 + 输出编码,一个都不能少

XSS 的整改核心原则只有一条:永远不要信任用户输入,永远不要在不编码的情况下输出到 HTML 上下文

我们做了三件事:

  1. 统一输入过滤。 在网关层引入 XSS 过滤器,对请求参数中的 <script>onerror=javascript: 等危险模式进行转义或拦截。但要注意,输入过滤只是第一道防线,不能作为唯一手段——因为绕过方式层出不穷。

  2. 严格输出编码。 这是最关键的一步。根据输出位置不同,采用不同的编码策略:HTML 内容区使用 HTML 实体编码,JavaScript 变量使用 JS 编码,URL 参数使用 URL 编码。前端框架方面,Vue 和 React 默认会对插值进行转义,但 v-htmldangerouslySetInnerHTML 是明确的危险信号,我们逐一审查并替换为安全的渲染方式。

  3. CSP 兜底。 配置 Content-Security-Policy 响应头,限制脚本来源为自身域名,禁止内联脚本执行。即使有遗漏的 XSS 点,CSP 也能在很大程度上阻断攻击链。

CSRF 加固:Token + SameSite 双保险

CSRF 的整改相对标准化,但细节决定成败。

我们在所有状态变更请求(POST/PUT/DELETE)中强制要求携带 CSRF Token。Token 由服务端生成,绑定用户 Session,每次请求校验。前端在页面加载时从 Meta 标签或 Cookie 中读取 Token,通过请求头(如 X-CSRF-Token)发送。

同时,将所有 Cookie 的 SameSite 属性设置为 Lax(对部分跨站场景)或 Strict(对安全性要求极高的接口)。这一设置能有效阻断绝大多数跨站请求携带 Cookie 的场景。

有一个容易忽略的点:文件上传接口和 WebSocket 握手也需要 CSRF 防护。我们在整改中就发现,头像上传接口没有校验 Token,攻击者可以诱导用户上传恶意文件。

安全加固的"最后一公里":流程与意识

技术整改完成后,我们做了三件事来确保问题不反弹:

  • 建立安全编码规范。 把 SQL 参数化、输出编码、CSRF Token 校验等要求写入团队开发规范,Code Review 时逐项检查。
  • 自动化安全测试。 在 CI/CD 流程中集成 DAST 工具(如 OWASP ZAP),每次发版前自动扫描核心接口。
  • 定期渗透测试。 从"一年一次"改为"一季度一次",并将范围扩大到新上线功能。

总结

这次整改历时六周,从最初的"打补丁"心态逐步转变为系统性加固。回过头看,最深的体会是:安全不是一次性的项目,而是持续的过程。渗透测试报告只是起点,真正的挑战在于把报告中的每一个问题转化为可落地、可验证、可持续的工程实践。

对于正在面临类似整改任务的团队,我的建议是:先做全量排查摸清底数,再按风险等级分批整改,最后用流程和工具把成果固化下来。安全加固没有银弹,但有方法可循。

未经允许不得转载:任鹏个人博客 » 从渗透测试报告到安全加固落地:一次 Web 项目整改实录

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏