fix: Critical 보안 이슈 4건 수정
1. SQL injection 취약점 수정 (currency 직접 삽입 제거) - SQL 쿼리에서 currency 제거, 결과 매핑에서 추가 2. 에러 메시지 정보 노출 수정 - 클라이언트에 내부 에러 상세 숨김 - 서버 로그에만 기록 3. API 키 로깅 제거 - sk-*** 형식만 표시, 실제 값 노출 안함 4. Rate limit fail-closed 정책 적용 - KV 오류 시 요청 거부 (보안 강화) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
19
src/index.ts
19
src/index.ts
@@ -635,8 +635,8 @@ async function checkRateLimit(clientIP: string, env: Env): Promise<{ allowed: bo
|
|||||||
return { allowed: true, requestId };
|
return { allowed: true, requestId };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[RateLimit] KV error:', error);
|
console.error('[RateLimit] KV error:', error);
|
||||||
// On error, allow the request (fail open)
|
// On error, deny the request (fail closed) for security
|
||||||
return { allowed: true, requestId };
|
return { allowed: false, requestId };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,10 +1077,10 @@ async function handleRecommend(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Recommend] Error:', error);
|
console.error('[Recommend] Error:', error);
|
||||||
console.error('[Recommend] Error stack:', error instanceof Error ? error.stack : 'No stack');
|
console.error('[Recommend] Error stack:', error instanceof Error ? error.stack : 'No stack');
|
||||||
|
console.error('[Recommend] Error details:', error instanceof Error ? error.message : 'Unknown error');
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
{
|
{
|
||||||
error: 'Failed to generate recommendations',
|
error: 'Failed to generate recommendations',
|
||||||
details: error instanceof Error ? error.message : 'Unknown error',
|
|
||||||
request_id: requestId,
|
request_id: requestId,
|
||||||
},
|
},
|
||||||
500,
|
500,
|
||||||
@@ -1303,7 +1303,6 @@ async function queryCandidateServers(
|
|||||||
it.gpu_count,
|
it.gpu_count,
|
||||||
it.gpu_type,
|
it.gpu_type,
|
||||||
MIN(${priceColumn}) as monthly_price,
|
MIN(${priceColumn}) as monthly_price,
|
||||||
'${currency}' as currency,
|
|
||||||
r.region_name as region_name,
|
r.region_name as region_name,
|
||||||
r.region_code as region_code,
|
r.region_code as region_code,
|
||||||
r.country_code as country_code
|
r.country_code as country_code
|
||||||
@@ -1436,8 +1435,14 @@ async function queryCandidateServers(
|
|||||||
throw new Error('Failed to query candidate servers');
|
throw new Error('Failed to query candidate servers');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate each result with type guard
|
// Add currency to each result and validate with type guard
|
||||||
const validServers = (result.results as unknown[]).filter(isValidServer);
|
const serversWithCurrency = (result.results as unknown[]).map(server => {
|
||||||
|
if (typeof server === 'object' && server !== null) {
|
||||||
|
return { ...server, currency };
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
const validServers = serversWithCurrency.filter(isValidServer);
|
||||||
const invalidCount = result.results.length - validServers.length;
|
const invalidCount = result.results.length - validServers.length;
|
||||||
if (invalidCount > 0) {
|
if (invalidCount > 0) {
|
||||||
console.warn(`[Candidates] Filtered out ${invalidCount} invalid server records`);
|
console.warn(`[Candidates] Filtered out ${invalidCount} invalid server records`);
|
||||||
@@ -1851,7 +1856,7 @@ async function getAIRecommendations(
|
|||||||
console.error('[AI] OPENAI_API_KEY has invalid format (should start with sk-)');
|
console.error('[AI] OPENAI_API_KEY has invalid format (should start with sk-)');
|
||||||
throw new Error('Invalid OPENAI_API_KEY format');
|
throw new Error('Invalid OPENAI_API_KEY format');
|
||||||
}
|
}
|
||||||
console.log('[AI] API key validated (sk-***' + apiKey.slice(-4) + ')');
|
console.log('[AI] API key validated (format: sk-***)');
|
||||||
|
|
||||||
// Build dynamic tech specs prompt from database
|
// Build dynamic tech specs prompt from database
|
||||||
const techSpecsPrompt = formatTechSpecsForPrompt(techSpecs);
|
const techSpecsPrompt = formatTechSpecsForPrompt(techSpecs);
|
||||||
|
|||||||
Reference in New Issue
Block a user