前端是直面用户的窗口,其安全性却是最容易被忽略的一环。本文将介绍:
一、什么是XSS攻击
1.1 定义
XSS攻击的本质是,在url参数中加入恶意的javascript代码,通过浏览器解析和运行恶意代码,达到恶意攻击用户的目的。
官方解释:
XSS是跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而恶意攻击用户的目的。
1.2 XSS攻击的危害
攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。或是通过前端请求操作数据库,获取数据库内容或修改数据库内容。
二、 XSS攻击类型
XSS攻击分为反射型攻击和存储型攻击。
2.1 反射型攻击
反射型攻击也叫非持久型XSS攻击,即在请求时将恶意代码插入URL中,作为参数提交到服务器。服务器解析数据并将响应数据返回到浏览器中,最终由浏览器执行。
2.1.1 攻击步骤
- 攻击者创造出带有恶意代码的特殊url,并发送到服务器
- 用户打开这个特殊的url,服务器网站将参数从url中解出,拼接在响应中返回给浏览器
- 浏览器解析响应时,这段恶意代码也被浏览器执行
- 这段恶意代码可以窃取用户信息并发送到攻击者网站,或冒充用户进行指定操作。
2.1.2 例子
如:
http://localhost:8080/helloController/search?name=<script>alert("hey!")</script>
http://localhost:8080/helloController/search?name=<img src='w.123' onerror='alert("hey!")'>
http://localhost:8080/helloController/search?name=<a onclick='alert("hey!")'>点我</a>
2.2 存储型攻击
存储型攻击也叫持久型攻击,即一次攻击成功后,下一次请求时就不用再带上恶意XSS代码了。第一次注入的XSS代码会被服务器存储(缓存或数据库),且在下一次被前端从服务器获取,返回到浏览器时被浏览器执行。常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
2.2.1 攻击步骤
存储型 XSS 的攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
2.2.2 恶意盗取用户信息
常见在留言板,IM聊天软件中出现。如某web聊天软件中,小王给小明发了一条XSS攻击消息
<script>alert('你被XSS攻击啦')</script>
当小明接收到这条消息时,就会出现一个内容为 “你被XSS攻击啦” 的弹窗。若这条消息没有被删掉,则小明每次打开这个消息的窗口,弹窗都会弹出来,因为这段XSS恶意代码已经被执行了。
他可以被包装得更具有隐蔽性
hello,你好<script>xxxxxxxxxx</script>
这种攻击常用于窃取用户信息,如cookie,token,账号密码等
2.2.3 插入图片
依旧是留言板,当留言板浏览器执行到这段代码时,会出现一张图片,点击图片,则会被重定向到另一个网站
< img onclick="window.location.href='http://www.baidu.com'" width='300' src='img/webwxgetmsgimg.jpg'/>
2.2.4 流量劫持、恶意跳转
当用户打开网址时,会被恶意重定向到指定的网址
<script>window.location.href="http://www.baidu.com";</script>
2.3 DOM型攻击
2.3.1 攻击步骤
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
2.4 三种攻击类型的区别
攻击类型 | 恶意代码存储位置 | 插入点 | 解决端 |
反射型XSS | URL | HTML | 服务端 |
存储型XSS | 后端数据库或缓存中 | HTML | 服务端 |
DOM型XSS | 后端数据库/前端存储/URL | 前端JAVASCRIPT | 前端 |
三、 如何防范XSS攻击
3.1 设置Cookie HttpOnly 为 true
服务端可以通过设置 HttpOnly 来防止XSS代码获取到用户的cookie。
@RequestMapping("/login")
@ResponseBody
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
......
Cookie cookie = new Cookie("access_token", UUID.randomUUID().toString());
cookie.setHttpOnly(true); // 此处设置HttpOnly为true
cookie.setPath("/");
cookie.setDomain("localhost");
response.addCookie(cookie);
response.sendRedirect("http://localhost:8088/index.html");
}
3.2 纯前端渲染
纯前端渲染即避免在前端代码中出现html拼接的情况。一个合理干净的前端代码应该是:
- 前端页面只负责加载一个静态dom
- 通过执行写好的JavaScript,通过网络请求的方式获取数据
- 调用DOM Api将数据更新到页面上
在很多内部、管理系统中,采用纯前端渲染是非常合适的,但纯前端渲染,会带来以下问题:
- 无法防止DOM 型 XSS 漏洞(例如
onload
事件和href
中的javascript:xxx
等 - 对于性能要求高,或有 SEO 需求的页面,我们仍然要面对拼接 HTML 的问题
3.3 使用合适的转义库
3.3.1 前端转义函数
/**
* @function escapeHTML 转义html脚本 < > & " '
* @param a -
* 字符串
*/
escapeHTML: function(a){
a = "" + a;
return a.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");;
},
/**
* @function unescapeHTML 还原html脚本 < > & " '
* @param a -
* 字符串
*/
unescapeHTML: function(a){
a = "" + a;
return a.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
},
3.3.2 后端转义库
Java 工程里,常用的转义库为 org.owasp.encode
3.4 预防DOM型XSS攻击
DOM型XSS攻击,实际是前端JavaScript将不可信的数据当作代码执行力。
应尽量使用 .textContent
、.setAttribute()
代替.innerHTML
、.outerHTML
、document.write()
,若必须使用时要特别小心,不要把不可信的数据作为 HTML 插到页面上
如果用 Vue/React 技术栈,并且不使用 v-html
/dangerouslySetInnerHTML
功能,就在前端 render 阶段避免 innerHTML
、outerHTML
的 XSS 隐患。
DOM 中的内联事件监听器,如 location
、onclick
、onerror
、onload
、onmouseover
等,<a>
标签的 href
属性,JavaScript 的 eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。
四、 线上XSS检测
4.1 特殊恶意字符串
将这段字符串输入到输入框中提交这个字符串,或者把它拼接到 URL 参数上,就可以进行检测了
http://xxx/search?keyword=jaVasCript%3A%2F*-%2F*%60%2F*%60%2F*%27%2F*%22%2F**%2F(%2F*%20*%2FoNcliCk%3Dalert()%20)%2F%2F%250D%250A%250d%250a%2F%2F%3C%2FstYle%2F%3C%2FtitLe%2F%3C%2FteXtarEa%2F%3C%2FscRipt%2F--!%3E%3CsVg%2F%3CsVg%2FoNloAd%3Dalert()%2F%2F%3E%3E
4.2 扫描工具
除了手动检测之外,还可以使用自动扫描工具寻找 XSS 漏洞,例如 Arachni、Mozilla HTTP Observatory、w3af 等
五、总结
充分遵循以下原则,能够降低XSS攻击得手的概率
- 利用模板引擎
开启模板引擎自带的 HTML 转义功能。例如:
在 ejs 中,尽量使用<%= data %>
而不是<%- data %>
;
在 doT.js 中,尽量使用{{! data }
而不是{{= data }
;
在 FreeMarker 中,确保引擎版本高于 2.3.24,并且选择正确的freemarker.core.OutputFormat
。 - 避免内联事件
尽量不要使用onLoad="onload('{{data}}')"
、onClick="go('{{action}}')"
这种拼接内联事件的写法。在 JavaScript 中通过.addEventlistener()
事件绑定会更安全。 - 避免拼接 HTML
前端采用拼接 HTML 的方法比较危险,如果框架允许,使用createElement
、setAttribute
之类的方法实现。或者采用比较成熟的渲染框架,如 Vue/React 等。 - 时刻保持警惕
在插入位置为 DOM 属性、链接等位置时,要打起精神,严加防范。 - 增加攻击难度,降低攻击后果
通过 CSP、输入长度配置、接口安全措施等方法,增加攻击的难度,降低攻击的后果。 - 主动检测和发现
可使用 XSS 攻击字符串和自动扫描工具寻找潜在的 XSS 漏洞。
参考网站:
感觉大佬的无私分享
Java防止XSS攻击获取Cookie: https://www.cnblogs.com/mao2080/p/9520185.html
网络攻击-XSS攻击详解:https://www.cnblogs.com/mao2080/p/9460397.html
美团前端安全系列(一):https://www.cnblogs.com/meituantech/p/9718677.html