AngularJS – 如何處理 XSS 漏洞

edithfang發表於2014-12-03
以下幾點內容將是我接下來要講述的重點:

  • 全部轉碼HTML
  • 安全插入HTML的同時忽略類似“script"這樣的標籤。如果不加以注意,這將一樣存在風險同時也會醜化頁面,尤其是在有”img“標籤的時候。
  • 依賴並插入純HTML這也有風險的同時會讓網頁很難看。使用ng-bind指令轉碼HTML

你可以用ng-bind指令來轉碼整個網頁。它將會轉碼所有HTML標籤但是仍然顯示本來的樣子。下列程式碼顯示了ng-bind的用法。

<div>
<form>
    <h1>AngularJS XSS Demo Test</h1>
    <hr/>
    <div class="col-md-12">
    <input type="text" ng-model="name" class="form-control col-md-12" ng-change="processHtmlCode()" placeholder="Enter Some HTML Text..."/>
    </div>
</form>
</div>
<hr/>
<div style="padding:20px">
    <span><strong>ng-bind directive: Note that HTML text is entered as it is.</strong></span><br/>
    <span ng-bind="helloMessage">{{helloMessage}}</span>
</div>


下面的圖證明了以上言論。注意在輸入欄中的HTML程式碼。它和在HTML頁面中完全一樣。


使用安全的方式插入HTML,也可以使用 ng-bind-html 指令忽略掉諸如“script”這樣的元素


這是解決XSS攻擊的關鍵. 也就是說,你仍然應該關注諸如“img"這樣的元素 ( 作為一部分包含進了白名單中; 還有空元素) 因為它恩能夠在你的web頁面上展示任何圖片 (包括非法的那些), 因此,它也可能會給你的web頁面帶來不利影響. 使用 ng-bind-html 指令皆可以AngularJS諸如“script”這樣的JavaScript標記直接被忽略掉. ng-bind-html 指令會計算表示式,並且用一種安全的方式將結果HTML插入元素中. 對於使用者會輸入包含了HTML內容(比如在評論中)的情況,放到 ng-bind-html指令中可以確保文字被編碼為白名單中的安全HTML字元. 安全字元的白名單被作為 $sanitize 的一部分編碼,下面會講到. 下面這些都被包含進了安全列表中 (直接從原始碼中獲得):


  • 空元素: area,br,col,hr,img,wbr. 詳細資訊請訪問 http://dev.w3.org/html5/spec/Overview.html#void-elements
  • 塊元素: address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul
  • 內聯元素: a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var
  • 結尾標記元素: colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr,rp,rt. 詳細資訊請訪問 http://dev.w3.org/html5/spec/Overview.html#optional-tags

下面的這兩個元素 因為其內容不收信任,需要被規避掉. 在這種情況下,如果你想要展示它們,就要使用 $sce 服務,呼叫Angular 的 trustAsHtml 方法來執行下面提到的元素.

  • script
  • style


如下呈現的程式碼展示了 ng-bind-html 指令的使用.

<div>
<form>
    <h1>AngularJS XSS Demo Test</h1>
    <hr/>
    <div class="col-md-12">
    <input type="text" ng-model="name" class="form-control col-md-12" ng-change="processHtmlCode()" placeholder="Enter Some HTML Text..."/>
    </div>
</form>
</div>
<hr/>
<div style="padding:20px">
    <span>ng-bind-html directive: Note that image is displayed appropriately as a result of text entered in the text field.</span>
    <span ng-bind-html="helloMessage"></span>
</div>


下面這張圖片展示了當在文字域中輸入HTML程式碼,Angular用一種安全的方式插入到DOM時,是什麼樣子的. 注意 “img” 元素是上述列表中空元素的一份子. 因為程式碼被輸入到了文字域中,作為”img"出現的圖片被放到了受信任的列表(白名單)中。



信任並插入整段HTML

警告: 這很危險,並且可能很容易就最終造成你web站點的汙染. 只有當你知道並且充分確認時,你才應該使用 trustAsHtml. 如此,你就有充足的信心認為這段文字是可以被信任的, 你應該使用$sce 服務並且呼叫 trustAsHtml 方法來講整段HTML插入DOM中。在$sce服務被用來呼叫 trustAsHtml 方法來信任一段HTML程式碼時,請留意HTML和其中的JavaScript程式碼塊. 在這種情況下,一段諸如 “<style>.hello{color:red}</style>” 這樣的程式碼被插入了,它最後可能會也給現有的HTML元素加上樣式。這可能不是很好。人們也可能採用那種方式用非法的圖片替換背景圖片.

<script type="text/javascript">
    angular.module('HelloApp', ["ngSanitize"])
    .controller('HelloCtrl', ['$scope', '$sce', function($scope, $sce){
        $scope.name="";
        $scope.processHtmlCode    =    function() {
            $scope.helloMessage = "<h1>" + $scope.name + "</h1>";    
            $scope.trustedMessage =  $sce.trustAsHtml( $scope.name );
        }
    }])
  
    </script>
<!-- Pay attention to class hello which is coded in UI and as a result, element is painted in red-->
<div style="padding:20px">
        <span class="hello"><strong>ng-bind directive: Note that HTML text is entered as it is.</strong></span><br/>
        <span class="hello" ng-bind="helloMessage">{{helloMessage}}</span>
</div>
<hr/>
<div style="padding:20px">
        <span>Note that script tag is executed as well.</span>
        <span ng-bind-html="trustedMessage"></span>
</div>


下面的圖片展示了當在文字域中輸入將要被插入DOM中的HTML樣式程式碼時,會是什麼樣子. 這裡的結果就是, 其它的HTML元素也帶上了紅色, 如下所示. 在某些場景中,黑客可能會插入一段帶有背景樣式越蘇,這可能會顯示出原本不要被顯示的背景,給終端使用者帶來糟糕的體驗.


全部程式碼 – 剪下/複製並貼上,還有展示

<html>
<head>
    <title>Hello AngularJS</title>
    <link rel="stylesheet" type="text/css" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular-sanitize.min.js"></script>
</head>
<body class="container" ng-app="HelloApp" ng-controller="HelloCtrl">
    <div>
        <form>
            <h1>AngularJS XSS Demo Test</h1>
            <hr/>
            <div class="col-md-12">
                <input type="text" ng-model="name" class="form-control col-md-12" ng-change="processHtmlCode()" placeholder="Enter Some HTML Text..."/>
            </div>
        </form>
        <hr/>
    </div>
    <hr/>
    <div style="padding:20px">
        <span class="hello"><strong>ng-bind directive: Note that HTML text is entered as it is.</strong></span><br/>
        <span class="hello" ng-bind="helloMessage">{{helloMessage}}</span>
    </div>
    <hr/>
    <div style="padding:20px">
        <span>Note that script tag is executed as well.</span>
        <span ng-bind-html="trustedMessage"></span>
    </div>
    <hr/>
    <div style="padding:20px">
        <span>ng-bind-html directive: Note that image is displayed appropriately as a result of text entered in the text field.</span>
        <span ng-bind-html="helloMessage"></span>
    </div>
    <hr/>
    <script type="text/javascript">
    angular.module('HelloApp', ["ngSanitize"])
    .controller('HelloCtrl', ['$scope', '$sce', function($scope, $sce){
        $scope.name="";
        $scope.processHtmlCode    =    function() {
            $scope.helloMessage = "<h1>" + $scope.name + "</h1>";    
            $scope.trustedMessage =  $sce.trustAsHtml( $scope.name );
        }
    }])
  
    </script>
</body>
</html>
相關閱讀
評論(1)

相關文章