Web安全三大漏洞CRLF、CSRF、SSRF:从原理到实战防御指南
1. 项目概述:从零开始理解三大Web安全漏洞
在Web安全领域,有三个缩写词让无数开发者和安全工程师又爱又恨:CRLF、CSRF和SSRF。它们不像SQL注入或XSS那样“声名显赫”,却常常是渗透测试报告中的“常客”,也是许多中高级漏洞的“跳板”。我见过太多项目,前端做得精美,后端逻辑复杂,却因为一个简单的CSRF漏洞导致用户账户被篡改,或者因为SSRF配置不当,让内网服务直接暴露在公网攻击者面前。这篇文章,就是我结合多年一线渗透测试和代码审计经验,为你梳理的一份从原理到实战,从基础绕过到深度利用的“避坑指南”。无论你是刚入门的安全爱好者,正在备考安全认证的学生,还是需要为自家应用做加固的开发工程师,收藏这一篇,足够你建立起对这三种攻击的立体防御认知。
简单来说,我们可以这样理解它们的核心:
- CRLF是关于“协议格式”的欺骗。它利用的是HTTP报文头与正文之间的分隔规则(回车换行,即CRLF),试图“注入”新的HTTP头或篡改响应体,常用来发起会话固定、XSS,甚至是Web缓存投毒攻击。
- CSRF是关于“身份验证”的滥用。它利用的是浏览器会自动携带用户凭证(如Cookie)访问已认证站点的机制,诱骗用户在不知情的情况下执行非本意的操作,比如修改密码、转账、发布内容。
- SSRF是关于“网络边界”的突破。它利用的是服务器端应用可以发起网络请求的能力,将服务器变为一个“跳板”或“侦察兵”,去访问或攻击其内部的、本不该从外网直接接触的服务。
下面,我们就抛开枯燥的理论,直接深入到它们的原理、利用场景和防御实践中去。
1.1 核心需求解析:为什么我们必须掌握这三者?
你可能会问,漏洞那么多,为什么偏偏要深入理解这三个?原因在于它们的“基础性”和“串联性”。
首先,它们是中高级漏洞的基石。一个复杂的漏洞利用链,起点往往是一个看似简单的SSRF,用来探测内网端口;获取到信息后,可能会利用CRLF漏洞在响应中注入恶意JavaScript,最终结合CSRF完成对管理员账户的终极控制。不理解它们,就看不懂现代高级攻击链。
其次,它们考验的是对Web基础协议和架构的理解深度。CRLF考验你对HTTP协议报文结构的理解;CSRF考验你对会话管理、浏览器同源策略的把握;SSRF则直接考验你的服务器网络架构和安全配置。吃透它们,你的Web安全功底会扎实一大截。
最后,它们的防御需要开发、运维、安全团队的共同协作。CSRF令牌需要开发在代码中实现;SSRF的过滤需要严谨的正则表达式或URL解析库,同时运维需要正确配置网络策略(如防火墙、VPC)。了解攻击,才能更好地推动防御措施落地。
2. CRLF注入攻击:在协议分隔符上做文章
CRLF注入,全称是“回车换行注入”。它的原理极其简单,但造成的后果可以很严重。HTTP协议规定,报文头和报文体之间,以及每个报文头之间,都需要用\r\n(即CRLF)来分隔。攻击者的目标,就是向应用注入这些分隔符,从而篡改HTTP响应。
2.1 攻击原理与场景拆解
想象一下,一个网站有一个功能,会将用户输入的某个参数(比如name)直接输出到HTTP响应头中,例如设置一个自定义头X-Username。正常的请求是:GET /welcome?name=Alice HTTP/1.1
服务器响应可能是:
HTTP/1.1 200 OK X-Username: Alice ... (正文)但如果攻击者构造这样的请求:GET /welcome?name=Alice%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0a...
这里,%0d%0a就是URL编码后的\r\n。缺乏过滤的服务器可能会这样处理:
- 读取
name=Alice\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK... - 将其填入响应头:
X-Username: Alice\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK... - 由于
\r\n被解析为分隔符,这个响应在客户端看来,可能就被“分割”成了两个响应,第二个响应是攻击者完全控制的。
主要利用场景:
- 响应拆分:如上例,注入两个CRLF(
\r\n\r\n)提前结束当前响应头,并开始注入一个新的、攻击者伪造的HTTP响应体,可用于XSS攻击。 - 请求走私:在反向代理(如Nginx)和后端应用服务器对请求解析不一致时,通过注入CRLF来“走私”一个额外的请求,绕过安全控制。
- 日志注入:如果应用日志记录用户输入,注入CRLF可以伪造新的日志行,干扰审计或进行欺骗。
- 设置恶意Cookie:通过注入
Set-Cookie头,为受害者设置攻击者控制的会话ID。
注意:现代浏览器和服务器对CRLF的防护已加强,纯前端的CRLF注入很难直接导致XSS,但它常与其他漏洞(如请求走私)结合,成为攻击链的关键一环。
2.2 实操:发现与利用CRLF漏洞
发现阶段:
- 参数探测:关注所有用户输入并出现在HTTP响应头中的参数。常见位置包括:重定向URL(
Location头)、文件名(Content-Disposition头)、自定义头等。 - 模糊测试:使用Burp Suite的Intruder模块,对目标参数加载CRLF测试字典(包含
%0d,%0a,%0d%0a,%0a%0d及其各种编码形式)。 - 观察响应:在Burp Repeater中发送测试payload,仔细观察原始HTTP响应。如果发现注入的CRLF字符出现在响应头中,或者响应结构被破坏(如多出一个空行后接上了你的payload),就可能存在漏洞。
利用示例:假设存在一个不安全的URL重定向:/redirect?url=https://example.com攻击payload:/redirect?url=https://attacker.com%0d%0aX-Forwarded-Host:%20attacker.com如果服务器未过滤,响应头可能变成:
HTTP/1.1 302 Found Location: https://attacker.com X-Forwarded-Host: attacker.com ...这本身可能不直接造成危害,但X-Forwarded-Host这类头常被用于生成绝对URL(如密码重置链接),可能引发其他逻辑漏洞。
一个更危险的例子——响应拆分实现XSS:如果有一个设置自定义头的端点:/setHeader?value=SomeValuePayload:/setHeader?value=Hello%0d%0aContent-Length:%200%0d%0a%0d%0a<script>alert(document.domain)</script>理想情况下(对攻击者),服务器响应会变成:
HTTP/1.1 200 OK Custom-Header: Hello Content-Length: 0 <script>alert(document.domain)</script>这样,<script>标签就被作为另一个响应的正文返回给了浏览器。虽然现代浏览器通常能抵御这种“多响应”攻击,但在某些代理缓存场景下仍可能生效。
2.3 防御之道:输入过滤与输出编码
防御CRLF的核心思想很简单:绝对不要让用户输入控制换行符出现在HTTP头中。
- 严格输入验证:
- 对于明确需要出现在头部的参数(如重定向URL),进行严格的白名单验证,只允许特定的格式和域名。
- 使用正则表达式过滤掉
\r、\n及其URL编码、双编码等形式。例如在Java中:input.replaceAll("[\r\n]", "")。
- 使用安全的API:
- 在设置HTTP响应头时,使用编程语言提供的安全函数。例如,在Python的Flask中,使用
response.headers['Custom-Header'] = safe_value,而不是手动拼接字符串。 - 避免手动拼接HTTP响应。如果需要动态生成重定向,使用框架的
redirect()函数,它会正确处理URL。
- 在设置HTTP响应头时,使用编程语言提供的安全函数。例如,在Python的Flask中,使用
- 框架与中间件配置:
- 确保使用的Web框架(如Spring Security, Django)已启用默认的CRLF防护。
- 配置Web服务器(如Nginx, Apache)对请求头进行规范化处理,拒绝包含非法字符的请求。
实操心得:在代码审计时,我习惯全局搜索Location:、setHeader、addHeader、header()等关键词,然后回溯查看设置的值是否来自未经验证的用户输入。这是发现CRLF漏洞最高效的方法之一。
3. CSRF跨站请求伪造:冒充用户的“合法”操作
CSRF可能是最容易被开发者忽视的漏洞之一,因为它不直接窃取数据,而是“借用”用户的身份和权限去做事。其攻击成本低,危害却可能很大。
3.1 攻击原理与必要条件
CSRF攻击成功的核心在于浏览器会自动携带当前站点的Cookie(包括会话Cookie)发送请求。攻击者构造一个恶意页面,其中包含一个指向目标站点的请求(如表单提交、图片加载)。当已登录目标站点的用户访问这个恶意页面时,浏览器就会自动发出这个携带用户凭证的请求,在用户不知情下完成操作。
攻击必要条件:
- 用户已登录目标网站(拥有活跃会话)。
- 目标网站存在可通过请求(尤其是GET请求)执行的有状态操作,如修改邮箱、转账、发帖。
- 请求的所有参数均可被预测或构造,没有不可伪造的随机令牌。
- 用户访问了攻击者控制的页面。
3.2 从低到高:DVWA靶场CSRF实战解析
我们以经典的DVWA靶场为例,看看不同安全级别下的CSRF漏洞形态和攻击方式。
3.2.1 Low级别:毫无防护Low级别的密码修改功能,就是一个简单的GET请求,密码作为URL参数。http://dvwa.local/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change攻击者只需构造一个隐藏了该请求的页面即可。
<img src="http://dvwa.local/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change" width="0" height="0" />用户访问这个页面,图片加载请求就会自动修改其密码。
3.2.2 Medium级别:尝试验证来源Medium级别代码会检查$_SERVER['HTTP_REFERER'],即请求来源页面的URL。它要求来源URL必须包含目标主机名(如dvwa.local)。
- 绕过方法1:空Referer。在某些情况下(如从本地
file://协议页面发起请求,或浏览器隐私设置),Referer头可能为空。攻击者可以诱导用户从本地HTML文件发起攻击。 - 绕过方法2:欺骗Referer。如果验证逻辑不严谨(例如用
strpos()检查是否包含字符串,而不是检查域名和协议),攻击者可以构造一个子域名或路径包含目标域名的页面。例如,攻击者注册域名dvwa.local.attacker.com,在其上放置攻击页面,Referer检查可能会通过。
3.2.3 High级别:使用Anti-CSRF TokenHigh级别引入了Anti-CSRF Token。每次访问修改密码页面时,服务器会生成一个随机Token并放在表单的隐藏域中。提交表单时,必须携带这个Token进行验证。
- 攻击思路:由于Token对每个用户会话是唯一的,攻击者无法直接获知。但CSRF攻击的本质是“借刀杀人”,攻击者可以先诱使用户访问一个自己可控的页面,在该页面中通过AJAX等方式,向目标站点发起一个合法请求,窃取到当前有效的Token,然后再用这个Token构造最终的CSRF请求。这需要目标站点存在其他XSS漏洞,或者JSONP接口泄露等信息,才能实现Token的窃取。因此,一个正确实现的Token机制能有效防御绝大多数CSRF攻击。
3.3 高级利用技巧与绕过
除了上述基础,CSRF还有一些“花式”利用技巧:
- JSON CSRF:如果API接受JSON格式的POST请求,且仅依赖Cookie认证,不检查
Content-Type头,攻击者可以用一个<form>配合JavaScript,将表单编码类型改为text/plain,然后提交JSON格式的数据。 - Content-Type 绕过:有些应用只检查
Content-Type是否为application/x-www-form-urlencoded或multipart/form-data,攻击者可以通过Flash或旧版浏览器发送这些类型的跨域请求。 - 结合其他漏洞:如上面提到的,结合XSS漏洞先窃取Token,再实施CSRF。或者利用URL解析差异,构造特殊的URL参数来绕过某些检查。
3.4 全面防御:不止于Token
防御CSRF需要多层次策略:
- 使用同步令牌模式:这是最有效、最主流的方法。
- 实现:服务器为每个用户会话生成一个高强度的随机令牌(CSRF Token),在渲染表单或页面时将其嵌入(如隐藏域、Meta标签)。任何会改变服务器状态的请求(POST, PUT, DELETE等)都必须提交这个令牌,服务器进行比对验证。
- 关键:Token必须与用户会话绑定,且足够随机(使用加密安全的随机数生成器)。Token不应通过Cookie发送,以避免被自动携带。
- 检查同源策略相关头部:
- 自定义Header:让前端在所有“写操作”请求的Header中携带一个自定义值(如
X-Requested-With: XMLHttpRequest)。由于浏览器限制,通过<form>或<img>发起的跨域请求无法添加自定义Header。后端验证该头是否存在。 - Origin/Referer 检查:对于所有敏感操作,严格检查
Origin或Referer头部,确保请求来自同源站点。Origin头更可靠,因为它不会像Referer那样包含完整路径,且在某些隐私场景下不会被发送。但需注意,在某些重定向或file://协议下,这些头可能为空,需要制定安全的降级策略(例如,空则拒绝)。
- 自定义Header:让前端在所有“写操作”请求的Header中携带一个自定义值(如
- 设置Cookie的SameSite属性:
- 将关键的会话Cookie设置为
SameSite=Strict或SameSite=Lax。Strict模式下,Cookie在任何跨站请求中都不会被发送;Lax模式下,允许安全的顶级导航(如链接点击)携带Cookie,但阻止来自跨站<form>POST或<img>的请求携带Cookie。这从浏览器层面极大地限制了CSRF攻击的空间。这是现代Web防御CSRF的基石之一。
- 将关键的会话Cookie设置为
- 实施双重认证:对于极其敏感的操作(如资金转账、修改主邮箱),要求用户进行二次验证(如输入短信验证码、密码),这能从根本上阻止CSRF。
实操心得:在代码审计中,不要只看表单有没有Token。要关注Token的生成是否随机、是否与会话绑定、在验证后是否立即销毁(防止重放攻击)。同时,检查所有状态变更的API端点,是否都受到了保护。我曾遇到过一个项目,Web表单有Token,但RESTful API接口却忘了加,被攻击者直接调用。
4. SSRF服务器端请求伪造:让服务器成为你的“代理”
SSRF是一种由攻击者构造请求,由服务器端发起,攻击内网或本地系统的一种漏洞。它之所以危险,是因为它能够绕过防火墙边界,访问到外部攻击者无法直接触及的内部服务。
4.1 漏洞原理与危害等级
漏洞产生于:应用提供了从服务器端发起网络请求的功能,且请求的目标URL(或部分)可由用户控制,但未经过严格过滤。 常见触发点:
- 网页内容抓取、预览功能(用户输入URL,服务器去获取)。
- 文件导入、处理功能(从指定URL下载文件)。
- Webhook回调、推送通知测试功能。
- 数据库、缓存等中间件的内置HTTP接口(如Redis的
Gopher协议)。
危害等级:
- 低危:仅能访问服务器自身(
127.0.0.1)的公开信息,或导致服务器资源消耗(DoS)。 - 中危:能扫描内网其他主机端口,探测内网拓扑和服务。
- 高危:能访问内网敏感服务(如Redis, MongoDB, Jenkins管理后台),并执行命令或读取数据。
- 严重:能结合内网服务的漏洞(如Redis未授权访问),实现远程代码执行,完全控制服务器。
4.2 利用技巧:绕过过滤与协议利用
防御SSRF通常会进行黑名单过滤(如禁止127.0.0.1、localhost、192.168.、10.等)或白名单校验。攻击者需要想方设法绕过。
4.2.1 绕过黑名单过滤
- IP地址的多种表示法:
- 十进制IP:
2130706433等价于127.0.0.1。计算方式:127*256^3 + 0*256^2 + 0*256 + 1。 - 八进制IP:
0177.0.0.1(0177是八进制的127)。 - 十六进制IP:
0x7f.0x0.0x0.0x1或0x7f000001。 - 省略格式:
127.1等价于127.0.0.1。
- 十进制IP:
- 利用URL解析差异:
- 添加端口:
http://127.0.0.1:80,过滤规则可能只匹配127.0.0.1。 - 利用
@符号:http://foo@127.0.0.1,某些解析库会提取@后的部分作为主机名。 - 利用
#片段标识符:http://example.com#@127.0.0.1,旧版本库可能错误解析。 - 利用反斜线:
http://127.0.0.1\,某些解析器会将其标准化为http://127.0.0.1/。 - 利用DNS重绑定:这是高级技巧。攻击者控制一个域名,其DNS记录的TTL极短。第一次解析时返回一个合法的外网IP(通过过滤),服务器发起请求后,在请求还未完成时,DNS记录被攻击者迅速改为
127.0.0.1。某些HTTP客户端会重用已建立的TCP连接,但有些会在新请求时重新解析主机名,从而访问到本地服务。
- 添加端口:
- 利用重定向:如果服务器会跟随重定向,可以提供一个指向内网地址的短链接或可控的重定向服务。
4.2.2 利用不同协议除了常见的http://和https://,许多编程语言的网络库支持多种协议,可能带来意外风险:
- file://:读取服务器本地文件。
file:///etc/passwd。 - dict://:可用于探测端口和服务信息。
dict://127.0.0.1:6379/info可能泄露Redis信息。 - gopher://:一个非常强大的协议,可以构造任意格式的TCP数据包。常被用于攻击内网的Redis、Memcached、FastCGI等服务。例如,通过Gopher协议向Redis发送命令,写入Webshell。
- ldap://, tftp://等。
4.3 实战:从端口扫描到RCE
假设我们发现一个存在SSRF的图片预览功能:/fetch?url=https://example.com/image.jpg
第一步:信息收集与端口扫描我们可以将url参数改为http://127.0.0.1:22。通过响应时间或错误信息,判断22端口是否开放(SSH服务)。通过Burp Intruder,批量替换端口号,可以快速绘制出服务器本机或内网其他主机的端口开放地图。
第二步:访问内部Web服务探测到内网存在一个192.168.1.10:8080的管理后台。尝试访问http://192.168.1.10:8080/admin。由于这些内部服务通常缺乏严格的身份验证(认为只在可信网络),可能直接暴露管理界面。
第三步:利用协议攻击内网服务(以Redis为例)假设通过扫描发现内网172.18.0.2:6379运行着Redis,且未设置密码。
- 构造Redis命令:我们需要通过SSRF,让服务器向Redis发送命令。由于HTTP协议不适合直接发送Redis协议,我们可以使用
gopher://协议。 - 生成Gopher Payload:Redis协议是纯文本的,命令格式如
*3\r\n$3\r\nSET\r\n$5\r\nshell\r\n$22\r\n\n\n<?php phpinfo();?>\n\n\r\n。我们需要将其进行URL编码。 - 发起攻击:构造URL:
gopher://172.18.0.2:6379/_*3%0d%0a%243%0d%0aSET%0d%0a%245%0d%0ashell%0d%0a%2422%0d%0a%0a%0a%3C%3Fphp%20phpinfo%28%29%3B%3F%3E%0a%0a%0d%0a这个Payload会向Redis写入一个键为shell,值为PHP代码的字符串。 - 写入Webshell:进一步,可以利用Redis的
config set dir和config set dbfilename命令,将数据保存到Web目录下的一个.php文件中,从而获得一个Webshell。
重要提示:此过程高度依赖于目标环境(Redis配置、Web目录权限、PHP环境等)。实际操作中步骤更为复杂,需要根据实际情况调整Payload。
4.4 防御策略:纵深防御体系
防御SSRF需要从应用层到网络层建立纵深防御。
- 应用层:输入验证与过滤
- 白名单优于黑名单:如果业务允许,只允许访问特定的、预设的域名或IP白名单。
- 统一URL解析与校验:使用语言标准库或公认安全的库(如Python的
urllib.parse, Java的java.net.URI)解析URL,并从中提取hostname。对这个主机名进行严格校验:- 解析为IP地址,检查是否属于内网IP段(127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1等)。
- 如果是域名,解析其IP地址,同样检查是否属于内网段。注意DNS重绑定攻击,需要在发起请求前解析一次,并在建立连接前再次解析,确保IP未变化,或者直接禁止使用域名。
- 禁用危险协议:在代码或库层面,显式禁用
file://、gopher://、dict://等非必要协议。
- 网络层:隔离与限制
- 最小权限原则:运行Web应用的服务器(即可能发起SSRF请求的服务器)所在的内网,应遵循最小权限原则。非必要的服务(如数据库、缓存、管理后台)不应与该服务器处于同一扁平网络。使用VPC、子网、安全组进行严格隔离。
- 出口防火墙:在服务器上配置严格的出口防火墙规则,只允许应用访问其必需的外部服务和特定的内网服务端口。
- 使用跳板机或代理:让所有出站请求都经过一个可控的代理服务器。代理服务器可以实施更严格的白名单策略。
- 响应处理:
- 对服务器获取到的远程内容,进行严格的类型检查(如检查Content-Type,验证图片文件头),避免被作为其他类型解析。
- 设置请求超时和大小限制,防止被用于DoS攻击。
实操心得:在渗透测试中,遇到任何可以输入URL的地方,我都会习惯性地尝试http://169.254.169.254。这是云平台(如AWS、阿里云)的元数据服务地址,如果存在SSRF且服务器在云上,很可能直接获取到访问密钥等敏感信息,这是最高效的“捡漏”方式之一。另外,测试时不要只盯着127.0.0.1,要尝试各种绕过技巧,并注意观察错误信息的差异,这往往是判断漏洞存在和获取内网信息的关键。
5. 联动攻击与高级场景
在实际的攻防对抗中,攻击者很少只使用单一技术。CRLF、CSRF、SSRF常常相互配合,形成更具破坏力的攻击链。
5.1 漏洞组合拳案例
场景:一个存在SSRF的社交网站“链接预览”功能。
- 利用SSRF探测内网:攻击者发现
/preview?url=存在SSRF,可以访问内网服务。 - 发现内部管理平台:通过SSRF扫描,发现内网
192.168.5.10:8080有一个Jenkins管理后台,且未设置强认证。 - 利用CRLF在响应中注入恶意请求:攻击者构造一个特殊的SSRF请求,指向一个可控的恶意服务器。该服务器返回的HTTP响应中,利用CRLF注入,包含一个自动提交的表单,这个表单的
action指向内网Jenkins的创建用户API(http://192.168.5.10:8080/securityRealm/createAccount...)。 - 触发CSRF攻击:当存在漏洞的服务器获取这个恶意响应并(由于某种解析漏洞)将其内容部分返回给用户浏览器时,内嵌的恶意表单就会在用户(此时是服务器进程的上下文)的浏览器中,向Jenkins发起一个CSRF请求,创建一个管理员账户。
- 结果:攻击者通过SSRF+CRLF,间接在内部Jenkins上实现了CSRF攻击,获得了持续的控制权限。
这个案例展示了如何将服务器端的漏洞(SSRF)转化为在客户端浏览器发起的攻击(CSRF),并利用协议解析问题(CRLF)来传递攻击载荷。
5.2 针对现代架构的攻击
随着微服务、API网关、Serverless等架构的普及,SSRF和CSRF有了新的攻击面:
- 攻击云函数/Serverless:如果云函数的触发条件包含网络请求,且未对调用源做严格限制,可能成为SSRF的新入口。攻击者可能通过云函数访问其所在VPC内的资源。
- 攻击API网关:API网关通常有缓存、转换、限流等功能。如果网关的配置允许将用户输入的一部分作为后端服务的URL,就可能存在SSRF。攻击者可能通过网关访问到其背后的其他未公开微服务。
- CSRF与JWT:在基于JWT的无状态API中,Token通常存储在
localStorage中,由JavaScript手动添加到请求头。这本身不受浏览器同源策略的Cookie自动携带机制保护,因此传统的基于Cookie的CSRF攻击无效。但是,如果应用存在XSS漏洞,攻击者可以窃取JWT Token,从而完全冒充用户。因此,在这种架构下,防御XSS比防御传统CSRF更为关键。
5.3 防御的叠加与权衡
面对联动攻击,防御也需要叠加:
- 输入验证的串联:一个参数可能先后用于构造数据库查询(防SQL注入)、输出到页面(防XSS)、作为URL发起请求(防SSRF)、作为操作的一部分(防CSRF)。每一环的验证都必须到位。
- 默认拒绝原则:在网络、应用配置上,默认拒绝所有不必要的访问。例如,服务器默认不能出访任何内网IP;API默认需要认证和CSRF Token。
- 深度防御:不要依赖单一安全措施。例如,防御SSRF,既要应用层做URL过滤,也要网络层做出口限制。防御CSRF,既要使用Token,也要设置
SameSiteCookie属性。 - 安全开发生命周期:将安全考虑嵌入需求、设计、编码、测试、部署的全过程。在代码审查时,重点关注用户输入的流向和最终使用场景。
6. 实战工具与自动化测试
理论再好,也需要工具来落地。以下是我在实战中常用的工具和方法。
6.1 手工测试与Burp Suite套件
- CRLF测试:
- Payload列表:准备包含
%0d,%0a,%0d%0a,%0a%0d,%0b,%0c等字符及其多重编码的字典。 - Burp Intruder:对目标参数进行模糊测试,观察响应中这些字符是否被原样输出或引发响应结构变化。
- Collaborator:使用Burp Collaborator生成一个唯一域名,尝试在注入的HTTP头中(如
Host头)使用该域名,看是否能收到来自目标服务器的DNS或HTTP请求,这能证明注入成功且服务器发起了新请求。
- Payload列表:准备包含
- CSRF测试:
- 手工验证:抓取一个敏感操作(如修改邮箱)的请求,移除或修改可能的Token/Referer头,在Repeater中重放,看是否成功。
- 生成POC:Burp Suite的
Engagement tools->Generate CSRF PoC功能可以一键生成攻击测试页面。 - 检查Token:使用
Sequencer分析CSRF Token的随机性。
- SSRF测试:
- 基础探测:尝试
http://127.0.0.1,http://localhost,http://169.254.169.254,http://[::1]等。 - 端口扫描:使用Intruder对常见端口(22, 80, 443, 8080, 6379, 27017等)进行批量请求,根据响应时间、状态码、长度差异判断端口开放情况。
- 利用Collaborator:这是最有效的方法之一。将Collaborator域名作为URL参数传入,观察是否能收到来自目标服务器的出站请求。这不仅能证明漏洞存在,还能看到请求的完整信息(源IP、User-Agent等),用于判断服务器所处环境。
- 协议探测:尝试
file:///etc/passwd,dict://127.0.0.1:6379/info,gopher://等。
- 基础探测:尝试
6.2 自动化扫描与辅助脚本
对于大型项目,可以借助一些自动化工具进行初步筛查:
- OWASP ZAP:其主动扫描规则包含了CSRF Token检测、SSRF测试等。
- Nuclei:有大量社区贡献的SSRF、CRLF检测模板,可以快速对目标进行批量检测。
- 自定义脚本:对于复杂的绕过场景(如DNS重绑定),可能需要编写自定义脚本。例如,一个简单的Python脚本,用于快速生成各种格式的IP地址进行测试:
import sys target = "127.0.0.1" # 十进制 dec_ip = str(int(target.split('.')[0]) * 256**3 + int(target.split('.')[1]) * 256**2 + int(target.split('.')[2]) * 256 + int(target.split('.')[3])) print(f"Decimal: http://{dec_ip}/") # 十六进制 hex_ip = "0x" + ''.join([f'{int(x):02x}' for x in target.split('.')]) print(f"Hex (dotless): http://{hex_ip}/") # 省略格式 print(f"Dotted省略: http://{target.split('.')[0]}.{target.split('.')[1]}/") # 127.06.3 漏洞挖掘思路总结
- 参数溯源:找到所有用户可控的输入点,尤其是那些最终会用于发起网络请求(
curl,file_get_contents,HttpClient)、设置HTTP头、进行重定向的参数。 - 功能点分析:重点关注“分享”、“预览”、“获取”、“导入”、“转码”、“代理”、“回调测试”等功能。
- 错误信息利用:仔细观察不同输入下服务器的错误响应。超时、连接拒绝、DNS解析失败等不同错误,可以帮你判断内网IP、端口状态。
- 旁敲侧击:如果直接访问
/etc/passwd被过滤,可以尝试file:///proc/self/cwd/../../etc/passwd(利用路径遍历)。如果直接IP被禁,尝试域名重绑定或利用URL解析特性。
7. 从攻击者视角看防御:我的加固检查清单
最后,切换回防御者视角。在代码上线前或进行安全审计时,我会对照下面这份清单进行检查:
CRLF注入检查项:
- [ ] 所有设置HTTP响应头的代码,是否对用户输入进行了严格的
\r\n过滤? - [ ] 日志记录函数,是否对换行符进行了处理?
- [ ] 重定向函数(如
redirect())是否使用框架安全函数,并验证了目标URL? - [ ] Web服务器(Nginx/Apache)是否配置了合适的请求头过滤规则?
CSRF防御检查项:
- [ ] 所有会改变服务器状态的操作(POST, PUT, DELETE, PATCH)是否都要求了CSRF Token?
- [ ] CSRF Token是否足够随机(使用加密安全的RNG)?是否与用户会话绑定?
- [ ] Token是否在一次使用后立即失效(防止重放)?对于Ajax请求,Token是如何传递和验证的?
- [ ] 会话Cookie是否设置了
SameSite=Lax或Strict属性? - [ ] 对于重要的API,是否考虑了
Origin/Referer头检查作为补充? - [ ] 是否存在通过GET请求进行状态变更的操作?(应坚决改为POST)
SSRF防御检查项:
- [ ] 所有需要发起外部网络请求的功能,其目标URL参数是否经过严格的解析和验证?
- [ ] 是否使用了白名单机制?如果必须使用黑名单,是否涵盖了所有内网IP段(包括IPv6)和各种IP表示法?
- [ ] 是否禁用了
file://、gopher://、dict://等危险协议? - [ ] 发起请求的客户端(如
curl)是否设置了协议限制和重定向跟随限制? - [ ] 服务器所在主机的网络出口防火墙是否进行了最小化配置?
- [ ] 内网服务(数据库、缓存、管理后台)是否与Web服务器进行了有效的网络隔离?
- [ ] 云服务器实例的元数据服务(
169.254.169.254)是否已通过防火墙或云安全组进行了访问控制?
安全是一个持续的过程,而非一劳永逸的状态。理解攻击者的思维和手段,是构建有效防御的最快路径。希望这篇长文能帮你把这三种常见但危险的漏洞彻底理清,无论是用于渗透测试、安全开发还是架构设计,都能多一份笃定,少一个隐患。