fix: critical security improvements

- Apply optimistic locking to deposit-matcher.ts (race condition fix)
- Add timing-safe comparison for API key validation
- Move admin IDs from wrangler.toml vars to secrets
- Add .env.example for secure credential management

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-21 17:18:21 +09:00
parent 8edab3069f
commit 91f50ddc12
6 changed files with 82 additions and 37 deletions

View File

@@ -23,27 +23,35 @@ function isValidTelegramIP(ip: string): boolean {
return TELEGRAM_IP_RANGES.some(range => ipInCIDR(ip, range));
}
// Webhook Secret Token 검증 (Timing-safe comparison)
function isValidSecretToken(request: Request, expectedSecret: string): boolean {
const secretHeader = request.headers.get('X-Telegram-Bot-Api-Secret-Token');
if (!secretHeader || !expectedSecret) {
/**
* Timing-safe string comparison to prevent timing attacks
* @param a - First string
* @param b - Second string
* @returns true if strings are equal
*/
export function timingSafeEqual(a: string | null | undefined, b: string | null | undefined): boolean {
if (!a || !b) {
return false;
}
// Timing-safe comparison
if (secretHeader.length !== expectedSecret.length) {
if (a.length !== b.length) {
return false;
}
let result = 0;
for (let i = 0; i < secretHeader.length; i++) {
result |= secretHeader.charCodeAt(i) ^ expectedSecret.charCodeAt(i);
for (let i = 0; i < a.length; i++) {
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return result === 0;
}
// Webhook Secret Token 검증 (Timing-safe comparison)
function isValidSecretToken(request: Request, expectedSecret: string): boolean {
const secretHeader = request.headers.get('X-Telegram-Bot-Api-Secret-Token');
return timingSafeEqual(secretHeader, expectedSecret);
}
// 요청 본문 검증
function isValidRequestBody(body: unknown): body is TelegramUpdate {
return (