« Blog

Content-Security-Policy và các security header cơ bản

Security header là nội quy website gửi cho trình duyệt. Bài này giải thích các header cơ bản (nosniff, frame-ancestors, Referrer-Policy, Permissions-Policy) và cách đưa CSP vào dần dần bằng chế độ Report-Only, kèm cấu hình cho Nginx, Apache, .htaccess, Cloudflare.

Security header là gì

Mỗi phản hồi của website có thể kèm các header dặn trình duyệt cách xử lý trang: không cho nhúng trang vào nơi khác, không đoán sai loại tệp, hạn chế thông tin gửi sang trang khác, chỉ chạy mã từ nguồn đã khai báo. Thêm chúng chỉ mất vài dòng cấu hình nhưng giảm đáng kể rủi ro mạo danh giao diện và chèn mã độc (XSS). Chúng không thay thế việc vá lỗi và viết mã an toàn.

Bộ header cơ bản

Header Giá trị gợi ý Tác dụng
X-Content-Type-Options nosniff Cấm trình duyệt tự đoán loại tệp, tránh việc tệp tải lên bị chạy như script
X-Frame-Options SAMEORIGIN hoặc DENY Chặn trang bị nhúng vào khung của trang lạ (clickjacking)
Referrer-Policy strict-origin-when-cross-origin Khi sang trang khác chỉ gửi tên miền, không gửi đường dẫn đầy đủ
Permissions-Policy camera=(), microphone=(), geolocation=() Tắt tính năng trình duyệt mà trang không dùng
Strict-Transport-Security xem bài về HSTS Buộc dùng HTTPS

Hai header lỗi thời nên bỏ: X-XSS-Protection (trình duyệt hiện đại đã bỏ bộ lọc này) và Feature-Policy (đã đổi tên thành Permissions-Policy). Cũng nên ẩn số phiên bản phần mềm khỏi header Server và X-Powered-By: Apache ServerTokens Prod, Nginx server_tokens off;, PHP expose_php = Off. Xem thêm bài về HSTS.

Content-Security-Policy

CSP là header mạnh nhất và cũng khó nhất, vì nó liệt kê nguồn được phép cho từng loại tài nguyên. Ví dụ một chính sách chặt cho website tĩnh:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self'; frame-ancestors 'none'; base-uri 'none'; object-src 'none'

Các chỉ thị chính: default-src là mặc định cho mọi loại; script-src và style-src cho script và CSS; img-src cho ảnh; frame-ancestors quyết định ai được nhúng trang (thay cho X-Frame-Options); object-src 'none' tắt plugin cũ; base-uri chặn chèn thẻ <base>.

Điều cần tránh nhất là 'unsafe-inline' và 'unsafe-eval' trong script-src, vì chúng cho phép mã nhúng và eval() chạy, làm CSP gần như mất tác dụng chống XSS. Nếu bắt buộc có script nhúng, dùng nonce hoặc hash thay vì 'unsafe-inline'. Cũng đừng cho script-src các nguồn quá rộng như *, https: hay data:.

Đưa CSP vào mà không làm vỡ giao diện

  1. Bắt đầu với header Content-Security-Policy-Report-Only: trình duyệt chỉ ghi vi phạm vào Console, chưa chặn gì.
  2. Duyệt các trang chính, mở DevTools, tab Console, ghi lại mọi nguồn bị báo (Google Fonts, Analytics, bản đồ, widget chat...).
  3. Thêm đúng các nguồn cần thiết vào chính sách, lặp lại tới khi Console sạch.
  4. Đổi tên header thành Content-Security-Policy để bắt đầu chặn thật, và vẫn theo dõi một thời gian.

Theo nền tảng

Nginx. Trong khối server:

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'" always;

Kiểm tra bằng nginx -t rồi nạp lại Nginx. Chú ý add_header trong khối location sẽ ghi đè các add_header ở cấp ngoài.

Apache và hosting cPanel, DirectAdmin (.htaccess). Cần mod_headers:

<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy-Report-Only "default-src 'self'"
</IfModule>

Mở .htaccess ở thư mục gốc website bằng File Manager (nhớ bật hiển thị tệp ẩn) và thêm vào cuối. Giữ bản sao lưu của tệp trước khi sửa; sai cú pháp có thể làm website báo lỗi 500.

Cloudflare. Không cần sửa máy chủ gốc: vào Rules, Transform Rules, Modify Response Header, tạo quy tắc áp dụng cho mọi yêu cầu và dùng Set static để đặt từng header ở trên. Riêng HSTS có mục riêng ở SSL/TLS, Edge Certificates.

Cookie phiên là "thẻ ra vào" của người đăng nhập, nên cần ba cờ: Secure (chỉ gửi qua HTTPS), HttpOnly (mã script không đọc được) và SameSite=Lax (hạn chế trang lạ lợi dụng phiên đăng nhập). Với PHP, đặt session.cookie_secure = 1, session.cookie_httponly = 1, session.cookie_samesite = Lax trong php.ini hoặc .user.ini (chỉ bật cookie_secure khi toàn website đã chạy HTTPS).

Cách tự kiểm tra

curl -sI https://example.vn

Kiểm tra từng dòng header vừa thêm. Rồi dán toàn bộ kết quả vào công cụ đọc security header: nó nêu header còn thiếu, cảnh báo 'unsafe-inline', phiên bản bị lộ ở Server và cờ cookie nếu có Set-Cookie; mọi thứ chỉ xử lý trong trình duyệt của bạn.

Cuối cùng, duyệt thủ công các trang chính (đăng nhập, giỏ hàng, form liên hệ) với DevTools mở để chắc chắn không có lỗi CSP trong Console trước khi chuyển từ Report-Only sang chặn thật.

Nguồn tham khảo

  1. MDN Web Docs: Content-Security-Policy
  2. OWASP Secure Headers Project
  1. HSTS: bật thế nào mà không tự khoá website

    · 4 phút đọc

    HSTS buộc trình duyệt luôn dùng HTTPS cho website của bạn, nhưng đặt sai có thể khiến cả tên miền con hoặc cả website không truy cập được. Bài này đưa ra lộ trình tăng max-age từng bước và cách bật trên Nginx, Apache, cPanel, DirectAdmin, Cloudflare.

    #web #https #hsts

  2. Chứng chỉ TLS hết hạn: nguyên nhân và cách phòng

    · 6 phút đọc

    Chứng chỉ hết hạn là sự cố rất phổ biến nhưng phòng được gần như hoàn toàn. Bài này liệt kê các nguyên nhân thật sự hay gặp (gia hạn tự động hỏng, quên nạp lại máy chủ, email nhắc gửi nhầm người) và cách đặt giám sát bằng openssl.

    #web #tls #https