XSS,即 Cross Site Script,是跨站脚本攻击, 其原本缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,因而在安全领域叫做 XSS。
XSS 攻击是指攻击者在网站上注入恶意的客户端代码,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。
攻击者对客户端网页注入的恶意脚本一般包括 JavaScript,有时也会包含 HTML 和 Flash。 有很多种方式进行 XSS 攻击,但它们的共同点为:将一些隐私数据像 cookie、session 发送给攻击者,将受害者重定向到一个由攻击者控制的网站,在受害者的机器上进行一些恶意操作。
XSS 攻击可以分为 3 类:
- 反射型(非持久型)
- 存储型(持久型)
- 基于 DOM。
CSRF 和 XSS 傻傻分不清
何伟 信息网络安全 ppt
一句话概括 CSRF 的危害:盗用受害者身份,受害者能做什么,攻击者就能以受害者的身份做什么。
CSRF 与 XSS 的区别:CSRF 攻击者并没有拿到用户的权限,只是借用户的权限来完成攻击;而 XSS 可以通过盗取 cookie 来获取用户权限来进行破坏。
反射型
反射型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器,这种攻击方式往往需要攻击者诱使用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。
恶意链接的地址指向了 localhost:8001/?q=111&p=222。 服务(Node)处理恶意链接的请求:
const http = require("http");
function handleReequest(req, res) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.writeHead(200, { "Content-Type": "text/html; charset=UTF-8" });
// 直接往请求地址写入脚步
res.write('<script>alert("反射型 XSS 攻击")</script>');
res.end();
}
const server = new http.Server();
server.listen(8001, "127.0.0.1");
server.on("request", handleReequest);
当用户点击恶意链接时,页面跳转到攻击者预先准备的页面,会发现在攻击者的页面执行了 js 脚本:
这样就产生了反射型 XSS 攻击。攻击者可以注入任意的恶意脚本进行攻击,可能注入恶作剧脚本,或者注入能获取用户隐私数据(如 cookie)的脚本
存储型
存储型 XSS 的攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
存储型 XSS 会把用户输入的数据 "存储" 在服务器端,当浏览器请求数据时,脚本从服务器上传回并执行。这种 XSS 攻击具有很强的稳定性。
比较常见的一个场景是攻击者在社区或论坛上写下一篇包含恶意 JavaScript 代码的文章或评论,文章或评论发表后,所有访问该文章或评论的用户,都会在他们的浏览器中执行这段恶意的 JavaScript 代码。
存储型 XSS 跟反射型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
<input type="text" id="input" />
<button id="btn">Submit</button>
<script>
const input = document.getElementById("input");
const btn = document.getElementById("btn");
let val;
input.addEventListener(
"change",
(e) => {
val = e.target.value;
},
false
);
btn.addEventListener(
"click",
(e) => {
fetch("http://localhost:8001/save", {
method: "POST",
body: val,
});
},
false
);
</script>
启动一个 Node 服务监听 save 请求。
const http = require("http");
let userInput = "";
function handleReequest(req, res) {
const method = req.method;
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
if (method === "POST" && req.url === "/save") {
let body = "";
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
if (body) {
userInput = body;
}
res.end();
});
} else {
res.writeHead(200, { "Content-Type": "text/html; charset=UTF-8" });
res.write(userInput);
res.end();
}
}
const server = new http.Server();
server.listen(8001, "127.0.0.1");
server.on("request", handleReequest);
当用户点击提交按钮将输入信息提交到服务端时,服务端通过 userInput 变量保存了输入内容。当用户通过 http://localhost:8001/${id}
访问时,服务端会返回与 id 对应的内容(本示例简化了处理)。如果用户输入了恶意脚本内容,则其他用户访问该内容时,恶意脚本就会在浏览器端执行:
var element = document.getElementById("root");
element.innerHTML = '<script>alert("XSS Attack");</script>';
基于 DOM
DOM 型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
<h2>XSS:</h2>
<input type="text" id="input" />
<button id="btn">Submit</button>
<div id="div"></div>
<script>
const input = document.getElementById("input");
const btn = document.getElementById("btn");
const div = document.getElementById("div");
let val;
input.addEventListener(
"change",
(e) => {
val = e.target.value;
},
false
);
btn.addEventListener(
"click",
() => {
div.innerHTML = `<a href=${val}>testLink</a>`;
},
false
);
</script>
用户提交之后,页面代码就变成了:<a href onlick="alert(/xss/)">testLink</a>
此时,用户点击生成的链接,就会执行对应的脚本:
防范 XSS
现在主流的浏览器内置了防范 XSS 的措施。但对于开发者来说,也应该寻找可靠的解决方案来防止 XSS 攻击。
HttpOnly 防止劫取 Cookie
HttpOnly 最早由微软提出,至今已经成为一个标准。浏览器将禁止页面的 Javascript 访问带有 HttpOnly 属性的 Cookie。
-
什么是 HttpOnly?
如果 cookie 中设置了 HttpOnly 属性,那么通过 js 脚本将无法读取到 cookie 信息,这样能有效的防止 XSS 攻击,窃取 cookie 内容,这样就增加了 cookie 的安全性;
即便是这样,也不要将重要信息存入 cookie。XSS 全称 Cross SiteScript,跨站脚本攻击,其原理是攻击者向有 XSS 漏洞的网站中输入(传入)恶意的 HTML 代码,这段 HTML 代码会自动执行,从而达到攻击的目的。主要盗取用户 Cookie、破坏页面结构、重定向到其它网站等。
-
HttpOnly 的设置样例(服务端代码)
response.setHeader( "Set-Cookie", "cookiename=httponlyTest;Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly" ); // 例如:设置cookie response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly"); //设置多个cookie response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly"); response.addHeader("Set-Cookie", "timeout=30; Path=/test; HttpOnly"); // 设置https的cookie response.addHeader("Set-Cookie", "uid=112; Path=/; Secure; HttpOnly");
上文有说到,攻击者可以通过注入恶意脚本获取用户的 Cookie 信息。通常 Cookie 中都包含了用户的登录凭证信息,攻击者在获取到 Cookie 之后,则可以发起 Cookie 劫持攻击。所以,严格来说,HttpOnly 并非阻止 XSS 攻击,而是能阻止 XSS 攻击后的 Cookie 劫持攻击。
输入检查
不要相信用户的任何输入。 对于用户的任何输入要进行检查、过滤和转义
在 XSS 防御中,输入检查一般是检查用户输入的数据中是否包含 <,> 等特殊字符,如果存在,则对特殊字符进行过滤或编码,这种方式也称为 XSS Filter。
而在一些前端框架中,都会有一份 decodingMap, 用于对用户输入所包含的特殊字符或标签进行编码或过滤,如果输入带 script 标签的内容,会直接过滤掉.
输出检查
用户的输入会存在问题,服务端的输出也会存在问题。在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。对输出内容进行有规则的过滤之后再输出到页面中。