XSS,三个让安全人员头疼的字母。它的全名是 Cross-Site Scripting,中文叫做跨站脚本攻击。你可能会问:“Cross-Site Scripting 的缩写不是应该是 CSS 吗?”
没错,但因为 CSS 已经被 “层叠样式表”(Cascading Style Sheets)用掉了,所以改用 XSS 来代表。这种攻击从 1999 年就存在了,到现在已经超过 25 年。
听起来是老掉牙的攻击手法,但它依然是现今最具威胁性的攻击之一。根据 IBM X-Force 的云端安全报告,XSS 是影响最大的攻击类型第一名。而知名的安全组织 OWASP(开放网络应用程序安全专案)也把它列为十大网页应用程序漏洞的第三名。
为什么一个存在这么久的攻击,到今天还是这么猖獗?这篇文章会带你拆解 XSS 攻击的原理,看一个实际的代码范例,最后介绍如何防范这种攻击。
XSS 攻击的运作原理
让我们先看看 XSS 攻击是怎么运作的。
这个攻击涉及三个角色:
| 角色 | 说明 |
|---|---|
| 攻击者(黑客) | 注入恶意代码的人 |
| 受害者(用户) | 不知情地执行恶意代码的人 |
| 网站(服务器) | 被利用来传播恶意代码的平台 |
攻击情境:黑客如何发动 XSS 攻击
第一步:黑客找到可以注入代码的地方
黑客会找到一个正常的、受信任的网站。
黑客会寻找网站上 “可以输入内容” 的地方。
例如:留言板、评论区、搜索框、个人简介字段等。
第二步:黑客注入恶意代码
假设这个网站有一个留言功能,用户可以自由输入文字。
正常用户会输入:“这个产品很棒!”
但黑客会输入一段恶意的代码。
如果网站没有做好防护,这段代码就会被存进数据库,并显示在网页上。
这时候,恶意代码就像一颗地雷,埋在网页里面,等待下一个访客触发。
第三步:受害者浏览网页,恶意代码被执行
当其他用户浏览这个页面时,浏览器会载入页面上的所有内容。包括黑客注入的那段恶意代码。
你可能会问:“我只是浏览网页而已,为什么会执行代码?”
这是因为浏览器的运作方式。当你打开一个网页时,浏览器会自动读取并执行网页里的所有代码。这就像你打开一本有声书,音乐会自动播放一样。
你不需要按任何按钮,浏览器就会帮你执行网页里的 JavaScript。
所以只要网页里藏有恶意代码,你一打开页面就中招了。这时候,网站就在不知不觉中成为黑客的帮凶。网站本身是无辜的,但它变成了 “恶意代码的传播管道”。
关键在于:这段恶意代码是在受信任的网站的环境下执行的。
浏览器不知道这段代码是黑客注入的,它只知道 “这是这个网站的代码,我应该执行它”。
这就是 XSS 攻击的可怕之处。
为什么叫做 XSS(Cross-Site Scripting)?
了解了攻击原理后,我们来看看这个名字的意思。
Scripting(脚本)
“脚本” 指的是一段可以被执行的代码。
在网页的世界里,最常见的脚本就是 JavaScript。
当浏览器看到网页里有 JavaScript 代码时,就会自动执行它。
黑客注入的恶意代码,就是一段 “恶意的脚本”。
Cross-Site(跨站)
“跨站” 是什么意思呢?
先来看 “同站” 的情况。
正常情况下,网页里的代码都是网站自己写的。
例如,example.com网站里的代码,是example.com的开发者写的。
这种情况叫做 “同站”—— 代码来自同一个网站。
但 XSS 攻击不一样。
黑客把自己的代码 “偷渡” 到别人的网站里。
例如,黑客把恶意代码塞进example.com的留言板。
这段代码不是example.com写的,而是黑客从 “外部” 注入的。
这种 “外部代码混入网站” 的情况,就是 “跨站”。
组合起来:Cross-Site Scripting
Cross-Site(跨站):恶意代码是从外部注入的,不是网站自己的Scripting(脚本):注入的是可执行的代码
合在一起就是:黑客把恶意脚本注入到别人的网站,让访客的浏览器执行。
这就是 XSS(Cross-Site Scripting)的意思。
XSS 攻击可以造成什么危害?
一旦恶意代码在受害者的浏览器中执行,黑客可以做很多事情。
篡改网页内容,欺骗用户
黑客可以用 JavaScript 修改网页上显示的内容。
想象一下这个情境:
你登录了网络银行,想要转账 10,000 元给朋友 “王小明”。
你填好表单,确认画面显示 “转账金额:10,000 元,收款人:王小明”。
看起来没问题,你按下确认。
但实际上,黑客的代码早就在背后偷偷修改了表单的数据。
你 “看到” 的是转给王小明,但 “实际送出” 的是转给黑客的账户。
等你发现账户少了钱,钱早就被转走了。
这就是篡改网页的可怕之处:你眼睛看到的,不一定是真的。
窃取 Cookie,劫持用户的登录状态
还记得 Cookie 吗?
当你登录一个网站时,服务器会产生一个 “Session ID”(会话识别码),代表你的登录身份。
这个 Session ID 会被存放在浏览器的 Cookie 里面。
之后每次你发送请求,浏览器都会自动带上这个 Cookie,服务器就知道 “这是刚才登录的那个人”。
换句话说,谁拿到你的 Cookie,谁就能假冒你的身份。
那黑客要怎么拿到你的 Cookie 呢?
这就要说到 JavaScript 和浏览器的关系了。
当 JavaScript 在网页上执行时,它可以存取浏览器提供的各种功能。
其中一个功能就是 “读取 Cookie”。
JavaScript 只要执行 document.cookie,就能读取到当前网站的所有 Cookie。
这是浏览器设计的正常功能,让网站可以管理用户的登录状态。
但问题是:黑客注入的恶意代码也是 JavaScript。
它在 “受信任的网站” 环境下执行,自然也有权限读取该网站的 Cookie。
于是黑客可以通过 JavaScript 读取你的 Cookie,然后传送到黑客自己的服务器。
黑客拿到你的 Cookie 后,就可以 “劫持”(Hijack)你的登录状态。
如果这是你的银行网站,黑客就可以用你的身份登录,把你的钱转走。
如果这是电商网站,黑客就可以用你的身份购物,让你付钱。
植入恶意软件
黑客可以通过 JavaScript 触发文件下载。
想象一下这个情境:
你只是浏览一个购物网站的商品页面。
突然间,浏览器跳出一个下载窗口,显示 “系统更新程序.exe”。
如果你不小心点了 “执行”,恶意软件就会安装到你的电脑里。
更狡猾的做法是,黑客会让下载窗口看起来像是正常的系统通知:
“您的 Flash Player 已过期,请点击更新”
很多人看到这种信息就会直接点下去,结果电脑就被植入了木马程序。
一旦恶意软件进入你的电脑,黑客就可以:
- 监控你的键盘输入,窃取所有密码
- 存取你电脑里的文件
- 把你的电脑变成僵尸网络的一部分,用来攻击其他人
导向钓鱼网站
黑客可以通过 JavaScript 把你重新导向到另一个网站。
想象一下这个情境:
你在浏览某个论坛,点了一篇看起来很正常的文章。
页面闪了一下,你发现自己还是在同一个论坛,但网页显示 “您的登录已超时,请重新登录”。
你没多想,就输入了账号密码。
但其实,你已经被导向到黑客做的假网站了。
这个假网站长得跟真的一模一样,网址也很相似(例如把 forum.com 改成 forurn.com,把 m 换成 rn)。
你输入的账号密码,全部都被黑客收走了。
这就是 “钓鱼攻击”(Phishing)。
而 XSS 让黑客可以在 “你信任的网站” 上发动钓鱼攻击,让你更难察觉异常。
另一种攻击方式:通过恶意链接
上面介绍的是 “把代码存进网站,等人来看” 的攻击方式。
但黑客还有另一种手法:直接发送一个带有恶意代码的链接给你。
这个链接可能通过 Email、短信、或社交媒体传送。
链接看起来像是正常的网站网址,但里面藏着恶意代码。
怎么藏的呢?
你可能看过这种网址:https://example.com/search?q=手机
问号后面的 q=手机 是搜索的关键字。
当你点击这个链接,网站会显示 “您搜索的关键字是:手机”。
黑客的手法是:把 “手机” 换成一段恶意代码。
当你点击这个链接时,会发生什么事?
首先,浏览器会向网站的服务器发送请求,请求里包含完整的网址。
服务器收到请求后,会从网址里取出 q= 后面的内容(也就是搜索关键字)。
接着,服务器会产生一个网页,把这个关键字放进 HTML 里面,变成 “您搜索的关键字是:OOO”。
最后,服务器把这个网页传回给浏览器。
问题就出在这里。
如果服务器没有检查 q= 后面的内容是什么,就直接放进 HTML 里,那么恶意代码也会被放进去。
所以恶意代码就从 “网址” 跑到 “网页” 上了。
浏览器收到这个网页后,看到里面有代码,就会自动执行。
浏览器不知道这段代码是从网址带进来的,它只知道 “网页里有代码,我要执行它”。
因为代码是从 “网址” 带进来,再被 “反射” 到网页上的,所以叫做反射型XSS。
这两种方式的差别在于:
| 攻击方式 | 代码存在哪里 | 如何触发 |
|---|---|---|
| 存进网站(存储型) | 网站的数据库 | 任何人浏览该页面就会中招 |
| 恶意链接(反射型) | 网址本身 | 只有点击该链接的人会中招 |
不管是哪种方式,结果都一样:黑客的代码在你的浏览器里执行了。
XSS 攻击代码范例
让我们看一个简单的范例。
这个范例不会造成实际伤害,但你可以理解攻击的原理。
假设一个网站的搜索功能,网址长这样:
plaintext
https://example.com/search?q=手机
当你搜索 “手机” 时,网页会显示:“您搜索的关键字是:手机”。
如果网站没有做好防护,黑客可以构造这样的网址:
plaintext
https://example.com/search?q=<script>alert(‘XSS’)</script>
让我们拆解这个网址:
| 部分 | 说明 |
|---|---|
| https://example.com | 网站的域名,看起来完全正常 |
| /search | 搜索功能的路径 |
| ?q= | 搜索的参数 |
| <script>alert(‘XSS’)</script> | 这就是恶意代码(Payload) |
当用户点击这个链接时,网页会显示:
plaintext
您搜索的关键字是:<script>alert(‘XSS’)</script>
浏览器看到 <script> 标签,就会执行里面的代码。
结果是画面会跳出一个警告窗口,显示 “XSS”。
这个范例只是跳出一个无害的窗口。
但黑客可以把 alert(‘XSS’) 换成任何恶意代码,例如窃取 Cookie 的代码。
开发者如何防范 XSS 攻击?
在讲防护方式之前,我们先来理解 XSS 攻击的核心问题。
浏览器在载入网页时,会区分两种东西:
- 数据:纯粹的文字内容,只是要 “显示” 给用户看的
- 代码:需要 “执行” 的指令,例如 JavaScript
正常情况下,用户在留言板输入的内容应该是 “数据”。
例如,用户输入 “这个产品很棒!”,网页应该只是把这段文字显示出来。
但 XSS 攻击的问题在于:
黑客输入的内容 “看起来像代码”,浏览器就会把它当成代码来执行。
例如,黑客输入 <script>…</script>,浏览器看到 <script> 标签,就会认为 “这是代码,我要执行它”。
所以 XSS 攻击的核心是:黑客把代码伪装成数据,让浏览器误以为是要执行的指令。
理解了这个核心问题,防护的思路就很清楚了:
确保用户输入的内容只会被当成数据,而不是代码。
具体来说,有两种做法。
防护方式一:不信任用户输入,验证并过滤
第一个原则:永远不要信任用户输入的任何内容。
不管是留言、评论、搜索关键字,还是任何用户可以输入的地方,都要进行验证。
做法是:检查并拒绝可疑的内容。
如果你的留言功能只应该接受纯文字,那就检查输入的内容:
- 纯文字 → 允许
- 包含 <script> 标签 → 拒绝
- 包含 onclick、onerror 等事件属性 → 拒绝
- 包含 SQL 指令(顺便防止 SQL 注入) → 拒绝
javascript
运行
// 简单的验证范例 function validateInput(input) { // 检查是否包含可疑的标签或属性 const dangerousPatterns = /<script|onclick|onerror|javascript:/i; if (dangerousPatterns.test(input)) { return false; // 拒绝这个输入 } return true; // 允许这个输入 }
防护方式二:输出时进行编码
第二个重要的防护是:在输出内容到网页时,进行编码(Encoding)。
什么是编码?
就是把有特殊意义的字符,转换成 “纯文字” 的对应版本。
| 原始字符 | 编码后 | 说明 |
|---|---|---|
| < | < | 小于符号 |
| > | > | 大于符号 |
| “ | " | 双引号 |
| ‘ | ' | 单引号 |
| & | & | And 符号 |
举个例子。
如果用户输入了 <script>alert(‘XSS’)</script>,经过编码后会变成:
plaintext
<script>alert(‘XSS’)</script>
当浏览器看到 < 时,它知道这只是要 “显示” 一个小于符号,而不是一个 HTML 标签的开始。
所以画面上会显示:<script>alert(‘XSS’)</script>
但这段文字只是被 “显示” 出来,不会被 “执行”。
这样就成功阻止了 XSS 攻击。
防护重点整理
| 防护方式 | 时机 | 做法 |
|---|---|---|
| 验证并过滤 | 接收用户输入时 | 检查并拒绝可疑的内容 |
| 输出编码 | 将内容显示到网页时 | 把特殊字符转换成纯文字对应版本 |
验证输入 + 编码输出,两者都要做,才能提供完整的防护。
如果你想了解更多 XSS 防护的细节,推荐参考 OWASP 提供的 Cheat Sheet。
OWASP 是知名的安全组织,他们整理了详细的代码范例和防护建议。
搜索 “OWASP XSS Prevention Cheat Sheet” 就可以找到。
小结
让我们回顾一下这篇文章的重点:
- XSS 是什么:Cross-Site Scripting,跨站脚本攻击,黑客把恶意代码注入到网页中
- 攻击原理:黑客注入的代码会在受害者的浏览器中执行,而且是在 “受信任网站” 的环境下执行
- 可能的危害:窃取 Cookie 劫持登录状态、篡改网页内容、植入恶意软件、导向钓鱼网站
- 防护方式一:验证并过滤用户输入,拒绝可疑的内容
- 防护方式二:输出时进行编码,把特殊字符转换成纯文字
XSS 攻击已经存在超过 25 年,但至今仍是最常见的攻击之一。
作为开发者,做好输入验证和输出编码,就能大幅降低被攻击的风险。
原创文章,作者:余初云,如若转载,请注明出处:https://blog.jidcy.com/jsjc/2672.html
