Chào mọi người trên Cafedev! Hôm nay, chúng ta sẽ cùng nhau khám phá về một chủ đề quan trọng và đang được rất nhiều nhà phát triển quan tâm – bảo mật trong VueJS. Trong thế giới của phát triển web, việc bảo vệ ứng dụng của bạn khỏi các lỗ hổng bảo mật là điều hết sức quan trọng. Hãy cùng tìm hiểu cách VueJS giúp chúng ta xây dựng ứng dụng an toàn và bảo mật hơn.

1. Báo cáo Các Lỗ Hổng

Khi có một lỗ hổng được báo cáo, nó ngay lập tức trở thành ưu tiên hàng đầu của chúng họ, với một người đóng góp làm việc toàn thời gian để giải quyết vấn đề đó. Để báo cáo một lỗ hổng, vui lòng gửi email đến địa chỉ security@vuejs.org.

Mặc dù việc phát hiện ra các lỗ hổng mới là hiếm, nhưng chúng tôi cũng khuyên bạn luôn sử dụng phiên bản mới nhất của Vue và các thư viện đồng hành chính thức của nó để đảm bảo ứng dụng của bạn luôn được bảo mật tốt nhất có thể.

2. Quy Tắc Số 1: Không Sử Dụng Mẫu Không Đáng Tin Cậy

Quy tắc bảo mật cơ bản nhất khi sử dụng Vue là không bao giờ sử dụng nội dung không đáng tin cậy làm mẫu thành phần của bạn. Việc làm này tương đương với việc cho phép thực thi mã JavaScript tùy ý trong ứng dụng của bạn – và tệ hơn nữa, có thể dẫn đến việc vi phạm máy chủ nếu mã được thực thi trong quá trình kết xuất phía máy chủ. Một ví dụ về việc sử dụng như vậy:

Vue.createApp({
  template: `<div>` + userProvidedString + `</div>` // NEVER DO THIS
}).mount('#app')

Các mẫu Vue được biên dịch thành JavaScript, và các biểu thức trong mẫu sẽ được thực thi như một phần của quá trình kết xuất. Mặc dù các biểu thức được đánh giá dựa trên một ngữ cảnh kết xuất cụ thể, nhưng do sự phức tạp của môi trường thực thi toàn cầu tiềm ẩn, việc hoàn toàn bảo vệ bạn khỏi việc thực thi mã độc hại một cách tiềm ẩn mà không gây ra quá nhiều tải trọng hiệu suất không thực tế là không khả thi cho một framework như Vue. Cách đơn giản nhất để tránh loại vấn đề này hoàn toàn là đảm bảo nội dung của mẫu Vue của bạn luôn được tin tưởng và hoàn toàn được kiểm soát bởi bạn.

3. Những gì Vue làm để bảo vệ bạn

3.1 Nội dung HTML

Cho dù sử dụng mẫu hoặc hàm kết xuất, nội dung sẽ tự động được thoát. Điều đó có nghĩa trong mẫu này:

<h1>{{ userProvidedString }}</h1>

nếu userProvidedString chứa:

'<script>alert("hi")</script>'

sau đó nó sẽ được thoát sang HTML sau:

<script>alert("hi")</script>

do đó ngăn chặn việc tiêm mã. Việc thoát này được thực hiện bằng cách sử dụng các API trình duyệt cơ bản, như textContent, vì vậy một lỗ hổng chỉ có thể tồn tại nếu trình duyệt chính nó bị lỗ hổng.

3.2 Ràng buộc thuộc tính

Tương tự, các ràng buộc thuộc tính động cũng tự động được thoát. Điều đó có nghĩa trong mẫu này:

<h1 :title="userProvidedString">
  hello
</h1>

nếu userProvidedString chứa:

'" onclick="alert(\'hi\')'

sau đó nó sẽ được thoát sang HTML sau:

" onclick="alert('hi')

do đó ngăn chặn việc đóng của thuộc tính title để tiêm HTML mới, tùy ý. Việc thoát này được thực hiện bằng cách sử dụng các API trình duyệt cơ bản, như setAttribute, vì vậy một lỗ hổng chỉ có thể tồn tại nếu trình duyệt chính nó bị lỗ hổng.

4. Nguy cơ Tiềm ẩn

Trong bất kỳ ứng dụng web nào, việc cho phép nội dung được cung cấp bởi người dùng mà không được làm sạch và thực thi như HTML, CSS hoặc JavaScript có thể tiềm ẩn nguy cơ, vì vậy nên tránh nó nếu có thể. Tuy nhiên, cũng có những lúc khi một số rủi ro có thể được chấp nhận.
Ví dụ, các dịch vụ như CodePen và JSFiddle cho phép nội dung do người dùng cung cấp được thực thi, nhưng nó được thực hiện trong một ngữ cảnh nơi điều này được mong đợi và được sandbox một phần bên trong các iframes. Trong những trường hợp khi một tính năng quan trọng bản thân yêu cầu một mức độ của lỗ hổng, đó là trách nhiệm của đội của bạn để cân nhắc sự quan trọng của tính năng so với các kịch bản xấu nhất mà lỗ hổng cho phép.

4.1 Tiêm Mã HTML

Như bạn đã học trước đó, Vue tự động thoát nội dung HTML, ngăn bạn vô tình tiêm mã HTML thực thi vào ứng dụng của bạn. Tuy nhiên, trong những trường hợp bạn biết HTML là an toàn, bạn có thể rõ ràng kết xuất nội dung HTML:
– Sử dụng mẫu:

<div v="userProvidedHtml"></div>

– Sử dụng một hàm kết xuất:

h('div', {
    innerHTML: this.userProvidedHtml
  })

– Sử dụng một hàm kết xuất với JSX:

x
  <div innerHTML={this.userProvidedHtml}></div>

cảnh báo HTML do người dùng cung cấp không thể bao giờ được coi là 100% an toàn trừ khi nó được đặt trong một iframe sandbox hoặc trong một phần của ứng dụng mà chỉ người dùng viết HTML đó mới bị tiếp xúc. Ngoài ra, cho phép người dùng viết các mẫu Vue riêng của họ mang lại những nguy cơ tương tự.

4.2 Tiêm Mã URL

Trong một URL như sau:

<a :href="userProvidedUrl">
  click me
</a>

Có một vấn đề bảo mật tiềm ẩn nếu URL chưa được “làm sạch” để ngăn thực thi JavaScript bằng cách sử dụng javascript:. Có các thư viện như sanitize-url để hỗ trợ việc này, nhưng lưu ý: nếu bạn đang thực hiện việc làm sạch URL trên frontend, bạn đã có một vấn đề bảo mật. URL do người dùng cung cấp luôn phải được làm sạch bởi backend của bạn trước khi được lưu vào cơ sở dữ liệu. Sau đó, vấn đề sẽ được tránh cho mọi client kết nối với API của bạn, bao gồm cả các ứng dụng di động native. Cũng lưu ý rằng ngay cả với các URL đã được làm sạch, Vue không thể giúp bạn đảm bảo rằng chúng dẫn đến các điểm đến an toàn.

4.3 Tiêm Mã Style

Nhìn vào ví dụ này:

<a
  :href="sanitizedUrl"
  :style="userProvidedStyles"
  click me
</a>

Hãy giả sử sanitizedUrl đã được làm sạch, để chắc chắn rằng đó là một URL thực sự và không phải là JavaScript. Với userProvidedStyles, người dùng độc hại vẫn có thể cung cấp CSS để “click jack”, ví dụ như thiết kế liên kết thành một hộp trong suốt che phủ nút “Đăng nhập”. Sau đó, nếu https://user-controlled-website.com/ được xây dựng để giống như trang đăng nhập của ứng dụng của bạn, họ có thể đã lấy được thông tin đăng nhập thực sự của một người dùng.
Bạn có thể tưởng tượng được việc cho phép nội dung do người dùng cung cấp cho một phần tử

Bạn có thể tưởng tượng việc cho phép nội dung do người dùng cung cấp cho một phần tử <style> sẽ tạo ra một lỗ hổng lớn hơn, cho phép người dùng kiểm soát hoàn toàn cách thức thiết kế trang web. Đó là lý do tại sao Vue ngăn chặn việc hiển thị các thẻ style bên trong các mẫu, như:

<style>{{ userProvidedStyles }}</style>

Để đảm bảo an toàn hoàn toàn cho người dùng của bạn khỏi clickjacking, chúng tôi khuyên bạn nên chỉ cho phép kiểm soát hoàn toàn CSS trong một iframe được sandbox. Hoặc nếu cung cấp kiểm soát của người dùng thông qua một ràng buộc kiểu dáng, chúng tôi khuyên bạn nên sử dụng cú pháp đối tượng của nó và chỉ cho phép người dùng cung cấp các giá trị cho các thuộc tính cụ thể mà an toàn cho họ kiểm soát, như sau:

<a
  :href="sanitizedUrl"
  :style="{
    color: userProvidedColor,
    background: userProvidedBackground
  }"
>
  click me
</a>

4.4 Tiêm JavaScript

Chúng tôi mạnh mẽ khuyến khích không bao giờ hiển thị một phần tử <script> với Vue, vì các mẫu và hàm render không bao giờ nên có tác dụng phụ. Tuy nhiên, điều này không phải là cách duy nhất để bao gồm các chuỗi sẽ được đánh giá là JavaScript tại thời điểm chạy.

Mỗi phần tử HTML đều có các thuộc tính với các giá trị chấp nhận chuỗi của JavaScript, như onclick, onfocus và onmouseenter. Ràng buộc JavaScript do người dùng cung cấp cho bất kỳ trong số các thuộc tính sự kiện này đều là một nguy cơ an ninh tiềm ẩn, vì vậy nên tránh sử dụng.

Cảnh báo: JavaScript được cung cấp bởi người dùng không bao giờ có thể được coi là an toàn 100% trừ khi nó được đặt trong một iframe được sandbox hoặc ở một phần của ứng dụng nơi chỉ người dùng viết JavaScript đó có thể bao giờ được tiếp xúc với nó.

Đôi khi chúng tôi nhận được các báo cáo về lỗ hổng về cách thực hiện cross-site scripting (XSS) trong các mẫu Vue. Nói chung, chúng tôi không coi những trường hợp như vậy là lỗ hổng thực sự vì không có cách thực tế nào để bảo vệ nhà phát triển khỏi hai tình huống có thể cho phép XSS:

  1. Nhà phát triển đang yêu cầu một cách rõ ràng Vue hiển thị nội dung được cung cấp bởi người dùng và chưa qua sự kiểm tra như là mẫu Vue. Điều này là không an toàn theo bản chất, và không có cách nào cho Vue biết nguồn gốc của nó.
  2. Nhà phát triển đang gắn Vue vào một trang HTML hoàn chỉnh mà có chứa nội dung được server-render và được cung cấp bởi người dùng. Điều này về cơ bản là vấn đề giống như #1, nhưng đôi khi các nhà phát triển có thể thực hiện mà không nhận ra điều đó. Điều này có thể dẫn đến các lỗ hổng có thể nơi tấn công cung cấp HTML an toàn nhưng không an toàn khi được xem là mẫu Vue. Thực hành tốt nhất là không bao giờ gắn Vue vào các nút có thể chứa nội dung được server-render và được cung cấp bởi người dùng.

5. Bạn nên

Quy tắc chung là nếu bạn cho phép nội dung được cung cấp bởi người dùng và chưa qua sự kiểm tra (dưới dạng HTML, JavaScript, hoặc thậm chí là CSS) được thực thi, bạn có thể mở cửa cho các cuộc tấn công. Lời khuyên này thực sự đúng cho cả khi sử dụng Vue, một framework khác, hoặc thậm chí không sử dụng framework nào.

Ngoài các khuyến nghị đã được đề cập ở trên về Các Nguy Cơ Tiềm Ẩn, chúng tôi cũng khuyến khích bạn nên làm quen với những tài nguyên sau:

Sau đó, sử dụng những gì bạn học được để cũng xem xét mã nguồn của các phụ thuộc của bạn để tìm các mẫu nguy hiểm tiềm ẩn, nếu bất kỳ cái nào trong số chúng bao gồm các thành phần của bên thứ ba hoặc ảnh hưởng đến những gì được hiển thị trên DOM.

6. Backend Coordination​

Điều này nên được xem xét và xử lý bởi backend team, vì vậy chúng tôi không cung cấp một giải pháp cụ thể cho nó. Tuy nhiên, tốt nhất là liên lạc với đội ngũ backend của bạn để biết cách tương tác tốt nhất với API của họ, ví dụ như gửi các token CSRF cùng với việc gửi biểu mẫu.

7. Server-Side Rendering (SSR)​

Có một số vấn đề bảo mật bổ sung khi sử dụng SSR, vì vậy hãy đảm bảo tuân thủ các quy tắc tốt nhất được trình bày trong tài liệu SSR của chúng họ để tránh các lỗ hổng bảo mật.

Trong bài viết này, chúng ta đã cùng nhau tìm hiểu về tầm quan trọng của bảo mật trong VueJS. Việc hiểu và áp dụng các nguyên tắc bảo mật không chỉ giúp ứng dụng của bạn trở nên an toàn hơn mà còn tạo điều kiện thuận lợi cho sự phát triển và mở rộng của dự án. Tại Cafedev, chúng tôi hy vọng rằng thông tin trong bài viết này đã giúp bạn có cái nhìn rõ ràng hơn về cách bảo vệ ứng dụng VueJS của mình. Hãy tiếp tục khám phá và áp dụng những nguyên tắc bảo mật này để xây dựng các sản phẩm chất lượng và an toàn hơn.

Tham khảo thêm: MIỄN PHÍ 100% | Series tự học Vuejs từ cơ bản tới nâng cao

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!