XSS
XSS (Cross-Site Scripting),跨站脚本攻击,因为缩写和 CSS重叠,所以只能叫 XSS。跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。
跨站脚本攻击有可能造成以下影响:
- 利用虚假输入表单骗取用户个人信息。
- 利用脚本窃取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送恶意请求。
- 显示伪造的文章或图片。
XSS 的原理是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而可以达到攻击者盗取用户信息或其他侵犯用户安全隐私的目的。
XSS 的攻击方式千变万化,但还是可以大致细分为几种类型。
- 存储型XSS:注入型脚本永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器上传回并执行。
- 反射型XSS: 当用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。Web服务器将注入脚本,比如一个错误信息,搜索结果等 返回到用户的浏览器上。浏览器会执行这段脚本,因为,它认为这个响应来自可信任的服务器。
- 基于DOM的XSS:被执行的恶意脚本会修改页面脚本结构
XSS最终是要以“输出编码”来完成攻击的,我们将“输出编码”的攻击方法总结为以下几种
- 在 HTML 标签中输出变量;
- 在 HTML 属性中输出变量;
- 在 script 标签中输出变量;
- 在事件中输出变量;
- 在 CSS 中输出变量;
- 在 URL 中输出变量。
表现为:
- 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。
- 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。
- 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。
- 在标签的 href、src 等属性中,包含 javascript: 等可执行代码。
- 在 onload、onerror、onclick 等事件中,注入不受控制代码。
- 在 style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)。
- 在 style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)。
- 在url参数上(下面有例子)
反射型 XSS
特征:
攻击者可以直接通过 URL (类似:https://xxx.com/xxx?default=)) 注入可执行的脚本代码。不过一些浏览器如Chrome其内置了一些XSS过滤器,可以防止大部分反射型XSS攻击。
措施
- Web 页面渲染的所有内容或者渲染的数据都必须来自于服务端。
- 尽量不要从 URL,document.referrer,document.forms 等这种 DOM API 中获取数据直接渲染。
- 尽量不要使用 eval, new Function(),document.write(),document.writeln(),window.setInterval(),window.setTimeout(),innerHTML,document.createElement() 等可执行字符串的方法。
- 如果做不到以上几点,也必须对涉及 DOM 渲染的方法传入的字符串参数做 escape 转义。
- 前端渲染的时候对任何的字段都需要做 escape 转义编码。
_// 防御xss漏洞_
_// 对字符串进行 XSS 过滤_
_//_ _https://eggjs.org/zh-cn/core/security.html#%E5%BC%80%E5%90%AF%E4%B8%8E%E5%85%B3%E9%97%AD%E9%85%8D%E7%BD%AE_
**function** decodeInput(char) {
switch (char) {
case "&":
return "&";
case "<":
return "<";
case ">":
return ">";
case '"':
return """;
case "'":
return "'";
case " ":
return " ";
default:
return char + "";
}
}
如果拼接 HTML 是必要的,就需要采用合适的转义库,对 HTML 模板各处插入点进行充分的转义。
常用的模板引擎,如 doT.js、ejs、FreeMarker 等,对于 HTML 转义通常只有一个规则,就是把 & < > " ' / 这几个字符转义掉,确实能起到一定的 XSS 防护作用,但并不完善:
XSS 安全漏洞 | 简单转义是否有防护作用 | |
---|---|---|
HTML 标签文字内容 | 有 | 例如前端工程,针对html 使用 https://jsxss.com/zh/index.html |
HTML 属性值 | 有 | 例如前端工程,针对html 使用https://jsxss.com/zh/index.html |
CSS 内联样式 | 无 | 例如前端工程,针对html 使用 https://jsxss.com/zh/index.html |
内联 JavaScript | 无 | 过滤,包括前端输入时候,服务端入库前,服务端输出前,前端触发前 |
内联json | 无 | 实现escapeJson函数 |
跳转链接 | 无 | 单独处理 |
所以要完善 XSS 防护措施,我们要使用更完善更细致的转义策略。
例如 Java 工程里,常用的转义库为 org.owasp.encoder。以下代码引用自 org.owasp.encoder 的官方说明
__
__
__
场景实践 针对这种,防护比较简单,我们直接过滤即可。
这个问题StackOverflow 有类似讨论 is-it-secure-to-use-window-location-href-directly-without-validation 存储型 XSS(持久型) 存储型 XSSXSS 漏洞,一般存在于 Form 表单,富文本编辑提交等交互功能,如文章留言,提交文本信息等,黑客利用的 XSS 漏洞,将内容经正常功能提交进入数据库持久保存,当前端页面获得后端从数据库中读出的注入代码时,恰好将其渲染执行。
场景实践 去掉script即可,因为无论怎么注入的,最终它是需要在页面里面通过script标签加载的 DOM型XSS (纯前端涉及的主要是这里) Dom Based XSS 并非按照“数据是否存在服务器端”来划分,DOM Based XSS 从效果上来说也是反射型 XSS。单独划分开来,是因为 DOM Based XSS 形成原因比较特别——通过修改页面的 DOM 节点形成的 XSS。 DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。
Attributes Methods
场景实践 注意我这里的例子很多同学会困惑这个明明是储存型的或者是下面要讲的富文本的,为什么会放在这里, 其实是因为最终页面使用的v-html 在发帖处输入 使用这个库js-xss, 白名单, 过滤掉a标签多余属性: https://github.com/leizongmin/js-xss/blob/master/lib/default.js
富文本(属于储存型,难点) 富文本和我们平常XSS 案例不一样,不是简单的在输出位置做针对性过滤就行的,因为既然提供富文本编辑器,那么肯定是内容需要支持某些标签输入输出,如果 XSS FILTER 直接干掉了标签,会影响到内容的展示,所以有必要对富文本情况拎出来单独处理。 富文本的xss防御比较多样化,除了上述的一些,还应注意使用白名单标签,避免使用黑名单标签。 处理富文本和输入检查是一致的,我们会对用户输入的信息添加一个白名单,比如在富文本里面允许用户输入等标签,不允许输入') 以上示例使用了 js-xss 来实现,可以看到在输出中保留了 h1 标签且过滤了 script 标签。 5) HttpOnly Cookie。 这是预防XSS攻击窃取用户cookie最有效的防御手段。Web应用程序在设置cookie时,将其属性设为HttpOnly,就可以避免该网页的cookie被客户端恶意JavaScript窃取,保护用户cookie信息
6) IP访问限制 系统后台增加ip访问限制,主要是针对获取cookie伪造管理员登入方式 7)白名单无法解决的,禁止掉触发事件 8)验证码:防止脚本冒充用户提交危险操作。 9)输入内容长度控制 对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度。 10)Javacript过滤(上面已经讲了一些,涉及的内容很多,具体问题具体分析)
**web安全从来是攻防的过程,攻击者的脑回路你总有猜不出来的时候, 所以总结来说,处理的时候可以有以下思路__
__
var msg = "<%= Encode.forJavaScript(UNTRUSTED) %>";
alert(msg);
__
var __INITIAL_STATE__ = JSON.parse('<%= Encoder.forJavaScript(data.to_json) %>');
__
onclick="alert('<%= Encode.forJavaScript(UNTRUSTED) %>');">
click me
__
__
__
link
_// 根据项目情况进行过滤,禁止掉_ _"javascript:"_ _链接、非法 scheme 等_
allowSchemes = ["http", "https"];
valid = isValid(getParameter("redirect_to"), allowSchemes);
if (valid) {
跳转...
} else {
跳转...
}
element.innerHTML = " Tags and markup";
element.outerHTML = " Tags and markup";
document.write(" Tags and markup");
document.writeln(" Tags and markup");
element.innerHTML = "<%=Encoder.encodeForJS(Encoder.encodeForHTML(untrustedData))%>";
element.outerHTML = "<%=Encoder.encodeForJS(Encoder.encodeForHTML(untrustedData))%>";
document.write("<%=Encoder.encodeForJS(Encoder.encodeForHTML(untrustedData))%>");
document.writeln("<%=Encoder.encodeForJS(Encoder.encodeForHTML(untrustedData))%>"
// ->
XSS Demo
<script>alert("xss");</script>console.log(html)
HttpOnly 是一个设置 cookie 是否可以被 javasript 脚本读取的属性,浏览器将禁止页面的 Javascript 访问带有 HttpOnly 属性的 Cookie。
const express = require('express');
const app = express();
app.use(function(req, res, next) {
res.setHeader('Content-Type', 'text/html;charset=utf-8');
// 模拟设置用户tookie
//可以把类似敏感信息都设置 httpOnly: true
res.cookie('token', 'zkskskdngqkkkgn245tkdkgj', { httpOnly: true });
next();
});