fix: deduplicate proxied security headers

This commit is contained in:
237899745
2026-07-27 13:45:22 +08:00
parent 6b7b79c7cb
commit f760aa0ae9
4 changed files with 87 additions and 1 deletions

View File

@@ -78,12 +78,17 @@ test('前端 package.json 与 lock 文件依赖同步且没有手工缓存版本
test('生产部署统一使用 Vite dist 且不会删除 lock 文件', () => {
const installer = fs.readFileSync(path.join(projectRoot, 'install.sh'), 'utf8');
const nginxConfig = fs.readFileSync(path.join(projectRoot, 'nginx', 'nginx.conf'), 'utf8');
const nginxExample = fs.readFileSync(path.join(projectRoot, 'nginx', 'nginx.conf.example'), 'utf8');
assert.ok(installer.includes('npm ci --include=dev'));
assert.ok(installer.includes('npm run build'));
assert.ok(installer.includes('frontend/dist'));
assert.strictEqual(/rm\s+-rf\s+node_modules\s+package-lock\.json/.test(installer), false);
assert.ok(nginxConfig.includes('root /usr/share/nginx/html;'));
assert.ok(nginxConfig.includes('alias /runtime/downloads/;'));
for (const config of [installer, nginxConfig, nginxExample]) {
assert.ok(config.includes('proxy_hide_header X-Request-ID;'));
assert.ok(config.includes('proxy_hide_header Content-Security-Policy;'));
}
});
test('Docker 构建上下文排除依赖、环境配置和运行时数据', () => {

View File

@@ -2407,6 +2407,16 @@ server {
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
# 对外安全头由 Nginx 统一输出,避免与后端重复
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header Referrer-Policy;
proxy_hide_header Permissions-Policy;
proxy_hide_header Strict-Transport-Security;
proxy_hide_header X-Request-ID;
proxy_hide_header Content-Security-Policy;
# 隐藏Nginx版本号
server_tokens off;
@@ -2707,6 +2717,16 @@ server {
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
# 对外安全头由 Nginx 统一输出,避免与后端重复
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header Referrer-Policy;
proxy_hide_header Permissions-Policy;
proxy_hide_header Strict-Transport-Security;
proxy_hide_header X-Request-ID;
proxy_hide_header Content-Security-Policy;
# 隐藏Nginx版本号
server_tokens off;
@@ -2857,6 +2877,16 @@ server {
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 对外安全头由 Nginx 统一输出,避免与后端重复
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header Referrer-Policy;
proxy_hide_header Permissions-Policy;
proxy_hide_header Strict-Transport-Security;
proxy_hide_header X-Request-ID;
proxy_hide_header Content-Security-Policy;
# 隐藏Nginx版本号
server_tokens off;
@@ -3621,6 +3651,35 @@ update_migrate_nginx_config() {
mv "$secured_file" "$config_file"
fi
if ! grep -q "proxy_hide_header X-Request-ID" "$config_file"; then
local proxy_headers_file="${config_file}.proxy-headers.$$"
if ! awk '
/add_header Content-Security-Policy/ && !inserted_proxy_headers {
print
print ""
print " # 对外安全头由 Nginx 统一输出,避免与后端重复"
print " proxy_hide_header X-Frame-Options;"
print " proxy_hide_header X-Content-Type-Options;"
print " proxy_hide_header X-XSS-Protection;"
print " proxy_hide_header Referrer-Policy;"
print " proxy_hide_header Permissions-Policy;"
print " proxy_hide_header Strict-Transport-Security;"
print " proxy_hide_header X-Request-ID;"
print " proxy_hide_header Content-Security-Policy;"
inserted_proxy_headers = 1
next
}
{ print }
END { if (!inserted_proxy_headers) exit 2 }
' "$config_file" > "$proxy_headers_file"; then
rm -f "$proxy_headers_file"
print_error "无法为 Nginx 配置添加上游响应头去重: $config_file"
cp -a "$backup_dir/$backup_name" "$config_file"
return 1
fi
mv "$proxy_headers_file" "$config_file"
fi
if ! grep -q "location /downloads/" "$config_file"; then
local migrated_file="${config_file}.migrate.$$"
awk -v project_dir="$PROJECT_DIR" '
@@ -3638,7 +3697,9 @@ update_migrate_nginx_config() {
mv "$migrated_file" "$config_file"
fi
if ! grep -q "root ${PROJECT_DIR}/frontend/dist;" "$config_file" || ! grep -q "location /downloads/" "$config_file"; then
if ! grep -q "root ${PROJECT_DIR}/frontend/dist;" "$config_file" || \
! grep -q "location /downloads/" "$config_file" || \
! grep -q "proxy_hide_header X-Request-ID" "$config_file"; then
print_error "无法安全迁移 Nginx 配置: $config_file"
cp -a "$backup_dir/$backup_name" "$config_file"
return 1

View File

@@ -13,6 +13,16 @@ server {
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
# 对外安全头由 Nginx 统一输出,避免与后端重复
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header Referrer-Policy;
proxy_hide_header Permissions-Policy;
proxy_hide_header Strict-Transport-Security;
proxy_hide_header X-Request-ID;
proxy_hide_header Content-Security-Policy;
# 隐藏Nginx版本
server_tokens off;

View File

@@ -52,6 +52,16 @@ server {
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
# 对外安全头由 Nginx 统一输出,避免与后端重复
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header Referrer-Policy;
proxy_hide_header Permissions-Policy;
proxy_hide_header Strict-Transport-Security;
proxy_hide_header X-Request-ID;
proxy_hide_header Content-Security-Policy;
# 隐藏 Nginx 版本
server_tokens off;