iT邦幫忙

DAY 22
1

我在前端 ng 時系列 第 22

外部來的 HTML 安全嗎? $sce & ng-sanitize

做專案久了,一定會有需求要顯示從外部來的 HTML 格式。
例如:顯示會員 HTML 格式的留言。

我們不能擔保使用者輸入的資料是絕對安全的。除了靠後端過濾外,前端一定也需要幫忙擋一擋。

今天介紹的 $sce service 就幫我們做到這點了。
(使用 ng-sanitize,AngularJS 版本需在 1.2 以上)


Strict Contextual Escaping ($sce) 中文翻譯就是 “嚴謹的語境跳脫”,我就白話翻譯成 “嚴謹安全的綁定語法”。

先來一段 quotes from AngularJS $sce documentation:

​SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier.

簡易翻譯:SCE 確保網站是安全的,並且協助我們更方便檢查 XSS, clickjacking 等 security vulnerabilities。

準備:$sce 通常搭配 ng-sanitize 使用,需引入 angular-sanitize.js。

  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.20/angular.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-sanitize.js"></script>

使用範例:

1. 在畫面上顯示使用者 HTML 格式的留言

後端傳來的使用者留言 String 是:

<div>我覺得海賊王很好看。</div>

先我們直接用 two-way binding 綁定。範例

  <div>小華 留言:</div>
  <div>{{ myCtrl.comment }}</div>

畫面上會顯示的結果不是我們要的,因為 AngularJS 會認定 input 只是 String,而不是 HTML 格式。

這是可改用 ng-bind-html 來綁定留言 String ,知道該 String 是 HTML 格式 範例

  <div>小華 留言:</div>
​  <div ng-bind-html="myCtrl.comment"></div>

2. 幫忙檢查並過濾將要存到後端的 HTML 格式資料

畫面上需求是使用者可以自己寫 HTML 格式,範例
雖然 ng-bind-html 可以幫忙過濾畫面上的 HTML 格式,可是真實的 model 還是不安全的資料。

假如想要傳到後端的資料也是安全的,可在 controller 用 $sanitize 過濾資料解決問題:

angular.module('myExample', ['ngSanitize'])
  .controller('myCtrl', function ($scope, $sce, $sanitize) {
      var ctrl = this;
      // comment 會轉變成安全的 string
      ctrl.comment = $sanitize('<div onmouseover="alert(\'我不安全~\')">我就是要打不安全的HTML</div>');
  })

範例:http://plnkr.co/edit/rjqB7VWJ86fryUCfJsSs?p=preview


ng-bing-html 的 attribute 白名單
名單:https://github.com/angular/bower-angular-sanitize/blob/master/angular-sanitize.js#L213

在白名單中發現 id 、style 這些常用的 attribute 是不在白名單內的。
所以 ng-bind-html 遇到這兩個 arrtibute 會在 string 中移除,畫面顯示就不會出現。
目前 AngularJS 也沒有提供方法讓我們自己加白名單...
所以假如要過濾完的 String 保留 id 或 style 目前最快的解法就是使用 $sce.trustAsHtml()。(ng-bind-html-unsafe,等等會介紹)
(有人已經開 issue 請 AngularJS github 讓我們可以新增白名單。)


實作 ng-bind-html-unsafe:

在 AngularJS 1.2.0 沒有 ng-bind-html-unsafe directive。
假如要在 angular $sanitize 在不被檢查的情況下綁定 HTML,可以先把 template 用 $sce.trustAsHtml() 包起來。

myCtrl.myTemplate = '<div onmouseover="alert(\'我不安全!\')">我不安全</div>';
myCtrl.myUnsaveTemplat = $sce.trustAsHtml(myCtrl.myTemplate);

在 HTML 就會產生沒有被安全檢查過的 template:

<div ng-bind-html="myCtrl.myUnsaveTemplat" class="ng-binding”>
   <div onmouseover="alert('我不安全!')">我不安全</div>
</div>

範例:http://plnkr.co/edit/cOOxbt34RDbpEAuNzkkh?p=preview

上面方法可把 trustAsHtml 寫成 filter:StackOverFlow 範例


$sce 額外用法:

  • $sce service 還可設定撈取不同 domain 下的 template。(使用 $sceDelegateProvider)

參考: how do I get hg-include directive to work with a Content Delivery Network?


上一篇
SEO for AngularJS Website
下一篇
AngularJS 結構 - 心路歷程
系列文
我在前端 ng 時30

尚未有邦友留言

立即登入留言