Có an toàn khi sử dụng InternalHTML trong Angular không?

Lỗ hổng Cross-Site Scripting (XSS) có thể và sẽ dẫn đến sự thỏa hiệp hoàn toàn của ứng dụng giao diện người dùng. Lỗ hổng XSS cho phép kẻ tấn công kiểm soát ứng dụng trong trình duyệt của người dùng, trích xuất thông tin nhạy cảm và thực hiện các yêu cầu thay mặt cho ứng dụng. Các framework hiện đại đi kèm với hệ thống phòng thủ tích hợp chống lại XSS, nhưng chúng thực sự đi được bao xa?

1 tháng 2 năm 2021 Bảo mật SPA Các ứng dụng XSS, góc cạnh, một trang

Một đoạn mồi ngắn về XSS

Lỗ hổng XSS là lỗ hổng tiêm chích, trong đó kẻ tấn công chèn một phần dữ liệu độc hại vào ứng dụng web. Dữ liệu được tiêm độc hại sẽ được trình duyệt chọn, diễn giải dữ liệu dưới dạng mã. Điều này làm cho tải trọng được đưa vào từ kẻ tấn công được thực thi dưới dạng mã ứng dụng hợp pháp, giúp kẻ tấn công có toàn quyền kiểm soát ứng dụng đang chạy trong trình duyệt của người dùng

Ví dụ mã bên dưới minh họa một ví dụ trong sách giáo khoa về lỗ hổng XSS và cuộc tấn công

Lỗ hổng DOM-XSS trong ứng dụng jQuery


Một thư độc hại chứa tải trọng JavaScript


Hello John!

Nếu dữ liệu này được chèn vào trang, trình duyệt sẽ thực thi tập lệnh trong thông báo, kích hoạt hộp thoại cảnh báo. Ví dụ ở đây gọi hàm


Hello John!
3, đây có lẽ là tác động ít nguy hiểm nhất của một cuộc tấn công XSS. Không tạo ra một sai sót. Trên thực tế, kẻ tấn công có thể đánh cắp dữ liệu nhạy cảm từ các trang, thu thập dữ liệu nhập của người dùng để đánh cắp mật khẩu hoặc thông tin thẻ tín dụng và thậm chí gửi yêu cầu đến máy chủ như thể chính ứng dụng hợp pháp gửi chúng. Sau khi kẻ tấn công thành công trong việc khai thác lỗ hổng XSS, bạn nên coi toàn bộ môi trường thực thi của ứng dụng trong trình duyệt đã bị xâm phạm

Cách phòng thủ tốt nhất trước các cuộc tấn công XSS là đảm bảo rằng trình duyệt sẽ không bao giờ xem dữ liệu dưới dạng mã. Một cách tiếp cận phổ biến để đạt được điều đó là áp dụng mã hóa đầu ra theo ngữ cảnh. Mã hóa đầu ra theo ngữ cảnh sẽ đảm bảo rằng dữ liệu được mã hóa cho ngữ cảnh mà nó kết thúc trên trang. Trong quá trình mã hóa, các ký tự nguy hiểm tiềm tàng được dịch thành các ký tự vô hại. Do cách dịch đó, trình duyệt sẽ xem các ký tự này là dữ liệu thay vì mã, tránh nhầm lẫn và ngăn chặn cuộc tấn công

Dưới đây là một ví dụ về tác động của việc áp dụng mã hóa đầu ra theo ngữ cảnh đối với dữ liệu chứa tải trọng độc hại

Một thông báo độc hại chứa tải trọng JavaScript, nhưng được mã hóa đúng cách để tránh XSS


Hello John!<script>alert("Let's play hide and seek!")</script>

Như bạn có thể thấy, trình duyệt hiện được hướng dẫn hiển thị mã HTML


Hello John!
4 và

Hello John!
5, hiển thị các ký tự

Hello John!
6 và

Hello John!
7. Vì đây là dữ liệu rõ ràng nên trình duyệt sẽ không nhầm lẫn giữa dữ liệu với mã

Góc áp dụng tự động thoát

Khi một ứng dụng đưa dữ liệu vào trang bằng cơ chế nội suy của Angular (i. e. , sử dụng


Hello John!
8), Angular nhận thức được mối nguy hiểm tiềm ẩn của XSS. Do đó, Angular sẽ tự động đảm bảo rằng dữ liệu kết thúc trên trang sẽ không gây ra các cuộc tấn công XSS. Đoạn mã dưới đây cho thấy một ràng buộc dữ liệu như vậy

Góc tự động thoát khỏi tất cả các ràng buộc dữ liệu dựa trên phép nội suy


{{review}}

Cơ chế phòng thủ thoát tự động này, mà Angular gọi là Thoát theo ngữ cảnh nghiêm ngặt, rất quan trọng để đảm bảo bảo vệ cơ bản chống lại các cuộc tấn công XSS. Khi các khung phía máy chủ và khung phía máy khách không cung cấp mức bảo vệ tối thiểu này, các ứng dụng sử dụng các khung đó thường gặp phải rất nhiều lỗ hổng XSS

Góc áp dụng hành vi nhạy cảm theo ngữ cảnh này trên tất cả các ràng buộc dữ liệu dựa trên phép nội suy. Ví dụ: liên kết dữ liệu vào thuộc tính HTML sẽ đảm bảo rằng dữ liệu không thể thoát khỏi thuộc tính và kích hoạt việc thực thi mã tập lệnh bổ sung

Khi trốn thoát hơi xa

Các ứng dụng trong thế giới thực thường gặp phải các yêu cầu khi chúng cần hiển thị mã HTML động. Mã HTML đó thường bắt nguồn từ các nguồn không đáng tin cậy, chẳng hạn như dữ liệu do người dùng cung cấp. Hình ảnh dưới đây minh họa một tình huống phổ biến của trường hợp sử dụng này

Hình ảnh từ trang web CKEditor minh họa các tính năng của trình soạn thảo
Có an toàn khi sử dụng InternalHTML trong Angular không?

Trình soạn thảo văn bản đa dạng thức, chẳng hạn như CKEditor, thường tạo HTML dưới dạng đầu ra. Tài liệu mà bạn có thể xem bên trên hiển thị hình ảnh có thành phần


Hello John!
9, tiêu đề có thẻ

Hello John!<script>alert("Let's play hide and seek!")</script>
0 và các đoạn văn bản có thẻ

Hello John!<script>alert("Let's play hide and seek!")</script>
1. Để hiển thị đầu ra của trình chỉnh sửa một cách chính xác, trình duyệt cần có khả năng phân tích cú pháp và hiển thị mã HTML

Trong các khóa đào tạo của mình, tôi sử dụng một ứng dụng đào tạo thu thập các đánh giá về nhà hàng. Ứng dụng này muốn người dùng có thể sử dụng các cấu trúc HTML lành tính trong các bài đánh giá của họ. Mặc dù các bài đánh giá ít phức tạp hơn so với tài liệu của CKEditor, nhưng các yêu cầu kỹ thuật là như nhau

Đối với những trường hợp sử dụng này, Angular hiển thị thuộc tính


Hello John!<script>alert("Let's play hide and seek!")</script>
2. Lưu ý các dấu ngoặc vuông ở đây, cho biết đây là thuộc tính Góc, không phải thuộc tính

Hello John!<script>alert("Let's play hide and seek!")</script>
3 của DOM API gốc. Bằng cách ràng buộc các giá trị với

Hello John!<script>alert("Let's play hide and seek!")</script>
2, chúng tôi hướng dẫn Angular tự động làm sạch dữ liệu trước khi đưa nó vào trang. Bạn có thể xem tải trọng mẫu và dữ liệu được hiển thị bên dưới

Dữ liệu do người dùng cung cấp là một vectơ tấn công phổ biến để khai thác lỗ hổng XSS


Hello John!
6

Nếu chúng tôi chạy bài đánh giá này thông qua trình khử trùng Angular, nó sẽ xuất hiện dưới dạng bài đánh giá mà bạn có thể xem bên dưới

Bài đánh giá do người dùng cung cấp sau khi đã được khử trùng bằng trình khử trùng HTML


Hello John!
7

Cơ chế hoạt động ở đây là khử trùng HTML bằng cách sử dụng trình khử trùng HTML tích hợp của Angular. Trình khử trùng HTML biết phần tử và thuộc tính nào an toàn (e. g. ,


Hello John!<script>alert("Let's play hide and seek!")</script>
5 và

Hello John!<script>alert("Let's play hide and seek!")</script>
6) và có khả năng không an toàn (e. g. ,

Hello John!<script>alert("Let's play hide and seek!")</script>
7 và thuộc tính

Hello John!<script>alert("Let's play hide and seek!")</script>
8 của phần tử

Hello John!<script>alert("Let's play hide and seek!")</script>
9). Trình khử trùng sẽ để lại tất cả các phần HTML an toàn trong dữ liệu nhưng loại bỏ tất cả các phần nguy hiểm

Lưu ý rằng vệ sinh nghe giống như lọc (a. k. a, những thứ bạn làm với biểu thức chính quy), nhưng đó là một cách tiếp cận hoàn toàn khác. Trình khử trùng sẽ phân tích dữ liệu thành cây cú pháp và đưa ra quyết định bảo mật dựa trên cây đó. Một chất khử trùng tốt sử dụng danh sách các giá trị an toàn và coi mọi thứ không có trong danh sách là không an toàn

Vì vậy, chúng ta không thể làm mọi thứ rối tung lên trong Angular?

Angular thực hiện công việc xuất sắc trong việc giúp các nhà phát triển viết mã an toàn. Bất kỳ ai xây dựng ứng dụng Angular theo “cách thông thường” đều có thể yên tâm rằng chúng sẽ không gây ra lỗ hổng XSS. Tìm cách vượt qua sự phòng thủ của Angular tốn khá nhiều công sức. Hãy xem xét một số mẫu bạn có thể muốn tránh trong các ứng dụng của mình

Bỏ qua bảo mật

Angular cung cấp một cách để xuất HTML thô mà không áp dụng bất kỳ biện pháp bảo vệ XSS nào. Chức năng này có sẵn thông qua chức năng


{{review}}

0. Đúng như tên gọi của hàm, cơ chế này bỏ qua hoàn toàn các cơ chế bảo mật của Angular. Sử dụng nó trên dữ liệu không đáng tin cậy sẽ dẫn đến lỗ hổng XSS

Ví dụ mã bên dưới cho biết cách sử dụng hàm


{{review}}

0

Đánh dấu đoạn mã tĩnh là an toàn bằng hàm bypassSecurityTrustHtml


Hello John!
5

Việc gán một đoạn mã an toàn cho [innerHTML] không kích hoạt trình khử trùng của Angular


Hello John!
6

Hành vi này cực kỳ nguy hiểm khi bị lạm dụng, như tên của chức năng gợi ý. Trường hợp sử dụng duy nhất có thể chấp nhận được là xuất một đoạn mã tĩnh, mã do chính bạn viết. Không bao giờ sử dụng chức năng này với bất kỳ giá trị không đáng tin cậy nào

Ngoài ra, nếu bạn cần sử dụng nó để xuất mã tĩnh, hãy xem xét đóng gói hàm này trong một hàm trợ giúp có tên apt (e. g. ,


{{review}}

2). Tên như vậy truyền đạt rõ ràng mục đích của chức năng này để tránh bất kỳ vấn đề nào về việc sử dụng sai hoặc tái cấu trúc sau này. Ngoài ra, mẫu mã hóa này cho phép bạn cấm sử dụng

{{review}}

0 trong cơ sở mã của mình, ngoại trừ chức năng trợ giúp duy nhất đó

Sử dụng các phần tử DOM gốc

Góc hoạt động ở cấp độ các thành phần và không thực sự ở cấp độ các phần tử HTML riêng lẻ. Chắc chắn, các mẫu chứa các phần tử HTML, nhưng các ứng dụng Angular hiếm khi tham chiếu các phần tử này một cách rõ ràng từ mã. Hiếm khi là từ khóa ở đây. Hầu hết các ứng dụng không cần truy cập trực tiếp vào các phần tử HTML, nhưng một số trường hợp sử dụng cụ thể yêu cầu quyền truy cập cấp thấp như vậy

Đó chính xác là lý do tại sao Angular hỗ trợ sử dụng ElementRef. Ví dụ mã bên dưới cho biết cách tham chiếu một phần tử trong mã của thành phần bằng ElementRef. Phần tử này được định nghĩa một mẫu là


{{review}}

4

Sử dụng ElementRef để chỉ một phần tử HTML cụ thể


Hello John!<script>alert("Let's play hide and seek!")</script>
0

Với tham chiếu này, giờ đây chúng ta có thể truy cập phần tử DOM gốc. Quyền truy cập như vậy hữu ích cho việc xử lý sự kiện chi tiết nhưng cũng cho phép các mẫu mã không an toàn, chẳng hạn như ví dụ mã bên dưới

Hành vi nguy hiểm khi sử dụng các phần tử DOM gốc


Hello John!<script>alert("Let's play hide and seek!")</script>
1

Trong ví dụ mã này, chúng tôi đưa dữ liệu trực tiếp vào DOM. Vì chúng tôi đang xử lý các API DOM gốc, nên Angular không còn có thể bảo vệ chúng tôi khỏi các cuộc tấn công XSS tiềm ẩn. Mã này cực kỳ không an toàn và vượt qua các biện pháp phòng thủ XSS tích hợp của Angular

Không có lý do chính đáng tại sao một mẫu mã như thế này nên được sử dụng trong ứng dụng Angular. Tôi khuyên bạn nên quét cơ sở mã của mình để đảm bảo không có mẫu này và thiết lập quy tắc linting để đảm bảo mẫu này sẽ không bao giờ xuất hiện trong cơ sở mã

Sử dụng API Renderer2

Việc sử dụng


Hello John!<script>alert("Let's play hide and seek!")</script>
3 trên các phần tử DOM gốc không phải là cách duy nhất bạn có thể vượt qua các biện pháp phòng thủ an toàn theo mặc định của Angular. Với ElementRef, bạn cũng có thể sử dụng API Renderer2 để thao tác DOM. Cơ chế này có lẽ còn nguy hiểm hơn vì API Renderer2 là API góc hợp pháp. Thật không may, API này không áp dụng các biện pháp bảo vệ XSS tự động và nên tránh càng nhiều càng tốt

Ví dụ mã bên dưới cho thấy cách sử dụng API Renderer2 với ElementRef để đặt thuộc tính


Hello John!<script>alert("Let's play hide and seek!")</script>
3 của một phần tử, bỏ qua các biện pháp phòng thủ XSS của Angular

Hành vi nguy hiểm khi sử dụng API Renderer2


Hello John!
0

Cũng giống như việc sử dụng API DOM gốc, tôi khuyên bạn nên quét cơ sở mã của mình để đảm bảo mẫu này không xuất hiện và thiết lập quy tắc linting để đảm bảo mẫu này sẽ không bao giờ được đưa vào cơ sở mã

Hội thảo bảo mật kéo dài 2 ngày này tiến xa hơn rất nhiều so với các biện pháp phòng thủ XSS tích hợp của Angular

Tham gia hội thảo này để tìm hiểu thêm về các biện pháp bảo mật tốt nhất hiện tại cho các ứng dụng Angular

Thêm thông tin

Đợi một chút, còn URL thì sao?

Một nguồn lỗ hổng XSS phổ biến khác là các URL. Hãy xem URL được hiển thị bên dưới

Tất cả các trình duyệt hiện đại đều hỗ trợ URL JavaScript trong thuộc tính phần tử


Hello John!
1

URL này sử dụng


{{review}}

7 làm lược đồ, thay vì

{{review}}

8 hoặc

{{review}}

9. Khi trình duyệt nhìn thấy một URL như vậy trong tài liệu HTML, trình duyệt thường xem đó là mã JavaScript cần được thực thi. Bạn có thể thử xem điều gì sẽ xảy ra nếu một URL

{{review}}

7 kết thúc bằng

Hello John!<script>alert("Let's play hide and seek!")</script>
8 của phần tử

Hello John!
62 bằng cách nhấp vào đây. Khi người dùng có thể kiểm soát một URL như vậy, nó có thể được sử dụng để kích hoạt việc thực thi mã JavaScript độc hại

Ngày nay, có thể nói rằng


{{review}}

7 URL là một sai lầm đau đớn trong quá khứ. Thật không may, các ứng dụng đã dựa vào hành vi này cho các tính năng hợp pháp, gây khó khăn cho việc tắt tính năng này ở cấp trình duyệt. Tuy nhiên, ngày càng có nhiều khung ứng dụng hiện đại không khuyến khích hoặc ngăn cản việc sử dụng các URL

{{review}}

7. Angular thực hiện chính xác điều đó thông qua một trình khử trùng URL cụ thể

Trình khử trùng góc đảm bảo rằng các URL được tạo động an toàn để sử dụng trong ứng dụng. Nhìn vào mã cho thấy rằng trình khử trùng chỉ cho phép các URL an toàn đã biết và thêm tiền tố vào các URL khác bằng lược đồ


Hello John!
65. Cơ chế này ngăn chặn hiệu quả XSS thông qua URL

Thậm chí tốt hơn, Angular áp dụng cơ chế này ngay lập tức

Không nhanh lắm, các URL tài nguyên không hoạt động tốt

Nếu bạn đã từng thử tải một iframe bằng cách gán một biến cho thuộc tính


Hello John!
66 trong Angular, bạn có thể đã gặp lỗi như lỗi hiển thị bên dưới

Angular từ chối tải các URL tài nguyên được gán động để tránh tải nội dung không an toàn hoặc không mong muốn
Có an toàn khi sử dụng InternalHTML trong Angular không?

Đây là Angular bảo vệ ứng dụng khỏi nội dung có thể gây hại. Tốt, nhưng tại sao lại có thông báo lỗi?

URL tài nguyên về cơ bản là các URL kích hoạt ngay việc tải tài nguyên. Ví dụ là iframe, tập lệnh, biểu định kiểu, v.v. Bao gồm các URL


{{review}}

7 ở một vị trí như vậy có thể nguy hiểm, nhưng còn nhiều điều nữa. Bạn muốn tải nội dung hoạt động như vậy từ đâu?

Thay vì đoán xem cái gì an toàn và cái gì không, Angular sai lầm ở khía cạnh an toàn bằng cách từ chối tải tài nguyên động. Đó là lý do tại sao bạn nhận được thông báo lỗi. Vì vậy, về bản chất, Angular bảo vệ bạn khỏi những mẫu nguy hiểm này

Để kích hoạt hành vi này, ứng dụng của bạn sẽ phải chứng minh tính hợp lệ của URL mà bạn đang cố tải. Về bản chất, điều này có nghĩa là bạn sẽ phải đảm bảo URL được xây dựng an toàn hoặc bạn sẽ phải xác thực URL trước khi gán nó cho một thành phần HTML. Để ngăn Angular kích hoạt lỗi, bạn phải đánh dấu URL là an toàn trước khi gán nó. Bạn có thể làm điều đó bằng cách sử dụng hàm


Hello John!
69, như hình bên dưới

Biến một URL động thành một URL an toàn bằng cách sử dụng hàm bypassSecurityTrustResourceUrl


Hello John!
2

Lưu ý rằng trong ví dụ mã này, chúng tôi tạo URL Youtube an toàn bằng cách chỉ cho phép người dùng cung cấp ID video. Bằng cách đó, chúng tôi chắc chắn rằng URL trỏ đến


Hello John!
70. Đảm bảo ứng dụng của bạn tuân theo một mẫu tương tự để đảm bảo an toàn cho URL. Không đánh dấu một URL không xác định là an toàn vì điều này có thể gây ra các lỗ hổng trong ứng dụng của bạn

Và những gì về kết xuất trang phía máy chủ

Cái hay của Angular là nó là một framework giải quyết các trường hợp sử dụng phổ biến. Kết quả là mã Angular chạy trên trình duyệt chính là mã đang chạy trên máy chủ. Mặc dù điều này có vẻ hợp lý, nhưng điều này không phải lúc nào cũng đúng với các framework khác, chẳng hạn như React (Hãy xem loạt bài gồm 3 phần này về XSS trong React)

Cụ thể, đối với Angular, điều này có nghĩa là nếu bạn tuân theo các nguyên tắc viết mã an toàn, như đã thảo luận trong bài viết này, thì các trang được hiển thị phía máy chủ của bạn cũng được bảo mật. Angular Universal cũng bảo vệ chống lại các lỗ hổng bên ngoài khác, điều này thật tuyệt. Thật không may, điều này không phải lúc nào cũng đúng với các dự án dựa trên Angular. Bạn có thể đọc thêm về điều đó trong bài viết này về XSS đến


Hello John!
71, một lỗ hổng đã có trong Scully

Tóm tắt và hướng dẫn mã hóa an toàn

Điều đó đưa chúng ta đến phần tóm tắt của bài viết này về XSS trong các ứng dụng Angular. Để kết thúc, hãy tóm tắt lại những điều quan trọng nhất

  1. Nếu bạn tuân thủ cách làm này của Angular, Angular sẽ giúp bạn đảm bảo tính bảo mật cho ứng dụng của mình
  2. Để đảm bảo bạn để Angular thực hiện công việc của mình, hãy tránh thao tác DOM trực tiếp thông qua ElementRef, API Renderer2 hoặc API DOM gốc
  3. Đảm bảo ứng dụng của bạn không sử dụng hàm
    
    Hello John!
    
    72 với dữ liệu động

Đó là nó. Ba nguyên tắc đơn giản này sẽ giúp bạn tránh XSS trong Angular. Và nếu bạn thích bài viết này, đừng quên chia sẻ nó với bạn bè và đồng nghiệp Angular của bạn

Sử dụng InternalHTML trong góc có tốt không?

Angular đi kèm với trình khử trùng html tích hợp DomSanitizer , như một tính năng bảo mật, được sử dụng bất cứ khi nào bạn sử dụng [innerHtml]. Đó là một tính năng tuyệt vời - nhưng có một lỗi/tính năng khá khó chịu ở chỗ nếu bạn có các thành phần có kiểu nội tuyến, thì các kiểu đó sẽ bị xóa khỏi trang của bạn .

InternalHTML có phải là rủi ro bảo mật không?

Việc đặt giá trị của InternalHTML cho phép bạn dễ dàng thay thế nội dung hiện có của phần tử bằng nội dung mới. Ghi chú. Đây là rủi ro bảo mật nếu chuỗi được chèn có thể chứa nội dung độc hại tiềm ẩn .

Việc sử dụng InternalHTML trong góc là gì?

Angular 14 cung cấp liên kết thuộc tính [innerHTML] mà bạn có thể sử dụng để kết xuất HTML thay vì sử dụng phép nội suy , như bạn có thể .

Nhược điểm của việc sử dụng InternalHTML trong JavaScript là gì?

Nhược điểm của InternalHTML . Khi chúng tôi đã sử dụng trình xử lý sự kiện thì trình xử lý sự kiện sẽ không tự động được gắn vào các phần tử mới được tạo bởi InternalHTML. It is very slow because as inner HTML already parses the content even we have to parse the content again so that's why it takes time. When we have used the event handlers then the event handlers are not automatically attached to the new elements created by innerHTML.