Làm việc với dangerous data trong AngularJs
Bài đăng này đã không được cập nhật trong 3 năm
1, Giới thiệu
Ngày nay, với sự phát triển không ngừng của Internet, các web applications ra đời ngày càng nhiều, cung với đó là sự ra đời của nhiều cách thức tấn công website khác nhau. Việc đảm bảo tính an toàn của website trong quá trình xây dựng ngày càng trở nên quan trọng. Hiện nay, nhiều web applications vẫn chưa thực chú trọng tới vấn đề này, minh chứng là chúng có cách hiển thị dữ liệu rất thủ công, ngay cả khi dữ liệu đó chứa những đoạn code Javascript, những đoạn Css, hay những nội dung nguy hiểm mà kẻ tấn công inject vào form để có thể lấy dữ liệu quan trọng của hệ thống, hoặc hiển thị một nội dung nào đó tới người dùng. Angular có cung cấp một số modules và services để làm việc với dangerous data, từ đó có thể làm giảm một phần nào đó sự nguy hiểm của những nội dung được đưa vào web applications, đó là: $sce, $sanitize services.
2, Sử dụng các services để làm việc với dangerous datas
2.1, Cách AngularJs hiển thị dangerous datas
Bình thường thì AngularJs ngăn chặn các dữ liệu không an toàn thông qua data binding. Tôi sẽ tạo một application nhỏ để demo cho việc angular dùng databinding để escape dữ liệu không an toàn.
a, view: htmlData.html
<div class="row" ng-app="demoApp" ng-controller="exampleController">
<div class="col-md-8 col-md-offset-2">
<div class="well">
<p><input type="text" class="form-control" ng-model="htmlData"></p>
<p ng-bind="htmlData"></p>
</div>
</div>
</div>
b, Controller: exampleController.js
'use strict';
angular.module('demoApp').controller('exampleController', exampleController);
exampleController.$inject = ['$scope'];
function exampleController($scope) {
$scope.htmlData = "<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>";
}
c, Kết quả
Trong ví dụ này tôi đã tạo ra một form input, sau khi người dùng nhập nội dung vào thì sẽ hiển thị nội dung đó ngay bên dưới. Tôi cũng tạo cho scope một property được gọi là htmlData nó chính là scope để quản lý data cho input. scope property này đang chứa một nội dung nguy hiểm đó là một đoạn Javascript mà sẽ hiển thị một alert với dòng text: dangerous. Điều này có nghĩa là khi vừa vào trang thì sẽ có một alert hiện lên, nhưng thực tế không phải như vậy, AngularJs đủ thông minh để phát hiện nội dung đó nguy hiểm và escape nó trước khi hiển thị ra ngoài. Nhìn vào kết quả, chúng ta thấy thay vì hiển thị một alert, app sẽ chỉ hiển thị một dòng text:
<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>
Điều này được giải thích bằng việc thực tế rằng AngularJs đã chuyển đổi dữ liệu nguy hiểm đã cung cấp về dạng sau:
<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>
Đoạn text này là thực sự an toàn đối với trình duyệt khi hiển thị.
2.2, $sanitize service
2.2.1, Dùng ng-bind-html
Để hiển thị dữ liệu mà không bị escape, có một cách đơn giản là chúng ta dùng ng-bind-html directive. Ng-bind-html directive phụ thuộc vào ngSanitize module, module này không phải buil-in của AngularJs vì vậy chúng ta cần download và import nó vào app. Sau khi import ngSanitize module vào app, chúng ta có thể sử dụng ng-bind-html như dùng với ng-bind như sau:
<p ng-bind-html="htmlData"></p>
Kết quả:
Chúng ta thấy dữ liệu hiển thị đã nhận html mà nội dung cung cấp chứa chỉ có đoạn mã Javascript là không có hiệu lực. Đó là bởi vì $sanitize service đã loại bỏ các element và attribute nguy hiểm từ nội dung html. Nội dung html đã cung cấp được chuyển đổi về dạng:
<p>This is <b>dangerous</b> data</p>
2.2.2, Dùng $sanitize service để thay đổi data
Trong phần trên, tôi đã trình bày việc dùng $sanitize để hiển thị dữ liệu một cách an toàn, $sanitize service cũng có khả năng làm sạch các nội dung được lưu trong app. Trong ví dụ sau, tôi sẽ dùng $santize service để làm sạch nội dung trước khi hiển thị nó.
a, View: htmlData.html
<div class="row" ng-app="demoApp" ng-controller="exampleController">
<div class="col-md-8 col-md-offset-2">
<div class="well">
<p><input type="text" class="form-control" ng-model="dangerousData"></p>
<p ng-bind="htmlData"></p>
</div>
</div>
</div>
b, Controller: exampleController.js
'use strict';
angular.module('demoApp').controller('exampleController', exampleController);
exampleController.$inject = ['$scope', '$sanitize'];
function exampleController($scope, $sanitize) {
$scope.dangerousData = "<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>";
$scope.$watch('dangerousData', function(newValue) {
$scope.htmlData = $sanitize(newValue);
})
}
Như bạn thấy, trong controller tôi đã sử dụng $sanitize service để lọc dangerous data và sau đó dùng $watch để khởi tạo data sẽ được hiển thị. Đồng thời, trong file view tôi đã dùng ng-bind thay vì dùng ng-bind-html.
Kết quả:
Như bạn thấy, $sanitize service đã loại bỏ các nội dung Javascript ra khỏi nội dung được nhập từ input element.
2.3, $sce service
Khi bạn chắc chắn rằng nội dung được cung cấp là an toàn và muốn hiển thị toàn bộ nội dung đó, có nghĩa là việc escape và sanitize dangerous data được bỏ qua, thì $sce service sẽ là một lựa chọn hợp lý cho bạn. $sce service định nghĩa trustHtml method, method này sẽ trả về nội dung được hiển thị một cách đầy đủ nhất.
Trong ví dụ dưới đây, tôi sẽ sử dụng $sce để có thể thực hiện đoạn code Javascript trong nội dung được cung cấp. Nghĩa là khi vào trang thì sẽ có một alert hiện lên với nội dung là dòng 'Attack!'
a, view: html_data.html
<div class="row" ng-app="demoApp" ng-controller="exampleController">
<div class="col-md-8 col-md-offset-2">
<div class="well">
<p><input type="text" class="form-control" ng-model="htmlData"></p>
<p ng-bind-html="trustedData"></p>
</div>
</div>
</div>
b, controler: exampleController.js
'use strict';
angular.module('demoApp').controller('exampleController', exampleController);
exampleController.$inject = ['$scope', '$sce'];
function exampleController($scope, $sce) {
$scope.htmlData = "<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>";
$scope.$watch('htmlData', function(newValue) {
$scope.trustedData = $sce.trustAsHtml(newValue);
})
}
Kết quả:
Như bạn thấy, alert đã hiện lên khi chúng ta vừa vào trang, tức là Javascript code trong nội dung được cung cấp đã được thực thi.
3, Kết luận
Cũng giống như trong Ruby On Rails và nhiều web framework khác dữ liệu được tự động escape trước khi được hiển thị. Nhưng trong một số trường hợp nào đó, chúng ta cần hiển thị các nội dung html, css, javascript thì AngularJs cũng đã cung cấp đầy đủ công cụ, service để chúng ta có thể thực hiện điều này. Có một điều cần lưu ý rằng, việc cho phép hiển thị, thực thi các mã html, css, javascript sẽ tiềm ẩn rất nhiều nguy hiểm vì có thể đó vô tình tạo môi trường cho các hackers tấn công app của chúng ta. Vì vậy cần xem xét kỹ lưỡng nội dung hiển thị được cung cấp xem có thực sự an toàn trước khi quyết định bỏ qua các bước escape và santize để hiển thị chúng.
Tài liệu tham khảo
http://www.tutorialspark.com/AngularJS/AngularJS_Working_with_Dangerous_Data.php https://docs.angularjs.org/api/ngSanitize https://docs.angularjs.org/api/ng/service/$sce http://www.tothenew.com/blog/sce-in-angular/ https://benohead.com/angularjs-binding-html-code-with-ng-bind-html-and-sce-trustashtml/
All rights reserved