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. Điều này có thể đơn giản như việc 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ư việc 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 để cô lập 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 những nội dung được gọi là miền hộp cát. Về cơ bản, 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 là đa 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 mọi loại nội dung không đáng tin cậy, bao gồm cả hình ảnh, nội dung tải xuống và HTML. Mặc dù có vẻ như không cần thiết phải sử dụng tham số này cho hình ảnh hoặc nội dung tải xuống, nhưng việc này giúp tránh được các rủi ro do phát hiện 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, chúng có 2 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, điều này đòi hỏi phải triển khai quy trình xác thực và uỷ quyền. Vì các miền hộp cát không chia sẻ cookie với miền ứng dụng chính một cách có chủ đích, nên rất khó thực hiện việc này 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 khác của người dùng. Điều này tạo ra nguy cơ nội dung của người dùng độc hại tấn công các dữ liệu khác trên miền hộp cát (ví dụ: bằng cách đọc dữ liệu cùng nguồn).

Bạn cũng nê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.

Các 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 hơn, an toàn hơn để phân phát nội dung không đáng tin cậy. Có nhiều cách tiếp cận khác nhau ở đây, vì vậy, chúng tôi sẽ trình bày hai giải pháp mà chúng tôi sử dụng tại Google.

Cách 1: Phân phát nội dung của 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 của người dùng không hoạt động (tức là nội dung không phải là HTML hoặc JavaScript, chẳng hạn như hình ảnh và nội dung tải xuống), thì giờ đây, trang web 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 biệt lập. Có 2 bước chính:

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

X-Content-Type-Options: nosniff

Ngăn chặn việc dò tìm nội dung

Content-Disposition: attachment; filename="download"

Kích hoạt quá trình tải xuống thay vì hiển thị

Content-Security-Policy: sandbox

Cách ly nội dung 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 quá trình thực thi JavaScript (và việc đưa vào mọi tài nguyên phụ)

Cross-Origin-Resource-Policy: same-site

Ngăn không cho trang được đưa vào trên nhiều trang web

Sự kết hợp này của các tiêu đề đả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 có thể tải phản hồ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 các 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 được trình bày có độ tin cậy cao 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 về việc chèn hoặc cách ly.

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

Mặc dù giải pháp được đề xuất thường đủ sức 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 bổ sung để cung cấp thêm các lớp 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 theo:
    • 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.

Cách tiếp cận 2: Phân phát nội dung của 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 (ví dụ: hình ảnh HTML hoặc SVG) một cách an toàn 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.

Lựa chọn đơ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 biệt phản hồi. Mặc dù không phải trình duyệt web nào cũng triển khai tính năng cô lập tiến trình cho các 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 của trình duyệt có thể cải thiện khả năng tách biệt nội dung hộp cát khỏi các ứng dụng nhúng. Nếu các cuộc tấn công SpectreJSrenderer compromise 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à một giải pháp đủ hiệu quả. Tại Google, chúng tôi đã phát triển một giải pháp có thể hoàn toàn cô lập nội dung độ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 là các cookie 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 chuyể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, có chức năng theo dõi trình xử lý sự kiện message và kết xuất mọi nội dung mà trình xử lý này nhận được.
  • Để sử dụng tính năng này, sản phẩm sẽ tạo một iframe hoặc hộp thoại 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 được kết xuất sẽ được chuyển đổi thành một Blob và kết xuất bên trong một iframe có thuộc tính 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 cách ly hoàn toàn trên một trang web duy nhất. Ngoài ra, bằng cách để ứ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

Khi kết hợp với nhau, hai giải pháp này 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 an toàn hơn, 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 sang sử dụng các giải pháp này và có kế hoạch di chuyển thêm trong năm tới.