fix: security hardening round 2
- CORS: origin=null now rejected (was: allowed as same-origin) - CORS: regex tightened to [\w-]+ to prevent subdomain bypass - CORS: add *.posimai.soar-enrich.com and posimai.soar-enrich.com explicitly - Stripe webhook: fix regex capture groups + add uppercase hex support Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0590d0995d
commit
ac8cc6db81
11
server.js
11
server.js
|
|
@ -116,9 +116,11 @@ const extraOrigins = (process.env.ALLOWED_ORIGINS || '')
|
||||||
.split(',').map(o => o.trim()).filter(Boolean);
|
.split(',').map(o => o.trim()).filter(Boolean);
|
||||||
|
|
||||||
function isAllowedOrigin(origin) {
|
function isAllowedOrigin(origin) {
|
||||||
if (!origin) return true; // 同一オリジン
|
if (!origin) return false; // origin なしは拒否(CSRF 対策)
|
||||||
if (process.env.NODE_ENV !== 'production' && /^http:\/\/localhost(:\d+)?$/.test(origin)) return true; // localhost 開発のみ
|
if (process.env.NODE_ENV !== 'production' && /^http:\/\/localhost(:\d+)?$/.test(origin)) return true; // localhost 開発のみ
|
||||||
if (/^https:\/\/posimai-[^.]+\.vercel\.app$/.test(origin)) return true; // 全 Posimai アプリ
|
if (/^https:\/\/posimai-[\w-]+\.vercel\.app$/.test(origin)) return true; // 全 Posimai アプリ(英数字・ハイフンのみ)
|
||||||
|
if (/^https:\/\/[\w-]+\.posimai\.soar-enrich\.com$/.test(origin)) return true; // 独自ドメイン配下
|
||||||
|
if (origin === 'https://posimai.soar-enrich.com') return true; // Dashboard
|
||||||
if (extraOrigins.includes(origin)) return true; // 追加許可
|
if (extraOrigins.includes(origin)) return true; // 追加許可
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2726,8 +2728,9 @@ async function handleStripeWebhook(req, res) {
|
||||||
try {
|
try {
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const rawBody = req.body; // Buffer
|
const rawBody = req.body; // Buffer
|
||||||
const [, tsStr, , v1Sig] = sig.match(/t=(\d+),.*v1=([a-f0-9]+)/) || [];
|
const match = sig.match(/t=(\d+).*?,.*?v1=([a-fA-F0-9]+)/);
|
||||||
if (!tsStr || !v1Sig) throw new Error('Invalid signature format');
|
if (!match || match.length < 3) throw new Error('Invalid signature format');
|
||||||
|
const [, tsStr, v1Sig] = match;
|
||||||
const tolerance = 300; // 5分
|
const tolerance = 300; // 5分
|
||||||
if (Math.abs(Date.now() / 1000 - parseInt(tsStr)) > tolerance) {
|
if (Math.abs(Date.now() / 1000 - parseInt(tsStr)) > tolerance) {
|
||||||
throw new Error('Timestamp too old');
|
throw new Error('Timestamp too old');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue