做專案久了,一定會有需求要顯示從外部來的 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 額外用法:
參考: how do I get hg-include directive to work with a Content Delivery Network?