安全头部配置
概述
实施全面的HTTP安全头部以保护Web应用程序免受XSS、点击劫持、MIME嗅探和其他基于浏览器的攻击。
何时使用
- 新Web应用程序部署
- 安全审计补救
- 合规性要求
- 浏览器安全硬化
- API安全
- 静态站点保护
实施示例
1. Node.js/Express安全头部
// security-headers.js
const helmet = require('helmet');
function configureSecurityHeaders(app) {
// 全面Helmet配置
app.use(helmet({
// 内容安全策略
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"'unsafe-inline'", // 生产中移除
"https://cdn.example.com",
"https://www.google-analytics.com"
],
styleSrc: [
"'self'",
"'unsafe-inline'",
"https://fonts.googleapis.com"
],
fontSrc: [
"'self'",
"https://fonts.gstatic.com"
],
imgSrc: [
"'self'",
"data:",
"https:",
"blob:"
],
connectSrc: [
"'self'",
"https://api.example.com"
],
frameSrc: ["'none'"],
objectSrc: ["'none'"],
upgradeInsecureRequests: []
}
},
// 严格传输安全
hsts: {
maxAge: 31536000, // 1年
includeSubDomains: true,
preload: true
},
// X-Frame-Options
frameguard: {
action: 'deny'
},
// X-Content-Type-Options
noSniff: true,
// X-XSS-Protection
xssFilter: true,
// Referrer-Policy
referrerPolicy: {
policy: 'strict-origin-when-cross-origin'
},
// Permissions-Policy (原Feature-Policy)
permittedCrossDomainPolicies: {
permittedPolicies: 'none'
}
}));
// 额外自定义头部
app.use((req, res, next) => {
// Permissions Policy
res.setHeader(
'Permissions-Policy',
'geolocation=(), microphone=(), camera=(), payment=(), usb=()'
);
// Expect-CT
res.setHeader(
'Expect-CT',
'max-age=86400, enforce'
);
// 跨源策略
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
// 移除powered-by头部
res.removeHeader('X-Powered-By');
next();
});
}
// CSP违规报告器
app.post('/api/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
const report = req.body['csp-report'];
console.error('CSP Violation:', {
documentUri: report['document-uri'],
violatedDirective: report['violated-directive'],
blockedUri: report['blocked-uri'],
sourceFile: report['source-file'],
lineNumber: report['line-number']
});
// 存储在数据库或发送到监控服务
// monitoringService.logCSPViolation(report);
res.status(204).end();
});
module.exports = { configureSecurityHeaders };
2. Nginx安全头部配置
# nginx-security-headers.conf
server {
listen 443 ssl http2;
server_name example.com;
# SSL配置
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# 安全头部
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# 内容安全策略
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; report-uri /api/csp-report" always;
# 跨源策略
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
# Expect-CT
add_header Expect-CT "max-age=86400, enforce" always;
# 隐藏服务器版本
server_tokens off;
location / {
root /var/www/html;
index index.html;
}
# API端点
location /api/ {
proxy_pass http://backend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 移除后端头部可能会泄露信息
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
}
}
# 重定向HTTP到HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
3. Python Flask安全头部
# security_headers.py
from flask import Flask, make_response
from functools import wraps
app = Flask(__name__)
def add_security_headers(f):
@wraps(f)
def decorated_function(*args, **kwargs):
resp = make_response(f(*args, **kwargs))
# 严格传输安全
resp.headers['Strict-Transport-Security'] = \
'max-age=31536000; includeSubDomains; preload'
# X-Frame-Options
resp.headers['X-Frame-Options'] = 'DENY'
# X-Content-Type-Options
resp.headers['X-Content-Type-Options'] = 'nosniff'
# X-XSS-Protection
resp.headers['X-XSS-Protection'] = '1; mode=block'
# Referrer-Policy
resp.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
# Permissions-Policy
resp.headers['Permissions-Policy'] = \
'geolocation=(), microphone=(), camera=(), payment=()'
# 内容安全策略
csp = {
"default-src": ["'self'"],
"script-src": ["'self'", "https://cdn.example.com"],
"style-src": ["'self'", "'unsafe-inline'"],
"img-src": ["'self'", "data:", "https:"],
"font-src": ["'self'"],
"connect-src": ["'self'", "https://api.example.com"],
"frame-ancestors": ["'none'"],
"base-uri": ["'self'"],
"form-action": ["'self'"],
"report-uri": ["/api/csp-report"]
}
csp_string = "; ".join([
f"{key} {' '.join(values)}"
for key, values in csp.items()
])
resp.headers['Content-Security-Policy'] = csp_string
# 跨源策略
resp.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'
resp.headers['Cross-Origin-Opener-Policy'] = 'same-origin'
resp.headers['Cross-Origin-Resource-Policy'] = 'same-origin'
# Expect-CT
resp.headers['Expect-CT'] = 'max-age=86400, enforce'
# 移除服务器头部
resp.headers.pop('Server', None)
return resp
return decorated_function
# 应用于所有路由
@app.after_request
def apply_security_headers(response):
# 同上头部
response.headers['Strict-Transport-Security'] = \
'max-age=31536000; includeSubDomains; preload'
response.headers['X-Frame-Options'] = 'DENY'
response.headers['X-Content-Type-Options'] = 'nosniff'
response.headers['X-XSS-Protection'] = '1; mode=block'
return response
# CSP违规端点
@app.route('/api/csp-report', methods=['POST'])
def csp_report():
report = request.get_json()
print(f"CSP Violation: {report}")
# 记录到监控服务
# monitoring.log_csp_violation(report)
return '', 204
if __name__ == '__main__':
# 仅支持HTTPS运行
app.run(ssl_context='adhoc', port=443)
4. Apache .htaccess配置
# .htaccess - Apache安全头部
# 严格传输安全
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# X-Frame-Options
Header always set X-Frame-Options "DENY"
# X-Content-Type-Options
Header always set X-Content-Type-Options "nosniff"
# X-XSS-Protection
Header always set X-XSS-Protection "1; mode=block"
# Referrer-Policy
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Permissions-Policy
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# 内容安全策略
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none'"
# 跨源策略
Header always set Cross-Origin-Embedder-Policy "require-corp"
Header always set Cross-Origin-Opener-Policy "same-origin"
Header always set Cross-Origin-Resource-Policy "same-origin"
# 移除服务器签名
ServerSignature Off
Header unset Server
Header unset X-Powered-By
# 强制HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
5. 安全头部测试脚本
// test-security-headers.js
const axios = require('axios');
async function testSecurityHeaders(url) {
console.log(`
=== Testing Security Headers for ${url} ===
`);
try {
const response = await axios.get(url, {
validateStatus: () => true
});
const headers = response.headers;
const tests = {
'Strict-Transport-Security': {
present: !!headers['strict-transport-security'],
value: headers['strict-transport-security'],
recommended: 'max-age=31536000; includeSubDomains; preload'
},
'X-Frame-Options': {
present: !!headers['x-frame-options'],
value: headers['x-frame-options'],
recommended: 'DENY or SAMEORIGIN'
},
'X-Content-Type-Options': {
present: !!headers['x-content-type-options'],
value: headers['x-content-type-options'],
recommended: 'nosniff'
},
'X-XSS-Protection': {
present: !!headers['x-xss-protection'],
value: headers['x-xss-protection'],
recommended: '1; mode=block'
},
'Content-Security-Policy': {
present: !!headers['content-security-policy'],
value: headers['content-security-policy'],
recommended: 'Define strict CSP'
},
'Referrer-Policy': {
present: !!headers['referrer-policy'],
value: headers['referrer-policy'],
recommended: 'strict-origin-when-cross-origin'
},
'Permissions-Policy': {
present: !!headers['permissions-policy'],
value: headers['permissions-policy'],
recommended: 'Restrict dangerous features'
}
};
let passed = 0;
let failed = 0;
for (const [header, test] of Object.entries(tests)) {
if (test.present) {
console.log(`✓ ${header}: ${test.value}`);
passed++;
} else {
console.log(`✗ ${header}: MISSING`);
console.log(` Recommended: ${test.recommended}`);
failed++;
}
}
console.log(`
=== Summary ===`);
console.log(`Passed: ${passed}/${Object.keys(tests).length}`);
console.log(`Failed: ${failed}/${Object.keys(tests).length}`);
const score = (passed / Object.keys(tests).length) * 100;
console.log(`Security Score: ${score.toFixed(0)}%`);
} catch (error) {
console.error('Error testing headers:', error.message);
}
}
// 使用方法
testSecurityHeaders('https://example.com');
最佳实践
✅ DO
- 处处使用HTTPS
- 实施严格的CSP
- 启用带有预加载的HSTS
- 使用X-Frame-Options阻止框架
- 防止MIME嗅探
- 报告CSP违规
- 定期测试头部
- 使用安全扫描器
❌ DON’T
- 允许CSP中的unsafe-inline
- 在子域上跳过HSTS
- 忽略CSP违规
- 使用过于宽松的政策
- 忘记测试更改
安全头部清单
- [ ] Strict-Transport-Security
- [ ] Content-Security-Policy
- [ ] X-Frame-Policy
- [ ] X-Content-Type-Options
- [ ] X-XSS-Protection
- [ ] Referrer-Policy
- [ ] Permissions-Policy
- [ ] 跨源策略
- [ ] Expect-CT
- [ ] 移除服务器签名