Статьи

AngularJS: как работать со сценариями уязвимости XSS


В этой статье представлены различные сценарии, связанные с XSS (межсайтовый скриптинг) и способы их правильной обработки с использованием таких функций AngularJS, как
SCE ($ sceProvider) и
службы sanitize ($ SanitizeProvider) . Пожалуйста, не стесняйтесь комментировать / предлагать, если я пропустил упоминание одного или нескольких важных моментов. Кроме того, извините за опечатки.

Ниже приведены ключевые сценарии, связанные с XSS, описанные далее в этой статье:

  • Полностью избежать HTML
  • Вставьте HTML безопасным способом, игнорируя такие элементы, как «скрипт». Это также опасно и может повредить ваш сайт, если не позаботиться, особенно с тегом «img».
  • Доверяй и вставляй весь HTML; Это опасно и может привести к порче вашего сайта.

Escape 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.

ng_bind_taj_mahal

Вставьте HTML безопасным способом, игнорируя такие элементы, как «скрипт», используя директиву ng-bind-html

Это ключ к решению атак XSS. Тем не менее, все равно следует позаботиться о таких элементах, как «img»  (включенных как часть белого списка; пустые элементы), поскольку он может отображать любое изображение (включая недопустимые) на вашей веб-странице, таким образом,  портя вашу веб-страницу., Используя директиву ng-bind-html, тег сценария javascript, такой как «script», можно сразу игнорировать. Директива 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
  • Элемент блока : адрес, статья, в стороне, цитата, заголовок, центр, del, dir, div, dl, рисунок, figcaption, нижний колонтитул, h1, h2, h3, h4, h5, h6, заголовок, hgroup, hr, ins, map , меню навигации, ол, предварительно, сценарий, раздел, таблица, ули
  • Встроенные элементы : a, abbr, аббревиатура, b, bdi, bdo, big, br, cite, код, del, dfn, em, шрифт, i, img, ins, kbd, метка, карта, метка, q, ruby, rp , к.т., с, SAMP, маленький, пролет, удар, сильный, к югу, SUP, время, тт, и, вар
  • Элементы конечных тегов : colgroup, dd, dt, li, p, tbody, td, tfoot, th, thead, tr, rp, rt. Подробности этого можно найти по адресу http://dev.w3.org/html5/spec/Overview.html#optional-tags.

Ниже приведены два элемента, которые экранированы, поскольку находятся в ненадежной категории . В случае, если вы хотите показать это, вам нужно будет использовать сервис $ sce и вызвать метод trustAsHtml для Angular для выполнения перечисленных ниже элементов.

  • скрипт
  • стиль

Ниже приведен пример кода, демонстрирующий использование директивы 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-кода в текстовое поле, которое безопасно вставляется в DOM. Обратите внимание на элемент «img», который является частью элементов Void в приведенном выше списке. Когда код вводится в текстовое поле, изображение отображается как «img» в списке доверенных (белый список)

ng_bind_html_taj_mahal

Доверяй и вставляй весь HTML

Предупреждение: это опасно и может привести к порче вашего сайта . Только когда вы знаете и вдвойне уверены, вы должны использовать trustAsHtml. В случае, если вы уверены, что текстовое содержимое можно доверять, вы можете использовать службу $ sce и вызвать метод trustAsHtml, который затем вставляет весь HTML в DOM. Обратите внимание на фрагмент кода HTML и javascript, где используется служба $ sce для вызова метода trustAsHtml для доверия к HTML-коду. В этом случае вставляется один код, такой как «<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>

На следующем рисунке показано, как это выглядит при вводе кода стиля HTML в текстовое поле, которое вставляется в DOM . В результате другой элемент HTML окрашивается в красный цвет, как показано ниже. В сценариях, где хакер может вставить элемент стиля с фоном, это может показать нежелательный фон и создать плохой опыт для конечных пользователей.

ng_bind_html_trust_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>