이 기사에서는 XSS(교차 사이트 스크립팅)를 위한 다양한 솔루션과 SCE($sceProvider)를 사용하고 AngularJS의 서비스 기능을 삭제하여 XSS를 올바르게 처리하는 방법을 보여줍니다. 중요한 내용을 놓친 경우 직접 의견을 말하거나 제안해 주세요. 또한, 오타가 있는 점 양해 부탁드립니다.
다음 내용은 제가 다음에 이야기할 내용의 초점이 될 것입니다.
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 지시문을 사용하여 "스크립트"와 같은 요소를 무시하세요.
이것이 XSS 공격을 해결하는 열쇠입니다. 하지만 "img"(화이트리스트의 일부로 포함됨, 빈 요소 포함)와 같은 요소는 웹 페이지에서 사용될 수 있으므로 여전히 주의를 기울여야 합니다. 불법적인 이미지를 포함하여 모든 이미지를 표시하면 웹 페이지에 부정적인 영향을 미칠 수도 있습니다. ng-bind-html 지시문을 사용하면 "script"와 같은 AngularJS JavaScript 태그를 직접 무시할 수 있습니다. bind-html 지시어는 표현식을 평가하고 결과 HTML을 안전한 방식으로 요소에 삽입합니다. 사용자가 HTML 콘텐츠(예: 주석)가 포함된 입력을 입력하는 경우 이를 ng-bind-html 지시어에 넣습니다. 이렇게 하면 텍스트가 안전한 HTML 문자의 허용 목록으로 인코딩됩니다. 안전한 문자의 허용 목록은 아래 설명된 $sanitize의 일부로 인코딩됩니다(소스 코드에서 직접 가져옴).
빈 요소:
블록 요소:
인라인 요소:
종료 태그 요소:
다음 두 요소는 해당 내용을 신뢰할 수 없으므로 우회해야 합니다. 이 경우 해당 요소를 표시하려면 $sce 서비스를 사용하고 Angular의 trustAsHtml 메소드를 호출하여 아래에 언급된 요소를 실행해야 합니다.
아래에 제시된 코드는 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代码块. 在这种情况下,一段诸如 “” 这样的代码被插入了,它最后可能会也给现有的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>