Lưu trữ an toàn dữ liệu người dùng trong các ứng dụng web hiện đại

David Dworken
David Dworken

Nhiều ứng dụng web cần hiển thị nội dung do người dùng kiểm soát. Việc này có thể đơn giản như phân phát hình ảnh do người dùng tải lên (ví dụ: ảnh hồ sơ) hoặc phức tạp như hiển thị HTML do người dùng kiểm soát (ví dụ: hướng dẫn phát triển web). Việc này luôn khó thực hiện một cách an toàn, vì vậy, chúng tôi đã nỗ lực tìm ra các giải pháp dễ dàng nhưng an toàn có thể áp dụng cho hầu hết các loại ứng dụng web.

Các giải pháp truyền thống để tách biệt nội dung không đáng tin cậy

Giải pháp cổ điển để phân phát nội dung do người dùng kiểm soát một cách an toàn là sử dụng miền hộp cát. Ý tưởng cơ bản là nếu miền chính của ứng dụng là example.com, thì bạn có thể phân phát tất cả nội dung không đáng tin cậy trên exampleusercontent.com. Vì hai miền này nằm trên nhiều trang web, nên mọi nội dung độc hại trên exampleusercontent.com đều không thể ảnh hưởng đến example.com.
Bạn có thể sử dụng phương pháp này để phân phát an toàn tất cả các loại nội dung không đáng tin cậy, bao gồm hình ảnh, nội dung tải xuống và HTML. Mặc dù có vẻ như bạn không cần phải sử dụng tính năng này cho hình ảnh hoặc nội dung tải xuống, nhưng việc này sẽ giúp tránh các rủi ro từ việc quét nội dung, đặc biệt là trong các trình duyệt cũ.
Các miền hộp cát được sử dụng rộng rãi trong ngành và đã hoạt động hiệu quả trong một thời gian dài. Tuy nhiên, các phương thức này có hai nhược điểm lớn:

  • Các ứng dụng thường cần hạn chế quyền truy cập nội dung cho một người dùng duy nhất. Do đó, bạn cần triển khai quy trình xác thực và uỷ quyền. Vì các miền hộp cát cố ý không chia sẻ cookie với miền ứng dụng chính, nên việc này rất khó thực hiện một cách an toàn. Để hỗ trợ xác thực, các trang web phải dựa vào URL chức năng hoặc phải đặt cookie xác thực riêng cho miền hộp cát. Phương thức thứ hai này đặc biệt gây ra vấn đề trong web hiện đại, nơi nhiều trình duyệt hạn chế cookie trên nhiều trang web theo mặc định.
  • Mặc dù nội dung của người dùng được tách biệt với trang web chính, nhưng nội dung này không được tách biệt với nội dung của người dùng khác. Điều này tạo ra nguy cơ nội dung độc hại của người dùng tấn công dữ liệu khác trên miền hộp cát (ví dụ: thông qua việc đọc dữ liệu cùng nguồn gốc).

Cũng cần lưu ý rằng các miền hộp cát giúp giảm thiểu rủi ro lừa đảo vì các tài nguyên được phân đoạn rõ ràng trên một miền riêng biệt.

Giải pháp hiện đại để phân phát nội dung của người dùng

Theo thời gian, web đã phát triển và hiện có những cách dễ dàng, an toàn hơn để phân phát nội dung không đáng tin cậy. Có nhiều phương pháp khác nhau ở đây, vì vậy, chúng tôi sẽ trình bày hai giải pháp hiện đang được sử dụng rộng rãi tại Google.

Phương pháp 1: Phân phát nội dung cho người dùng không hoạt động

Nếu một trang web chỉ cần phân phát nội dung không hoạt động của người dùng (tức là nội dung không phải HTML hoặc JavaScript, chẳng hạn như hình ảnh và nội dung tải xuống), thì giờ đây, bạn có thể thực hiện việc này một cách an toàn mà không cần miền hộp cát riêng biệt. Có hai bước chính:

  • Luôn đặt tiêu đề Content-Type thành một loại MIME nổi tiếng mà tất cả trình duyệt đều hỗ trợ và đảm bảo không chứa nội dung đang hoạt động (khi không chắc chắn, application/octet-stream là một lựa chọn an toàn).
  • Ngoài ra, hãy luôn đặt các tiêu đề phản hồi dưới đây để đảm bảo rằng trình duyệt tách biệt hoàn toàn phản hồi.
Tiêu đề phản hồi Mục đích

X-Content-Type-Options: nosniff

Ngăn chặn việc đánh hơi nội dung

Content-Disposition: attachment; filename="download"

Kích hoạt quá trình tải xuống thay vì kết xuất

Content-Security-Policy: sandbox

Đưa nội dung vào hộp cát như thể nội dung đó được phân phát trên một miền riêng biệt

Content-Security-Policy: default-src ‘none'

Tắt tính năng thực thi JavaScript (và bao gồm mọi tài nguyên phụ)

Cross-Origin-Resource-Policy: same-site

Ngăn trang được đưa vào nhiều trang web

Sự kết hợp các tiêu đề này đảm bảo rằng ứng dụng của bạn chỉ có thể tải phản hồi dưới dạng tài nguyên phụ hoặc người dùng chỉ có thể tải xuống dưới dạng tệp. Hơn nữa, các tiêu đề này cung cấp nhiều lớp bảo vệ chống lại lỗi trình duyệt thông qua tiêu đề hộp cát CSP và quy tắc hạn chế default-src. Nhìn chung, chế độ thiết lập nêu trên giúp bạn có thể tin tưởng rằng các phản hồi được phân phát theo cách này không thể dẫn đến các lỗ hổng chèn hoặc cô lập.

Phòng thủ theo chiều sâu

Mặc dù giải pháp trên thường là đủ để bảo vệ chống lại XSS, nhưng bạn có thể áp dụng một số biện pháp tăng cường bảo mật khác để tăng cường bảo mật:

  • Đặt tiêu đề X-Content-Security-Policy: sandbox để tương thích với IE11.
  • Đặt tiêu đề Content-Security-Policy: frame-ancestors 'none' để chặn việc nhúng điểm cuối.
  • Nội dung của người dùng trong Hộp cát trên một miền con riêng biệt bằng cách:
    • Phân phát nội dung của người dùng trên một miền con riêng biệt (ví dụ: Google sử dụng các miền như product.usercontent.google.com).
    • Đặt Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp để bật tính năng tách biệt nhiều nguồn gốc.

Phương pháp 2: Phân phát nội dung cho người dùng đang hoạt động

Bạn cũng có thể phân phát nội dung đang hoạt động một cách an toàn (ví dụ: hình ảnh HTML hoặc SVG) mà không gặp phải những điểm yếu của phương pháp miền hộp cát cổ điển.
Cách đơn giản nhất là tận dụng tiêu đề Content-Security-Policy: sandbox để yêu cầu trình duyệt tách riêng phản hồi. Mặc dù không phải tất cả trình duyệt web hiện đều triển khai tính năng tách biệt quy trình cho tài liệu hộp cát, nhưng việc liên tục tinh chỉnh các mô hình quy trình trình duyệt có thể cải thiện khả năng tách biệt nội dung trong hộp cát với các ứng dụng nhúng. Nếu các cuộc tấn công SpectreJSxâm phạm trình kết xuất nằm ngoài mô hình mối đe doạ của bạn, thì việc sử dụng hộp cát CSP có thể là giải pháp đầy đủ.
Tại Google, chúng tôi đã phát triển một giải pháp có thể tách biệt hoàn toàn nội dung đang hoạt động không đáng tin cậy bằng cách hiện đại hoá khái niệm về miền hộp cát. Ý tưởng cốt lõi là:

  • Tạo một miền hộp cát mới được thêm vào danh sách hậu tố công khai. Ví dụ: bằng cách thêm exampleusercontent.com vào PSL, bạn có thể đảm bảo rằng foo.exampleusercontent.combar.exampleusercontent.com nằm trên nhiều trang web và do đó hoàn toàn tách biệt với nhau.
  • Tất cả URL khớp với *.exampleusercontent.com/shim đều được định tuyến đến một tệp shim tĩnh. Tệp shim này chứa một đoạn mã HTML và JavaScript ngắn để theo dõi trình xử lý sự kiện message và hiển thị mọi nội dung mà trình xử lý sự kiện nhận được.
  • Để sử dụng tính năng này, sản phẩm sẽ tạo một iframe hoặc cửa sổ bật lên cho $RANDOM_VALUE.exampleusercontent.com/shim và sử dụng postMessage để gửi nội dung không đáng tin cậy đến shim để hiển thị.
  • Nội dung hiển thị được chuyển đổi thành Blob và hiển thị bên trong iframe có hộp cát.

So với phương pháp miền hộp cát cổ điển, phương pháp này đảm bảo rằng tất cả nội dung đều được tách biệt hoàn toàn trên một trang web riêng biệt. Ngoài ra, khi ứng dụng chính xử lý việc truy xuất dữ liệu cần hiển thị, bạn không cần phải sử dụng URL chức năng nữa.

Kết luận

Hai giải pháp này kết hợp với nhau giúp bạn có thể di chuyển khỏi các miền hộp cát cổ điển như googleusercontent.com sang các giải pháp bảo mật hơn và tương thích với tính năng chặn cookie của bên thứ ba. Tại Google, chúng tôi đã di chuyển nhiều sản phẩm để sử dụng các giải pháp này và dự định di chuyển thêm nhiều sản phẩm khác trong năm tới.