Header bảo vệ người dùng trình duyệt (XSS, clickjacking, downgrade HTTPS). Với API chỉ phục vụ máy, HSTS và X-Content-Type-Options vẫn nên có; CSP chủ yếu cho trang HTML. Cấu hình reverse proxy đầy đủ xem Nginx reverse proxy + TLS.
Header và giá trị khuyên dùng
| Header | Giá trị điển hình | Chống gì |
|---|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
Downgrade về HTTP, SSL stripping |
Content-Security-Policy |
default-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self' |
XSS, nhúng script lạ, clickjacking |
X-Frame-Options |
DENY hoặc SAMEORIGIN |
Clickjacking (trình duyệt cũ; CSP frame-ancestors thay thế) |
X-Content-Type-Options |
nosniff |
Trình duyệt đoán sai MIME, chạy file upload như script |
Referrer-Policy |
strict-origin-when-cross-origin (hoặc no-referrer) |
Lộ URL có token/ID sang site khác |
Permissions-Policy |
camera=(), microphone=(), geolocation=() |
Trang/iframe dùng API nhạy cảm |
Cross-Origin-Opener-Policy |
same-origin |
Cửa sổ khác origin truy cập window.opener |
Cache-Control (trang có dữ liệu riêng tư) |
no-store |
Proxy/trình duyệt cache dữ liệu đăng nhập |
HSTS với includeSubDomains (và nhất là preload) áp lên mọi subdomain: subdomain nào chưa có HTTPS sẽ không vào được cho tới khi hết max-age. Bắt đầu bằng max-age=300, tăng dần.
Directive CSP hay dùng
| Directive | Ví dụ | Ghi chú |
|---|---|---|
default-src |
'self' |
Mặc định cho các loại chưa khai báo |
script-src |
'self' https://cdn.example.net |
Tránh 'unsafe-inline', 'unsafe-eval'; dùng nonce/hash nếu cần inline |
style-src |
'self' 'unsafe-inline' |
Nhiều framework UI cần inline style |
img-src |
'self' data: |
|
connect-src |
'self' wss://api.example.com |
fetch/XHR/WebSocket |
frame-ancestors |
'none' hoặc 'self' |
Ai được nhúng trang này vào iframe; không đặt được qua thẻ meta |
upgrade-insecure-requests |
(không giá trị) | Tự đổi URL http:// trong trang thành https:// |
report-to / report-uri |
report-uri /csp-report |
Nhận báo cáo vi phạm |
Triển khai CSP an toàn: dùng Content-Security-Policy-Report-Only trước, xem console/report, sửa, rồi mới chuyển sang header thật.
nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;server {
listen 443 ssl;
server_name app.example.com;
include snippets/security-headers.conf;
location /static/ {
add_header Cache-Control "public, max-age=86400";
include snippets/security-headers.conf; # bắt buộc include lại, xem lưu ý dưới
}
}| Lưu ý | Giải thích |
|---|---|
Kế thừa add_header |
Block con (location) có bất kỳ add_header nào thì không kế thừa add_header của block cha. Include lại snippet trong mọi block có add_header riêng |
always |
Không có always thì header chỉ gắn vào response 2xx/3xx, trang lỗi 4xx/5xx thiếu header |
| Header trùng | Upstream đã gửi header rồi thì thêm proxy_hide_header <Tên>; trước khi tự add_header |
| HSTS trên HTTP | Chỉ đặt trong server 443; trình duyệt bỏ qua HSTS gửi qua HTTP |
Kiểm tra
| Mục đích | Lệnh |
|---|---|
| Xem header response | curl -sSI https://app.example.com/ | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer|permissions' |
| Header trên trang lỗi | curl -sSI https://app.example.com/khong-ton-tai |
| Header ở path con | curl -sSI https://app.example.com/static/app.css |
| Xem cấu hình nginx đang hiệu lực | sudo nginx -T | grep -n add_header |
| Đánh giá online | securityheaders.com, Mozilla Observatory |
Tham khảo: MDN HTTP headers, OWASP Secure Headers Project.