Security headers là lớp bảo vệ đầu tiên chống lại nhiều tấn công web phổ biến. Nhiều header chỉ là 1 dòng config — nhưng hiệu quả rất lớn.
Content Security Policy (CSP)#
CSP kiểm soát resource nào được load trên trang — chống XSS và data injection mạnh nhất.
Cấu Trúc#
Content-Security-Policy: default-src 'self'; script-src 'self' https://analytics.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.comhttpDirectives#
const csp = [
// Default — fallback cho các directive khác
"default-src 'self'",
// Script — chỉ cho phép từ same origin
"script-src 'self' 'wasm-unsafe-eval'",
// Style
"style-src 'self' 'unsafe-inline'",
// Images
"img-src 'self' data: https://*.cloudinary.com",
// Font
"font-src 'self' https://fonts.gstatic.com",
// Connect (fetch, XHR, WebSocket)
"connect-src 'self' https://api.example.com wss://ws.example.com",
// Frame
"frame-src 'none'",
// Object (Flash, plugins)
"object-src 'none'",
// Form action
"form-action 'self'",
// Base URI
"base-uri 'self'",
// Report
"report-uri /csp-report",
].join('; ');typescriptCSP Report Only#
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reporthttpChỉ report, không block — dùng để test CSP mới trước khi enforce.
Nounce & Hash#
<!-- Nonce — random token mỗi request -->
<script nonce="abc123">
inlineScript();
</script>htmlContent-Security-Policy: script-src 'nonce-abc123'http<!-- Hash — hash của inline script -->
<script>
inlineScript();
</script>htmlContent-Security-Policy: script-src 'sha256-abc123...'httpStrict Transport Security (HSTS)#
Yêu cầu trình duyệt chỉ dùng HTTPS — không bao giờ dùng HTTP:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadhttp| Directive | Ý nghĩa |
|---|---|
max-age=31536000 | 1 năm — nhớ chỉ dùng HTTPS |
includeSubDomains | Áp dụng cho subdomain |
preload | Đưa vào HSTS preload list (Chrome, Firefox) |
Preload#
Khi preload được set và domain được submit vào hstspreload.org ↗ — browser built-in list, không bao giờ cho phép HTTP ngay cả lần đầu.
X-Frame-Options#
Chống clickjacking — không cho trang web bị iframe:
X-Frame-Options: DENY
# hoặc
X-Frame-Options: SAMEORIGINhttpCSP tương đương: frame-ancestors 'self' (CSP mạnh hơn, nên dùng CSP thay vì X-Frame-Options).
X-Content-Type-Options#
Chống MIME type sniffing — browser không tự đoán content type:
X-Content-Type-Options: nosniffhttpNếu không có header này, browser có thể đọc file .txt như HTML và chạy script.
Referrer-Policy#
Kiểm soát thông tin referrer gửi đi:
Referrer-Policy: strict-origin-when-cross-originhttp| Policy | Mô tả |
|---|---|
no-referrer | Không gửi referrer |
same-origin | Chỉ gửi khi cùng origin |
strict-origin-when-cross-origin | (Mặc định) Gửi full URL cho same-origin, chỉ origin cho cross-origin, không gửi khi HTTPS→HTTP |
no-referrer-when-downgrade | Gửi full URL, trừ khi HTTPS→HTTP |
Permissions-Policy (Feature Policy)#
Kiểm soát API browser được phép dùng:
Permissions-Policy: camera=(), microphone=(), geolocation=(self "https://maps.example.com"), payment=()http# Chặn tất cả
Permissions-Policy: camera=(), microphone=(), geolocation=()
# Cho phép một số origin
Permissions-Policy: geolocation=(self "https://maps.example.com"), usb=()
# Cho phép tất cả (không khuyến nghị)
Permissions-Policy: camera=*httpCross-Origin-Read-Block (CORB) & COOP/COEP#
Cross-Origin-Opener-Policy (COOP)#
Cross-Origin-Opener-Policy: same-originhttpIsolate trang khỏi cross-origin — chống Spectre attack.
Cross-Origin-Embedder-Policy (COEP)#
Cross-Origin-Embedder-Policy: require-corshttpYêu cầu cross-origin resource phải có CORS header — cho phép dùng SharedArrayBuffer.
Cross-Origin-Resource-Policy (CORP)#
Cross-Origin-Resource-Policy: same-origin
# hoặc same-site, cross-originhttpCache Headers#
# Static assets — cache dài
Cache-Control: public, max-age=31536000, immutable
# HTML — không cache
Cache-Control: no-cache, no-store, must-revalidate
# API response — cache có thời hạn
Cache-Control: private, max-age=300httpImplementation — Express#
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'wasm-unsafe-eval'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https://images.example.com'],
connectSrc: ["'self'", 'https://api.example.com'],
fontSrc: ["'self'", 'https://fonts.gstatic.com'],
objectSrc: ["'none'"],
frameSrc: ["'none'"],
formAction: ["'self'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));typescriptImplementation — Cloudflare Workers#
const securityHeaders = {
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'",
};
export default {
async fetch(request: Request): Promise<Response> {
const response = await handleRequest(request);
const newResponse = new Response(response.body, response);
Object.entries(securityHeaders).forEach(([key, value]) => {
newResponse.headers.set(key, value);
});
return newResponse;
},
};typescriptCheck Security Headers#
# Curl
curl -I https://example.com | grep -i security
# Online tools
# https://securityheaders.com
# https://csp-evaluator.withgoogle.combashChecklist#
- Content-Security-Policy (CSP) — chống XSS
- Strict-Transport-Security (HSTS) — HTTPS bắt buộc
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY hoặc CSP frame-ancestors
- Referrer-Policy: strict-origin-when-cross-origin
- Permissions-Policy: thu hẹp API access
- Cache-Control: phù hợp cho từng loại resource
- Set-Cookie: có HttpOnly, Secure, SameSite
Kết Luận#
Security headers là low-effort, high-impact. Mất 10 phút để config — bảo vệ chống XSS, clickjacking, MIME sniffing, và nhiều tấn công khác. Dùng Helmet (Node.js) hoặc tự set headers trong Workers/nginx. Kiểm tra với securityheaders.com.