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ối hình ảnh do người dùng tải lên (ví dụ: ảnh hồ sơ) hoặc phức tạp như kết xuất HTML do người dùng kiểm soát (ví dụ: hướng dẫn phát triển web). Điều này luôn khó thực hiện một cách bảo mật, do đó 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 cũ để tách biệt nội dung không đáng tin cậy

Giải pháp cổ điển để phân phối an toàn nội dung do người dùng kiểm soát là sử dụng miền hộp cát. Về cơ bản, nếu miền chính của ứng dụng của bạn 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 hoạt động 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ể 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 công cụ này cho hình ảnh hoặc nội dung tải xuống, nhưng làm như vậy sẽ giúp tránh rủi ro do nắm bắt nội dung, đặc biệt là trong các trình duyệt 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ó hai nhược điểm lớn:

  • Các ứng dụng thường cần giới hạn quyền truy cập nội dung để chỉ cho phép một người dùng truy cập, do đó yêu cầu phải triển khai việc xác thực và cấp phép. Vì miền hộp cát chủ động không chia sẻ cookie với miền của ứng dụng chính, nên rất khó thực hiện việc này một cách an toàn. Để hỗ trợ tính năng 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 pháp thứ hai này đặc biệt rắc rối 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 đó không 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 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ụ: bằng cách đọc dữ liệu có cùng nguồn gốc).

Ngoài ra, cần lưu ý rằng 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 vào một miền tách 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, môi trường web phát triển và giờ đây có thêm nhiều cách để phân phát nội dung không tin cậy một cách dễ dàng và an toàn hơn. Có nhiều cách tiếp cận khác nhau, vì vậy chúng tôi sẽ phác thảo 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 không hoạt động của người dùng

Nếu một trang web chỉ cần phân phát nội dung người dùng không hoạt động (tức là nội dung không phải là HTML hoặc JavaScript, ví dụ như hình ảnh và tài nguyên 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 tách biệt. Có hai 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ả trình duyệt hỗ trợ và được đả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à lựa chọn an toàn).
  • Ngoài ra, luôn đặt các tiêu đề phản hồi bên dưới để đả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 việc đánh cắp 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

Tạo hộp cát cho nội dung như thể nội dung đó được phân phát trên một miền riêng

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

Vô hiệu hoá thực thi JavaScript (và bao gồm mọi tài nguyên phụ)

Cross-Origin-Resource-Policy: same-site

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

Sự kết hợp các tiêu đề này đảm bảo rằng phản hồi chỉ có thể được ứng dụng của bạn tải dưới dạng tài nguyên phụ hoặc người dùng tải xuống dưới dạng tệp. Hơn nữa, các tiêu đề này còn cung cấp nhiều lớp bảo vệ khỏi lỗi của 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, cách thiết lập nêu trên rất chắc chắn 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ách ly.

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

Mặc dù giải pháp trên thể hiện sự bảo vệ nhìn chung là đủ để 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 khác để cung cấp các lớp bảo mật bổ sung:

  • Đặ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 nhúng điểm cuối.
  • Hộp cát nội dung người dùng trên một miền con tách biệt bằng cách:
    • Phân phát nội dung 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 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 một cách an toàn (ví dụ: hình ảnh HTML hoặc SVG) mà không có đ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 biệt phản hồi. Mặc dù hiện tại, không phải tất cả trình duyệt web đều triển khai việc tách biệt quy trình đối với tài liệu hộp cát, nhưng việc liên tục cải tiến mô hình quy trình của trình duyệt có thể giúp cải thiện khả năng tách riêng nội dung trong hộp cát khỏi các ứng dụng nhúng. Nếu các cuộc tấn công SpectreJStrì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à một giải pháp hợp lý.
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 miền hộp cát. Ý tưởng cốt lõi là:

  • Tạo miền hộp cát mới rồi 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à trên nhiều trang web và do đó tách biệt hoàn toàn 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 nghe trình xử lý sự kiện message và hiển thị mọi nội dung mà trình xử lý đó nhận được.
  • Để sử dụng điều này, sản phẩm sẽ tạo iframe hoặc cửa sổ bật lên tới $RANDOM_VALUE.exampleusercontent.com/shim và sử dụng postMessage để gửi nội dung không đáng tin cậy đến phần đệm để hiển thị.
  • Nội dung kết xuất sẽ chuyển đổi thành Blob và hiển thị bên trong một iframe 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 được tách biệt hoàn toàn trên một trang web duy nhất. Đồng thời, khi giao dịch cho ứng dụng chính xử lý việc truy xuất dữ liệu sẽ hiển thị, bạn không cần phải sử dụng URL tính năng nữa.

Kết luận

Hai giải pháp này giúp bạn 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 đã 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 khác trong năm tới.