Các header bảo mật nên thiết lập đầu tiên
Security header là một trong những cách gia cố rẻ nhất bạn có thể thêm vào site: vài dòng cấu hình server chỉ thị cho trình duyệt hoạt động nghiêm ngặt hơn. Chúng không thay thế cho mã an toàn, nhưng hạn chế thiệt hại khi có gì đó lọt qua. Dưới đây là danh sách ưu tiên, theo thứ tự chúng tôi sẽ triển khai, kèm header cần cẩn trọng nhất.
Những cải thiện nhanh hiếm khi làm hỏng gì
X-Content-Type-Options
Đặt nosniff ngăn trình duyệt đoán loại tệp từ nội dung của nó, nhờ đó một tệp tải lên độc hại không bị coi là script. Nó chỉ hoạt động đúng khi server của bạn gửi giá trị Content-Type chính xác, nên hãy kiểm tra điều đó trước.
Strict-Transport-Security
HSTS bảo trình duyệt dùng HTTPS cho domain của bạn trong một khoảng thời gian nhất định, mà không dựa vào việc redirect từ HTTP. Ví dụ thường gặp từ web.dev là thời hạn một năm tính bằng giây. Hãy bắt đầu với khoảng thời gian ngắn hơn trong khi bạn xác nhận mọi subdomain thật sự phục vụ HTTPS, và kéo dài khi đã chắc chắn.
Kiểm soát việc nhúng khung (framing)
Clickjacking hoạt động bằng cách nhúng trang của bạn vào khung của người khác. Header cũ hơn X-Frame-Options nhận DENY hoặc SAMEORIGIN. Directive CSP frame-ancestors làm cùng việc đó với khả năng kiểm soát chi tiết hơn, và hướng dẫn của MDN là frame-ancestors 'none' có thể thay thế header cũ trừ khi các trang của bạn buộc phải được nhúng.
Các header cô lập cross-origin
Cross-Origin-Opener-Policy cô lập cửa sổ của bạn khỏi các trang đã mở nó, và Cross-Origin-Resource-Policy giới hạn những site nào có thể tải tài nguyên của bạn. Chúng được khuyến nghị cho các site nói chung, còn Cross-Origin-Embedder-Policy chủ yếu quan trọng nếu bạn cần các tính năng như SharedArrayBuffer, vì việc yêu cầu tài nguyên được nhúng phải chủ động chấp thuận có thể làm hỏng nội dung của bên thứ ba.
# nginx: a conservative starting set
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always;
add_header Cross-Origin-Resource-Policy "same-site" always;
Hãy coi khối đó là một mẫu để thử nghiệm, không phải câu trả lời dán vào rồi quên. Riêng resource policy nên khớp với cách hình ảnh và script của bạn được chia sẻ giữa các subdomain.
Content-Security-Policy: header mạnh nhất
CSP giới hạn những script và tài nguyên khác mà một trang được phép tải hoặc chạy, nên mã bị chèn vào sẽ bị chặn ngay cả khi việc làm sạch dữ liệu thất bại. MDN nói rõ rằng nó bổ sung chứ không thay thế việc escape và làm sạch dữ liệu đầu vào. Một policy là danh sách các directive như default-src 'self', mỗi directive giới hạn một loại tài nguyên.
Khuyến nghị từ cả MDN và web.dev là một strict CSP dựa trên nonce hoặc hash thay vì các allowlist domain dài. Allowlist dễ làm sai, có xu hướng bao gồm các domain lưu trữ nội dung không an toàn, và trở nên cồng kềnh khi có nhiều tag của bên thứ ba. Một strict policy trông như sau:
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
frame-ancestors 'none'
- Dùng nonce khi server render trang động: một giá trị mới không đoán được cho mỗi response, được thêm vào từng thẻ script đáng tin cậy.
- Dùng hash cho trang tĩnh, nơi nội dung của mỗi inline script là cố định. Bất kỳ chỉnh sửa nào với script đều cần tính lại hash của nó.
'strict-dynamic'cho phép một script đáng tin cậy tải thêm các script khác, giúp tích hợp bên thứ ba dễ hơn, dù nó làm yếu bảo vệ nếu các script đó dựng thẻ từ đầu vào không đáng tin cậy.- Tránh
'unsafe-inline'; dù sao trình duyệt cũng bỏ qua nó khi có nonce hoặc hash, nhưng dựa vào nó làm mất mục đích.
Triển khai ở chế độ report-only
Áp dụng một policy mới mà không thử trước sẽ làm hỏng nhiều thứ. Hãy gửi nó trước dưới dạng Content-Security-Policy-Report-Only, thu thập các báo cáo vi phạm, sửa những gì chúng chỉ ra, rồi mới chuyển sang áp dụng thật. MDN mô tả việc báo cáo thông qua header Reporting-Endpoints với directive report-to, và khuyên giữ report-uri đã lỗi thời bên cạnh trong khi mức hỗ trợ của trình duyệt bắt kịp. Policy report-only không thể được gửi qua thẻ meta, thêm một lý do để ưu tiên header thật.
Reporting-Endpoints: csp="https://example.com/csp-reports"
Content-Security-Policy-Report-Only:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none'; base-uri 'none';
report-to csp
Refactor những gì policy chặn
Một strict policy chặn các inline event handler như onclick="...", URL javascript: và eval(). Hãy thay các handler bằng lệnh gọi addEventListener và loại bỏ việc dùng eval. Với việc chèn mã qua DOM, web.dev cũng mô tả Trusted Types, được bật bằng một directive CSP, buộc dữ liệu đi qua một policy làm sạch trước khi đến các API rủi ro như innerHTML.
Lưu ý về mốc thời gian
Bản tham khảo nhanh của web.dev được cập nhật lần cuối năm 2021, và nó ghi chú rằng Safari lúc đó không hỗ trợ strict-dynamic. Mức hỗ trợ của trình duyệt luôn thay đổi, vì vậy hãy xác minh khả năng tương thích hiện tại trước khi dựa vào bất kỳ header đơn lẻ nào, và lưu ý rằng web.dev chuyển phần Referrer-Policy sang một bài viết riêng.
Áp dụng thế nào trên nền tảng bạn không kiểm soát hoàn toàn
Trên các nền tảng được host sẵn, bạn có thể chỉ đặt được một số header, hoặc không đặt được gì. Khi đó hãy tập trung vào những gì bạn kiểm soát: các script bạn đưa vào và các tag bên thứ ba bạn cho phép. Nếu bạn muốn được hỗ trợ lập kế hoạch triển khai header cho một site WordPress hoặc site tùy biến, dịch vụ của chúng tôi là điểm khởi đầu tốt.