1. 프런트엔드 JavaScript에 박힌 API 키
이것은 가장 피해가 크면서도 가장 저지르기 쉬운 실수입니다. AI 도구에 ‘Stripe 결제 추가’나 ‘OpenAI 연결’을 요청하면, 시크릿 키를 클라이언트 컴포넌트에 곧바로 연결합니다. 앱은 작동합니다 — 그리고 이제 시크릿 키가 모든 방문자의 브라우저로 전달되어, DevTools를 열거나 JS 번들을 grep하는 누구나 읽을 수 있습니다.
NEXT_PUBLIC_(또는 VITE_, REACT_APP_) 접두사가 붙은 것은 무엇이든 빌드 시점에 클라이언트 번들에 포함됩니다. 시크릿 키 — Stripe sk_live_, OpenAI sk-, 데이터베이스 URL, service-role 키 — 는 오직 서버 측 코드(API route, 서버 컴포넌트, edge function)에서만 읽어야 합니다.
빠른 확인: 배포된 사이트를 열고, 페이지 소스와 번들된 JS를 보고, sk_live, sk-, 그리고 .env에 있는 키의 첫 글자들을 검색하세요. 하나라도 나타나면 먼저 키를 교체한 뒤 호출을 서버 측으로 옮기세요.
2. 웹 루트에서 곧바로 제공되는 .env 또는 .git
놀라울 만큼 많은 배포된 앱이 요청만 하면 .env 파일 — 또는 .git 디렉터리 전체 — 을 순순히 반환합니다. 잘못 설정된 정적 파일 서버 하나, 또는 모든 것을 공개 폴더로 복사하는 프레임워크 템플릿 하나면 충분합니다.
영향은 총체적입니다: 데이터베이스 자격 증명, 인증 시크릿, API 키가 모두 한 파일에 있습니다. 노출된 .git 디렉터리도 거의 마찬가지로 나쁩니다. 공격자가 커밋되었다가 나중에 ‘제거된’ 시크릿을 포함한 전체 소스 히스토리를 재구성할 수 있기 때문입니다.
빠른 확인은 10초면 됩니다:
curl -i https://yourapp.com/.env
curl -i https://yourapp.com/.git/config
# Both should return 404 (or 403) — never 200.노출된 .env 파일: 전체 가이드3. Content-Security-Policy 없음
AI 도구는 보안 헤더를 거의 추가하지 않으며, CSP는 그중 가장 중요합니다. 이것이 없으면 앱 어디든 HTML 주입이나 XSS 버그 하나로 공격자가 사용자의 브라우저에서 임의의 JavaScript를 실행할 수 있습니다 — 세션 탈취와 데이터 유출.
대부분의 앱에 좋은 시작 정책: default-src 'self', 객체와 프레이밍 차단, 그리고 실제로 사용하는 서드파티 스크립트 origin만 허용. 엄격하게 시작하고 의도적으로 완화하세요 — 절대 그 반대가 아닙니다.
// next.config.js
const csp = [
"default-src 'self'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data:",
"base-uri 'none'",
"frame-ancestors 'none'",
"object-src 'none'",
].join("; ");
module.exports = {
async headers() {
return [{ source: "/:path*", headers: [{ key: "Content-Security-Policy", value: csp }] }];
},
};CSP 설명: 지시문, nonce, 흔한 실수4. Secure, HttpOnly, SameSite 없는 세션 쿠키
인증 라이브러리가 AI 도구에 의해 기본 옵션으로 연결되었다면, 세션 쿠키 플래그를 확인할 가치가 있습니다. HttpOnly 없는 쿠키는 주입된 스크립트가 읽을 수 있고(작은 XSS를 전체 계정 탈취로 바꿈), Secure 없는 쿠키는 일반 HTTP로 유출될 수 있으며, SameSite 없는 쿠키는 교차 사이트 요청 위조를 훨씬 쉽게 만듭니다.
배포된 앱에서 DevTools → Application → Cookies를 여세요. 모든 세션 또는 인증 쿠키에 셋 다 표시되어야 합니다: Secure, HttpOnly, SameSite=Lax(또는 Strict).
5. HTTPS가 되지 않는 HTTP(그리고 HSTS 없음)
대부분의 호스팅 플랫폼은 HTTPS를 자동으로 제공합니다 — 하지만 여전히 많은 앱이 리디렉션 없이 일반 HTTP로 순순히 응답하거나, 리디렉션은 하지만 Strict-Transport-Security 헤더를 절대 보내지 않습니다. 어느 쪽이든, 주소창에 도메인을 입력하는 사용자는 첫 요청을 암호화되지 않은 채로 보내고, 그 요청은 리디렉션이 일어나기 전에 가로채이거나 다운그레이드될 수 있습니다.
해결책은 설정 두 줄입니다: HTTP에서 HTTPS로의 영구 301 리디렉션, 그리고 브라우저가 다시는 다운그레이드하기를 거부하도록 하는 HSTS 헤더(max-age=31536000; includeSubDomains).
6. 모든 것을 허용하도록 설정된 CORS
AI로 생성한 프런트엔드가 개발 중에 자신의 API에 도달하지 못하면, 생성되는 ‘해결책’은 거의 항상 Access-Control-Allow-Origin: * — 때로는 자격 증명도 허용 — 입니다. 이는 인터넷의 모든 웹사이트에게 방문자의 브라우저에서 여러분의 API를 호출해도 된다고 알리는 것입니다.
자신의 프런트엔드에 서비스하는 API라면, 허용 origin은 정확히 여러분 자신의 도메인이어야 하며 그 외에는 없어야 합니다. 와일드카드 CORS는 진정으로 공개적이고 인증이 없으며 읽기 전용인 API에만 허용됩니다.
7. 도메인에 SPF나 DMARC 없음
이것은 앱 코드와는 전혀 관계가 없습니다 — 도메인에 관한 것입니다. SPF와 DMARC DNS 레코드가 없으면 누구나 [email protected]에서 온 것처럼 주장하는 이메일을 보낼 수 있고, 수신 메일 서버는 그것이 위조된 것인지 알 방법이 없습니다. 이는 여러분의 브랜드를 걸친, 여러분 자신의 사용자를 겨냥한 피싱입니다.
DNS TXT 레코드 두 개가 이를 해결합니다. Resend, Postmark, SES 같은 제공업체로 이메일을 보낸다면 그들의 문서가 정확한 SPF 값을 알려 줍니다. 그 옆에 DMARC 정책을 추가하고, 정상 메일이 통과하는 것을 확인한 뒤 p=none에서 p=quarantine으로 강화하세요.
일곱 가지를 한 번에 확인하기
이 목록의 모든 항목은 외부에서 감지할 수 있으며, 이는 손으로 하나씩 처리할 필요가 없다는 뜻입니다. AppSafe는 이 모든 점검 — 여기에 SSL/TLS, 서브도메인 탈취, source map 등 — 을 한 번의 스캔으로 실행하고, 결과를 A~F로 등급 매기고, 각 발견 항목마다 Cursor나 Claude Code에 그대로 붙여넣을 수 있는 AI 수정 프롬프트를 드립니다.
홈페이지의 빠른 스캔은 가입이 필요 없습니다. 전체 스캔(노출된 시크릿 점검 포함)은 소유권을 인증한 도메인에서만 실행됩니다 — 다른 사람의 JS 번들을 키를 찾으려 grep하지 않기 위한 의도적 설계입니다.