🐛 fix: 修复通知 401 错误和 CSP 违规

- middleware withAuth 新增 requireAuth: false 跳过密钥校验
- CSP style-src 添加 fonts.googleapis.com (修复字体加载拦截)
- CSP font-src 添加 fonts.gstatic.com (修复字体资源拦截)
- CSP connect-src 添加 cdn.jsdelivr.net (修复 source map 加载)
- server.js CSP 同步更新与 netlify.toml 保持一致
This commit is contained in:
Abner
2026-04-25 16:46:50 +08:00
parent 9cfa35adb3
commit cc49daa993
3 changed files with 34 additions and 7 deletions

View File

@@ -144,7 +144,7 @@
Referrer-Policy = "strict-origin-when-cross-origin"
# 生产 CSP移除 unsafe-inline基于 nonce/hash 的策略(页面模板需注入 nonce
# 如暂无模板注入机制,可先允许外链受信源并避免内联脚本
Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.googletagmanager.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com https://browser.sentry-cdn.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self' https://qrcode.show https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com https://*.sentry.io; font-src 'self' data: https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; manifest-src 'self';"
Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.googletagmanager.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com https://browser.sentry-cdn.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.googleapis.com; img-src 'self' data: https:; connect-src 'self' https://qrcode.show https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com https://*.sentry.io https://cdn.jsdelivr.net; font-src 'self' data: https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.gstatic.com; manifest-src 'self';"
[[headers]]
for = "/manifest.webmanifest"

View File

@@ -231,10 +231,37 @@ function withAuth(handler, options = {}) {
const functionName = context.functionName || 'unknown';
try {
// 鉴权
const auth = authenticate(event);
// 鉴权requireAuth: false 时跳过密钥校验,仅保留 CORS 检查)
let auth;
if (options.requireAuth === false) {
const lower = Object.fromEntries(
Object.entries(event.headers || {}).map(([k, v]) => [k.toLowerCase(), v])
);
const requestOrigin = lower.origin;
// 预检请求直接返回
// CORS 预检请求
if (event.httpMethod === 'OPTIONS') {
if (requestOrigin && requestOrigin !== ALLOWED_ORIGIN) {
throw new AuthError('Origin not allowed', 403);
}
return {
statusCode: 200,
headers: createHeaders(requestOrigin),
body: ''
};
}
// 非预检请求:仅验证来源
if (requestOrigin && requestOrigin !== ALLOWED_ORIGIN) {
throw new AuthError('Origin not allowed', 403);
}
auth = { authorized: false, origin: requestOrigin, public: true };
} else {
auth = authenticate(event);
}
// 预检请求直接返回(仅 requireAuth=true 分支到达此处)
if (auth.preflight) {
return {
statusCode: 200,

View File

@@ -40,10 +40,10 @@ app.use(helmet({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://fonts.googleapis.com"],
imgSrc: ["'self'", "data:", "https:", "http:"],
connectSrc: ["'self'", "https://qrcode.show", "https://api.qrserver.com", "https://appapi.simyo.nl", "https://api.giffgaff.com", "https://id.giffgaff.com", "https://publicapi.giffgaff.com"],
fontSrc: ["'self'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com"]
connectSrc: ["'self'", "https://qrcode.show", "https://api.qrserver.com", "https://appapi.simyo.nl", "https://api.giffgaff.com", "https://id.giffgaff.com", "https://publicapi.giffgaff.com", "https://cdn.jsdelivr.net", "https://*.sentry.io"],
fontSrc: ["'self'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://fonts.gstatic.com"]
}
}
}));