XSS 攻击

什么是 XSS❓

XSS(跨站脚本攻击)就是:​坏人把一个“恶意脚本”(可以理解为一段坏代码)偷偷塞进了一个正规的、你信任的网站上。当你的浏览器加载这个网站时,这个坏代码也会一起运行,然后干坏事。

🧐 ==一个生动的比喻:留言板的恶作剧==

想象一下,有一个公共留言板(比如一个论坛或者微博),每个人都可以在上面留言。

  1. ​正常情况​:大家留言都是文字,比如“今天天气真好!”。

  2. ​坏人来了​:一个坏人没有留文字,而是留了一段“指令”,比如:<script> 把看到这条留言的人的头像都改成小猪佩奇 </script>

  3. ​网站没检查​:这个留言板的管理员很粗心,没有对留言内容进行过滤,直接就把这段“指令”原封不动地显示出来了。

  4. ​你中招了​:当你下次打开这个留言板,浏览器加载页面时,看到了这条留言里的“指令”。浏览器以为这是网站合法的部分,于是就忠实地执行了这条指令——真的把你的头像换成了小猪佩奇!​

这个过程就是一次最简单的 XSS 攻击。那个“指令”就是恶意脚本,留言板就是那个被利用的可信网站。

XSS 攻击能干什么❓

坏人利用 XSS,可不仅仅是换头像这种恶作剧,他们能做的坏事包括:

  1. ​盗取你的 Cookie​:Cookie 就像是你的“登录凭证”。坏人偷到后,就能不用密码登录你的账号。

  2. ​冒充你​:在你不知情的情况下,用你的账号发帖、转账、买东西。

  3. ​监听你的键盘​:记录你在网页上输入的所有密码、信用卡号等敏感信息。

  4. ​网页挂马​:引导你下载病毒、木马软件。

  5. ​挖矿​:利用你电脑的算力偷偷为坏人挖加密货币,让你的电脑变卡。

主要的三种 XSS 类型

​反射型 XSS​

  • 非持久化​:恶意代码“存活”在 URL 中。只有点击了这个特定链接的人才会中招。
  • ​需要诱导​:必须欺骗用户去点击那个精心构造的链接。

🌰 坏人给你发了一个伪装过的链接(比如藏在钓鱼邮件里),你一点击,恶意代码就在你访问的网站上执行了。就像你按了一下坏人的“遥控器”,攻击才发生。

// 极度危险的代码:直接从URL参数获取值并输出
const search = window.location.search;
const params = new URLSearchParams(search);
const name = params.get("name");
if (name) {
  let el = document.getElementById("greet");
  el.innerHTML = "你好, " + name + "!";
}

攻击过程​:

  1. 这个页面正常的访问方式是:http://example.com/greet.html?name=张三,页面上会显示:你好, 张三!

  2. 攻击者构造了一个恶意链接:http://example.com/greet.html?name=<img src='x' onerror='alert("反射型XSS!")'>

  3. 攻击者通过邮件、短信等方式把这个链接发给受害者

  4. 受害者点击这个链接后,浏览器加载页面

    • JavaScript 代码从 URL 中取出 name 参数的值:<img src='x' onerror='alert("反射型XSS!")'>
    • 代码将这个值直接通过 innerHTML 插入到页面中。
  5. 浏览器解析这段字符串,创建了一个 img标签。它尝试加载 src='x'(一个不存在的图片),失败后立刻触发 onerror事件,执行了 alert(“反射型XSS!”)

 

​存储型 XSS​

  • ​持久化​:恶意代码被永久“存储”在了服务器(或本例中的 localStorage)中。
  • 无需诱导​:攻击者只需要成功投毒一次。之后所有访问受影响页面的用户都会自动中招,​他们访问的是完全正常的、可信的网站 URL,不需要点击任何可疑链接。这是它比反射型更危险的原因。

🌰 用留言板举个简单的例子。任何一个用户访问那个页面都会中招,不需要单独点击链接。

<input
  type="text"
  id="msg"
  placeholder="输入你的留言"
/>
<button onclick="saveMessage()">
  提交
</button>
<div id="messageBoard"></div>

<script>
// 封装下方法
function $(str) {
  return document.getElementById(str)
}
function getItem(key) {
  return localStorage.getItem(key)
}
function setItem(key, val) {
  return localStorage.setItem(key, val)
}
function saveMessage() {
    // 1. 获取用户输入
    let msg = $('msg').value;
    // 2. 存储到数据库 (用localStorage模拟)
    let storage = getItem('msgs')
    let msgs = JSON.parse(storage);
    // 漏洞:直接存储,未经任何处理
    msgs.push(msg);
    setItem('msgs',JSON.stringify(msgs));
    // 3. 更新显示
    displayMessages();
  }
function displayMessages() {
  // 4. 从“数据库”读取所有留言
  let msgs = JSON.parse(getItem('msgs'));
  let board = $('messageBoard');
  // 5. 致命漏洞:直接将每条留言作为HTML插入
  board.innerHTML = ''; // 清空旧留言
  msgs.forEach((msg) => {
    board.innerHTML += `<div>${msg}</div>`
  });
}
// 页面加载时显示所有留言
displayMessages();
</script>

攻击过程​:

  1. 攻击者在留言输入框中提交了一条恶意留言,内容不是文字,而是一段代码:<img src="x" onerror="alert('存储型XSS! 你的Cookie: ' + document.cookie)">
  2. 点击“提交”后,这段恶意代码被原封不动地保存到了 localStorage(模拟的数据库)中。
  3. 从此以后,任何用户​(包括攻击者自己和所有其他无辜访客)访问这个页面时:
    • 页面加载时调用displayMessages()函数
    • 该函数从localStorage中读取所有留言
    • 函数通过 innerHTML 将留言插入到页面
    • 浏览器解析到留言会执行 onerror 脚本
  4. 每一个访问者都会看到弹窗,他们的 Cookie 信息被窃取。

​DOM 型 XSS​

  • 特点​:更隐蔽,是前端代码本身的问题,不经过服务器,所以传统的防护手段很难发现。

🌰 网站上的 JavaScript 代码不严谨,错误地处理了网址中的片段(比如 #后面的部分),导致执行了恶意代码。

// 从 URL hash 中读取数据并直接用于构建HTML
const hash = window.location.hash;
// 去掉开头的 '#'
const username = hash.substring(1);
let el = document.getElementById("el");
el.innerHTML = `欢迎, ${username}!`;

攻击过程​:

  1. 攻击者构造一个恶意URL,并发送给受害者:http://vulnerable-site.com/welcome.html#<img src='x' onerror='alert("DOM型XSS成功!")'>

  2. 受害者点击了这个链接。

  3. 页面加载后,JavaScript 代码从 URL hash 中提取出 # 后面的内容(即恶意代码)。

  4. 代码使用 innerHTML 将恶意字符串插入到 welcomeMessage 元素中。

  5. 浏览器将其解析为 HTML,创建了 img 标签并尝试加载图片,失败后触发 onerror 事件,执行了其中的恶意 JavaScript

代码防范

  • ​过滤输入​:对用户提交的所有数据(留言、用户名等)进行严格的检查和消毒,把可疑的代码标签(如 ``)去掉或转义(变成普通文本)。
  • ​净化输出​:在把用户数据显示到页面上之前,再做一次处理。
  • 使用安全策略(如CSP),告诉浏览器只执行来自可信源的脚本。

总结

XSS 的本质是利用了 JavaScript 作为一门在浏览器中解释执行的动态语言这一特性,并滥用了浏览器将字符串数据解析为可执行代码的能力。完成"借刀杀人",坏人自己不出面,而是利用你对某个正规网站的信任,让这个网站的服务器或你的浏览器“帮”他执行恶意代码,从而攻击你。