Web攻击十大高危漏洞实战解析:原理、案例与纵深防御指南
2026/9/9 14:31:06 网站建设 项目流程

做了快十年Web开发,也断断续续扛了七八年线上告警,我对Web攻击这件事最大的感受是:真正让你睡不着觉的,往往不是那些听起来很酷的0day,而是排行榜上翻来覆去那几张老面孔。前两年帮一家电商公司做上线前的安全巡检,登录接口被人用最基础的SQL注入 payload 刷了十几万次,WAF日志里全是' or 1=1--这种教科书级手法。你说攻击者水平高吗?不高。但就是这么老套的攻击,依然能一晚上试出好几个可注入点,因为业务代码里总有人图省事拼字符串。

所以这次我打算把这几年在实战里遇到的、以及行业公认高发的Web攻击整理成一份清单。不会只列概念,重点讲清楚每种攻击为什么会成功、在你自己的系统里最可能藏在哪个角落、以及用什么样的防御姿势才能真的挡住。看完你可以直接拿这份清单去对照自己的项目做一轮自查,比单纯背OWASP Top 10有用得多。

1. 为什么要反复聊这十个:从攻击者的视角看Web系统

很多开发兄弟看到"十大攻击"就觉得是老生常谈,但我想先说一个反直觉的事实:攻击者从来不会因为你用的是新框架、新语言就绕道走。他们找的是业务逻辑里永恒不变的东西——输入不可信、权限校验缺失、组件依赖过老。这些东西跟技术栈没太大关系,十年前写的PHP站和今天用Go写的微服务,犯的错可能是同一个。

我自己做攻防演练的时候,习惯把攻击路径分成四类,方便梳理:

攻击类别典型攻击攻击目标
注入类SQL注入、命令注入、反序列化篡改服务端执行逻辑
前端对抗类XSS、CSRF劫持用户会话、骗取浏览器信任
资源滥用类DDoS、CC攻击、慢速HTTP耗尽服务资源
信任链利用类SSRF、文件上传、路径遍历、XXE突破网络边界、读取敏感文件

这四类基本覆盖了我日常收到告警的90%以上。下面的内容就按这个逻辑展开,每一类里挑最典型的攻击方式,讲透原理和防御。你别把它当成安全课程,就当是同事之间聊项目时互相提醒:"哎,你那个上传接口得注意啊。"

2. 注入家族的常青树:SQL注入与命令注入为什么堵不住

2.1 SQL注入:从一个登录框开始的噩梦

SQL注入的原理我尽量用大白话讲:你的程序本来打算执行一条固定的SQL,但因为把用户输入直接拼进了SQL字符串,用户输入的内容里带了特殊的引号或注释符,就把你的SQL语句"截胡"了。经典的' or '1'='1之所以能通杀很多登录接口,就是因为它把原本的密码校验逻辑变成了"永真条件"。

在我处理过的真实案例里,SQL注入的危害通常分三个层次:

  • 绕过认证:不需要密码直接登录后台,这是最低级的利用方式。
  • 拖库:用UNION SELECT把其他表的数据查出来,用户名、手机号、密码哈希统统被人拿走。
  • 写马提权:如果数据库账号权限够大,可以通过INTO OUTFILE往服务器写文件,直接拿Webshell。

有个误区我必须强调:用了ORM框架不等于免疫SQL注入。我在排查一个Java项目的时候,发现团队用了MyBatis,但SQL语句是这么写的:

<select id="findUser" resultType="User"> SELECT * FROM users WHERE name = '${name}' </select>

${name}是字符串拼接,相当于裸奔。正确写法应该是#{name}走预编译。所以就算你用了框架,也一定要做一次全局搜索,排查有没有${}拼SQL的地方。

2.2 命令注入:系统调用里最容易翻车

命令注入跟SQL注入思路一样,不同的是它往操作系统命令里拼东西。最常见的场景是后台管理工具里的"网络检测"功能,让你填一个IP或域名去ping,然后代码这么干:

String cmd = "ping -c 1 " + ip; Runtime.getRuntime().exec(cmd);

这时候如果用户填的是127.0.0.1; cat /etc/passwd,分号后面那条命令就会被当作新命令执行。还有一种隐蔽的注入方式是利用反引号或者$(command),在不知不觉中执行任意命令。

防御上就两条硬规矩:

  1. 能用API绝不用命令行拼。比如Java里判断网络通不通,用InetAddress.isReachable()替代去调系统ping命令。
  2. 必须执行外部命令时,用参数数组方式调用。不要拼完整命令行字符串,而是把每一个参数作为独立数组元素传给exec,这样即使参数里带特殊字符,也只是被当作文本,不会被Shell解释。

2.3 注入类防御的统一打法

很多人只盯着加过滤函数,比如转义单引号、过滤分号,这种思路防不住变了形的payload。我比较推荐的是"三层防线的纵深配合":

  • 第一层:参数化查询/预编译。这是SQL注入的根治方案,让SQL语句结构在执行前就固定下来,用户输入永远只当数据看。
  • 第二层:输入校验。能明确格式的字段就做白名单校验,比如IP就只允许数字和点,邮箱就只允许邮箱正则。
  • 第三层:数据库最小权限。业务账号只用SELECT/INSERT/UPDATE/DELETE,坚决不给FILE权限,至少数据库被攻破时,拖库的难度会大很多。

提示:对用户输入的过滤要放在服务端做,前端校验只是用户体验,不是安全防线。

3. 前端对抗的攻防战:XSS与CSRF的相爱相杀

3.1 XSS:不直接攻击你,而是控制你的浏览器

跨站脚本攻击(XSS)的本质是把攻击者的脚本注入到你的页面里,让它在你浏览器的域下执行。为什么可怕?因为脚本在你浏览器里跑,相当于攻击者借了你的手操作你的账号——发微博、改密码、转账,全都能代劳。

我按触发方式把XSS分成三类,实际开发中都要考虑:

  • 反射型:脚本藏在URL参数里,比如?keyword=<script>alert(1)</script>,服务端没做转义直接回显到页面。这种攻击要骗用户点击恶意链接才会触发,通常配合钓鱼邮件。
  • 存储型:脚本被存到数据库里。比如在评论区提交<script>alert(1)</script>,所有打开这个页面的用户都会中招,危害更大。
  • DOM型:纯前端漏洞,服务端根本没参与。比如JS用innerHTML直接渲染URL参数里的内容,前端代码里躺着雷。

防御XSS,最核心的是分上下文编码。这句话我强调多少遍都不为过:在HTML标签里输出变量就用HTML实体编码,在JS字符串里输出变量就用JavaScript转义,在style属性里就做CSS转义。一套编码通吃所有场景是不存在的。

另外两个兜底手段很有效:

  • 设置CSP(Content Security Policy)响应头,比如Content-Security-Policy: default-src 'self',告诉浏览器只加载本域名的脚本,即使攻击者注入了脚本标签,浏览器也会拒绝执行。
  • Cookie 加HttpOnly标志,这样JavaScript读不到会话Cookie,就算XSS成功了,攻击者也没法直接偷走登录态。

3.2 CSRF:浏览器替你出手的"影子操作"

CSRF(跨站请求伪造)的思路完全反过来,攻击者不碰你的脚本,而是骗你的浏览器向目标网站发请求。原理是浏览器在发请求时会自动带上目标域名的Cookie,服务器分不清这个请求是用户主动发的,还是恶意页面诱导的。

举个真实场景:你登录了某论坛,然后在另一个标签页打开了一个带恶意图片的网站:

<img src="https://forum.example.com/post?type=delete&id=10086" />

只要你的论坛登录态没过期,这个请求发出去,帖子就被删了。整个过程你完全无感。

防御CSRF,现代框架基本都有内置方案,但很多老项目还是漏的。我建议按三层来加固:

  1. CSRF Token:服务端在渲染表单时生成一个随机Token存到session,提交时校验。攻击者无法跨域读取你页面的Token,所以伪造不了合法请求。
  2. SameSite Cookie:给Cookie加SameSite=LaxStrict,浏览器会限制跨站请求携带Cookie,这是成本最低的一招。
  3. 校验自定义请求头:如果接口只接受X-Requested-With: XMLHttpRequest,攻击者的跨域简单请求就发不出来,因为自定义Header会触发浏览器的CORS预检,而跨域预检通常过不了。

3.3 前端对抗里最容易漏的细节

这里说两个我在代码评审里反复提醒的点。

第一个是富文本编辑器。很多团队意识到要过滤用户内容,于是对评论区做了转义,结果产品要求支持富文本,就把HTML标签白名单放开了一部分。问题出在onerroronclick这类事件属性上,如果只过滤了<script>标签而忘了事件属性,攻击者用一个<img src="x" onerror="alert(1)">就能绕过。处理富文本最靠谱的方式是用成熟的白名单库,比如Java的OWASP Java HTML Sanitizer,把标签和属性都限定死。

第二个是接口是否需要校验Referer。CSRF的Token方案也有一个坑:如果Token是写在URL参数里,Referer会泄露Token;如果Token放在请求头里,就必须用AJAX,而AJAX跨域又是个坎。所以我个人更推荐把Token放在自定义Header里,然后对非AJAX请求直接拒绝,算是双保险。

4. 信任链上的两个大洞:SSRF与文件上传

4.1 SSRF:让服务器替你访问内网

服务端请求伪造(SSRF)属于这几年曝光率飙升的攻击类型。原因是云服务普及之后,内网资源越来越有"含金量",而业务里经常有这样的功能:用户提交一个URL,服务器去抓取这个URL的内容再返回给用户。比如在线图片代理、PDF预览、Webhook回调测试、部分SSO配置。

攻击者一旦发现这个功能,会把URL改成内网地址去探测:

  • http://127.0.0.1:8080探测本机端口
  • http://192.168.1.1/扫描内网网段
  • file:///etc/passwd直接读本地文件(如果后端支持file协议)

我在一次攻防演练中遇到过最狠的利用:目标是某个云主机上的应用,攻击者通过SSRF访问云平台的元数据地址,拿到了临时访问凭证,结果整个对象存储的读权限都丢了。所以SSRF的防御绝对不能只靠"过滤用户提交的URL",要按下面这套思路来:

  • 协议白名单:只允许http/https,禁用filedictgopher等协议。
  • IP黑名单(重定向也要查):解析URL拿到IP后,判断是否为内网IP、保留IP、云元数据IP,是就拒绝。注意必须对重定向后的新URL再检查一遍,否则很容易用http://example.com/redirect?target=内网地址绕过。
  • 域名解析校验:防止DNS重绑定攻击,就是在解析域名和建立连接之间,IP被悄悄换了。稳妥的做法是自行解析域名并校验IP后,再用这个IP去连接,而不是让系统去解析。

4.2 文件上传:从图片马到Webshell的经典路径

文件上传漏洞之所以危险,是因为它往往直接通向代码执行。攻击思路通常是这样的:

  1. 上传一个包含恶意代码的PHP/JSP文件,扩展名改成.jpg.png
  2. 如果服务器配置不当,比如Apache对某些扩展名的解析顺序有问题,或者Nginx配置了php_engine off但允许某些路径直接执行,这个"图片"可能被当作脚本执行。
  3. 如果访问不了,就尝试找文件包含漏洞,把上传的图片内容包含进去执行。

我之前排查过一个出问题的项目,对方做了文件类型校验,但只检查了HTTP头里的Content-Type。攻击者在Burp Suite里手改一下请求头,Content-Typeimage/png换成application/x-php,直接就上传成功了。

正确做法我建议一套组合拳

  • 魔数校验(文件头校验):用程序读取文件开头的几个字节,比如JPEG的FF D8 FF,PNG的89 50 4E 47,跟扩展名匹配才放行。
  • 随机化存储文件名:上传后重新命名为UUID.jpg,用户永远没法控制实际文件路径和扩展名。
  • 存储与执行分离:上传的文件存到独立的文件服务器或对象存储,并设置为不支持脚本执行,就算文件内容有问题,最多也就是个静态图片。
  • 图片重新编码:如果业务只需要图片,可以对上传的图片做一次重压缩/重采样,这个操作会破坏图片里嵌入的恶意代码,堪称"杀图片马"神器。

4.3 这两类攻击其实都是边界问题

聊到这里你会发现一个共性:SSRF是让服务器"主动"访问了不该访问的地方,文件上传是让服务器"被动"接收了不该接收的文件。两者本质上都是边界信任被突破了

我在设计系统架构时,习惯把这两类攻击的防御放在网关层统一处理:对外暴露的文件上传接口统一走对象存储,对外暴露的URL抓取功能统一走代理服务,并且代理服务部署在独立的内网区域,只开放必要的出站白名单。这样就算业务代码写得再粗糙,边界上还能兜住一层。

5. 藏在对象流里的定时炸弹:反序列化攻击

5.1 为什么反序列化会成为高危项

反序列化攻击属于原理稍微难理解、但一旦被打通就是直接RCE(远程代码执行)的高危漏洞。我用大白话解释:序列化是把对象变成字节流方便存储或传输,反序列化就是把字节流还原成对象。问题出在——还原的过程中,如果数据本身是攻击者构造的,就可能触发对象生命周期里的某些魔法方法,这些方法里如果存在危险操作,就被攻击者利用上了

最典型的场景是Java配合Commons Collections等常用库,攻击者构造一个特殊的序列化流,当服务端调用ObjectInputStream.readObject()时,利用库内部的反射链,一步步达到执行任意命令的目的。PHP也有类似问题,unserialize()配合魔术方**法__destruct__wakeup等,同样能打出命令执行。

我接触过的真实案例里,常见入口有这么几个:

  • 没加鉴权的Dubbo/Feign接口直接传序列化对象
  • 登录态用Java序列化对象存Cookie
  • 消息队列里传Java对象
  • 某些老旧的Session存储方式

5.2 反序列化防御的实操方案

防反序列化攻击,思路跟SQL注入的参数化很像,核心是让不可信数据永远不要进入反序列化流程。具体落地建议:

  • 优先用JSON等纯数据格式。能传字符串就传字符串,别为了省事直接传对象。大部分业务场景,用JSON序列化传输足够,完全不碰readObject
  • 加签名认证。如果你的系统确实需要反序列化对象,在序列化之前加一个HMAC签名,反序列化之前校验签名,保证数据只可能来自你自己的服务端。这样攻击者虽然能看到序列化内容,但没法篡改。
  • 类白名单过滤。使用ObjectInputFilter(JDK 9+)或ObjectInputStream的重写,只允许反序列化项目里已知的类,其他类直接拒绝。

提示:反序列化漏洞很难靠WAF拦截,因为攻击流量看起来就是一段二进制乱码。所以运行时防护(RASP)和类过滤这类"内防"手段更重要。

5.3 我见过的最隐蔽的反序列化入口

最难查的反序列化漏洞不是那种明显传对象的接口,而是隐藏在第三方组件里的。比如某次排查,日志里一直看不到异常请求,但服务器被种了Webshell。最后定位到一个老版本Apache Shiro框架,它默认用Java序列化来加密和管理Session,攻击者只需要拿到加密密钥(硬编码在开源版本里),就能构造恶意序列化数据直接打穿。这类问题纯粹属于供应链层面的漏洞,除了及时升级组件版本、关注依赖安全通告,定期做依赖SCA扫描是唯一有效的办法。

6. 资源层面的持久战:DDoS、CC与慢速HTTP

6.1 从流量型DDoS到应用层CC

我先区分一组概念:

  • DDoS(分布式拒绝服务):通常指流量型攻击,用大量僵尸主机向目标服务器发送海量数据包,把带宽或连接数打满。常见的UDP Flood、SYN Flood、ICMP Flood都属于这一类。
  • CC攻击(Challenge Collapsar):属于应用层攻击。攻击者不断请求某个消耗资源的业务接口,比如搜索、导出、登录,用大量假请求把应用服务器的CPU或数据库连接池占满。
  • 慢速HTTP攻击:最阴险的一种,攻击者建立连接后长时间不发送完整数据,把服务器的并发连接数一点点耗尽,比如Slowloris就是发一部分HTTP头然后一直吊着。

我处理过一个比较典型的CC攻击:某活动页面被刷了几百万次验证码请求,实际上攻击者并不需要登录,就是在不停地请求下发短信的接口。因为接口没有做频率限制,导致短信通道被刷爆,服务器也跟着慢下来。这种攻击不靠流量也不难识别,但特别消耗运维精力。

6.2 资源层防御的层次化思路

对DDoS这类攻击,防御没法靠应用代码自己搞定,要分层次布局:

防御层次手段应对攻击类型
网络层云服务商的DDoS高防、流量清洗大流量型DDoS
接入层CDN隐藏源站IP、负载均衡限制连接数基础攻击、端口扫描
应用层WAF拦截恶意特征、接口限流CC攻击、慢速HTTP
业务层验证码、频控、用户行为分析针对核心接口的CC

特别提醒一点:CDN隐藏源站IP是应用层防御的基石。如果源站IP泄露了,攻击者直接打源站,再多的高防也是空转。泄露源站IP的常见渠道包括:DNS历史记录、子域名的A记录、SSL证书日志、邮件头的Received字段,这些都要定期查一遍。

6.3 慢速HTTP攻击的排查与兜底

慢速HTTP攻击最难防,是因为它看起来像正常用户。排查的时候可以关注这几个指标:

  • 服务器连接数持续走高,但CPU、带宽都正常。
  • 大量连接的请求头不完整,几十秒都没发完。
  • 同一IP的连接数异常多,User-Agent千篇一律或故意模仿搜索引擎。

防御手段上,Web服务器层面可以做三件事:设置请求头超时时间(比如Nginx的client_header_timeout)、设置最大请求体大小(client_max_body_size)、限制单IP最大连接数(limit_conn)。但这只是基础配置,真正对抗慢速HTTP最好在四层负载均衡层限流,或者使用能识别"慢速连接"的WAF。

7. 古老但从未缺席的隐患:路径遍历与XXE

7.1 路径遍历:看起来很简单,绕起来千变万化

路径遍历漏洞的原理一句话:用户提交一个文件名,程序没做检查就去拼接路径读取了。最常见的攻击方式:

GET /download?filename=../../../../etc/passwd

如果后端直接拼接文件路径去读文件,攻击者就能一路往上跳目录,把系统敏感文件读出来。

你可能觉得现在不会有这么傻的代码了,但实际上我见过加了过滤还照样被绕过的:

  • 过滤了../,但攻击者用....//,因为过滤后剩下的../刚好能被解析。
  • 过滤了点号,攻击者用URL编码%2e%2e%2f,服务端如果先解码再拼接路径,正好还原。
  • 用Windows路径的..\,在支持Windows的服务器上也能生效。

防御路径遍历,业界一致认可的方法是规范化后校验前缀。具体做法是:用户提交的文件名先做一次路径规范化(比如Java的Paths.get(path).normalize()),然后判断规范化后的完整路径是否仍然在允许的根目录内,不在就拒绝。同时,拼接文件路径时不要直接用用户输入,而是用文件ID去数据库里查预定义的存储路径。

7.2 XXE:老旧XML解析器里的老熟人

XXE(XML外部实体注入)在前几年爆发过一阵,现在很多现代框架默认禁用了外部实体,但老系统里依然是高发漏洞。它的攻击原理是:XML解析器遇到DOCTYPE声明的外部实体时,会把实体指定的文件内容替换进来。攻击者构造一个这样的XML:

<?xml version="1.0"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> <foo>&xxe;</foo>

服务端解析完这个XML后,返回值里就带上了/etc/passwd的内容。更高级的玩法是盲打带外数据,实体指向攻击者控制的服务器,通过请求日志读取目标内网文件内容,绕过了回显限制。

XXE防御最简单有效的手段就是在解析XML之前禁用外部实体。Java的DocumentBuilderFactory要设置setFeature("http://apache.org/xml/features/disallow-doctype-decl", true),PHP要用libxml_disable_entity_loader(true)。如果业务确实需要解析用户上传的XML,建议先做分析,看能否用JSON替代。

7.3 这两类漏洞的共同启示

路径遍历和XXE,本质上都是用户输入被当成了路径或结构的一部分,只是载体不同(文件路径 vs XML结构)。在代码评审的时候,我会重点检查所有接收文件路径、URL、XML/JSON数据的地方,问三个问题:这个输入能到达什么位置?它会被用来拼接什么?解析逻辑里有没有放行"外部引用"的开关?这三个问题问完,大部分配置层面的漏洞就藏不住了。

8. 统一防线:纵深防御与自主排查清单

8.1 安全的本质是纵深,不是单点铜墙铁壁

聊完十种攻击,我想强调一个观点:没有任何一种防御能单独挡住所有攻击。WAF会被绕过,参数校验总有漏网之鱼,框架总有未升级的版本。所以我才一直强调"纵深防御"——每一层都拦截一部分风险,即使某一层被突破,下一层还能兜住,最终为应急响应争取时间。

一个相对完整的Web纵深防线可以这样设计:

  • 边界层:CDN、WAF、DDoS高防,拦截大部分流量型攻击和自动化工具有特征的行为。
  • 接入层:网关统一做鉴权、限流、参数校验、异常检测。
  • 应用层:代码层面落实参数化查询、输出编码、白名单校验、安全头。
  • 数据层:数据库最小权限、敏感字段加密、审计日志。
  • 运行时层:RASP、依赖SCA扫描、容器基线检测。

8.2 一张可以照着做自查的清单

每次有新项目上线,或者老项目做安全整改,我会把下面这张表拉出来当checklist过一遍:

检查项自查问题对应攻击
SQL注入是否有${}拼接SQL?所有查询是否预编译?SQL注入
命令执行是否有Runtime.execshell_exec?参数可控?命令注入
输出编码动态内容按上下文做了编码吗?CSP头配置了吗?XSS
会话安全Cookie加了HttpOnly和SameSite吗?CSRF Token覆盖了吗?CSRF
文件上传魔数校验了吗?存储和解析分离了吗?文件上传
URL请求服务器发起的请求有限制协议和网段吗?SSRF
序列化反序列化接口有类白名单或签名吗?反序列化
频率控制核心接口有限流吗?连接数有上限吗?DDoS/CC
路径读取所有文件读取都校验了规范化路径吗?路径遍历
XML解析解析XML时禁用外部实体了吗?XXE

8.3 安全巡检不是一次性的差事

最后说点经验层面的东西。我见过太多团队,上线前搞一次安全测试,修完就以为高枕无忧了。但Web系统的攻击面是不断变化的——新功能上线、第三方组件升级、配置被调整,都可能引入新的漏洞。所以我的习惯是把安全巡检嵌入到CI/CD流水线里,每次构建自动跑一遍依赖SCA扫描、常见漏洞端口检查、安全检查项扫描,有高危问题直接阻断发布。这比定期人工渗透测试更贴合实际,因为很多漏洞是在开发过程中就埋进去的,越早发现修复成本越低。

另外,告警日志一定要有人看。WAF、Web服务器、应用日志里的异常告警,如果只是躺在监控系统里没人处理,那约等于没装。可以按严重级别做分级响应:注入类的告警必须立刻人工排查,扫描类的告警可以按天汇总分析,恶意IP自动拉黑。这样安全运维才不会变成"装了个寂寞"。

我自己踩过一个坑,在这里也提醒大家:某次上线前把所有"高危漏洞"都修了,但用的是那种教条式的修复——比如一遇到XSS就加个全局编码过滤器,一遇到SQL注入就统一在控制器入口做转义。结果呢?编码过滤器把富文本标签也全编码了,业务功能直接废了;SQL转义在预编译语句面前多此一举,还引入了字符集相关的乱码问题。所以我对安全的整体态度是:理解漏洞的根本成因,在正确的层做正确的防御,而不是看见漏洞的名称就往上堆过滤器。这也是我写这份总结的初衷——希望看完之后,你能对每一种攻击的"为什么"有真正的理解,而不是只会搜"XX攻击怎么防御"然后复制粘贴。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询