바이브코딩 보안 점검 — 배포 전에 브라우저·권한·결제를 보는 순서

바이브코딩119 · 2026-07-22 · 데이터·보안
최종 확인 2026-07-23적용 Next.js · Supabase · Stripe공식 출처 14개

배포 직전에는 로그인 한 번, 결제 한 번이 성공하면 마음이 놓입니다. 보안 문제는 반대로 실패해야 할 요청을 보내 봐야 보입니다. 아래 순서는 전문 보안 감사를 대신하지는 않지만, 공개 전에 흔한 구멍을 찾는 데 도움이 되는 실전 점검표입니다.

능동 테스트는 본인이 소유했거나 명시적으로 허가받은 스테이징에서만 하세요. 결제는 제공사의 테스트 모드를 사용합니다.

브라우저에 서버용 값이 들어갔는지 봅니다

배포된 사이트에서 개발자도구의 소스·네트워크·저장소 탭을 엽니다. Supabase publishable key처럼 공개를 전제로 한 값도 있지만, sb_secret_..., legacy service_role, Stripe sk_live_...whsec_..., 개인 키는 브라우저에 있으면 안 됩니다.

Next.js의 NEXT_PUBLIC_ 값은 process.env.NEXT_PUBLIC_NAME처럼 정적으로 참조하면 next build 시점 값으로 브라우저 번들에 인라인되고, 그 빌드를 다른 환경으로 옮겨도 값이 고정됩니다. process.env[varName] 같은 동적 참조는 자동 인라인되지 않지만, 이것을 비밀 보관 방법으로 쓰면 안 됩니다. 서버용 값에는 애초에 NEXT_PUBLIC_ 접두사를 붙이지 않고 서버 코드에서만 읽으세요.

현재 작업 폴더에 .env가 없다고 끝은 아닙니다. 소스와 생성된 번들, Git 이력, CI/CD, 배포 플랫폼의 환경변수, 로그를 함께 확인합니다. 예를 들어 아래 패턴은 출발점일 뿐이며 전용 secret scanner와 GitHub 경고도 같이 봅니다.


rg -l --hidden --glob '!.git/**' --glob '!node_modules/**' \
  '(sb_secret_|SUPABASE_SERVICE_ROLE_KEY|service_role|sk_(live|test)_|sk-proj-|gh[pousr]_|github_pat_|AKIA[0-9A-Z]{16}|whsec_|BEGIN ([A-Z ]+ )?PRIVATE KEY)' .

이 명령은 현재 체크아웃에서 일치한 파일 이름만 보여 주며 실제 값은 출력하지 않습니다. 전체 Git 이력은 값을 그대로 출력하지 않는 전용 scanner나 GitHub secret scanning으로 따로 검사하세요. 실제 secret이 노출됐다면 코드에서 가리는 것보다 폐기가 먼저입니다. 새 키를 만들고 모든 사용처를 교체한 뒤 이전 키를 끄며, 저장소 이력에 남은 값도 별도로 정리합니다. 검색 결과나 이슈에 실제 키 전체를 다시 붙여 넣지 마세요.

사용자 두 명으로 권한 경계를 넘겨 봅니다

테스트 계정 A와 B를 만들고 다음 요청을 비교하세요.

버튼을 숨기는 것은 권한 검사가 아닙니다. 서버와 데이터베이스가 매 요청에서 세션, 역할, 객체 소유권을 확인해야 합니다.

Supabase에서는 Data API 노출 여부, anon·authenticatedGRANT, 기존 RLS 정책을 함께 봅니다. RLS는 허용할 을 고를 뿐 열을 감추지 않습니다. 이메일이나 내부 메모처럼 숨길 열은 열 단위 권한, 필요한 열만 노출하는 안전한 뷰, 서버 API 중 하나로 분리하세요. 이미 있는 permissive 정책은 새 정책과 OR로 합쳐질 수 있으므로 using (true) 같은 넓은 정책이 남아 있지 않은지도 확인합니다.

CORS와 CSRF를 따로 확인합니다

CORS는 브라우저가 다른 출처의 응답을 읽는 방식을 제한할 뿐, 로그인이나 권한 검사도 아니고 요청 전송 자체를 모두 막는 장치도 아닙니다. 허용 목록과 정확히 일치하는 Origin에만 Access-Control-Allow-Origin을 반환하고, 쿠키를 받아야 할 때만 Access-Control-Allow-Credentials: true를 사용하세요. 요청 출처에 따라 응답 헤더가 달라지면 캐시가 출처별 응답을 섞지 않도록 Vary: Origin도 보냅니다.

쿠키 기반 로그인에서 상태를 바꾸는 POST·PUT·PATCH·DELETE에는 프레임워크의 CSRF 보호나 검증된 CSRF 토큰 방식을 적용하고 Origin도 확인합니다. 조회용 GET이 데이터를 바꾸게 만들지 마세요. 세션 쿠키의 Secure, HttpOnly, 적절한 SameSite는 유용하지만 보통 CSRF 토큰을 대신하지는 않습니다.

Stripe 웹훅처럼 외부 서비스가 호출하는 경로는 일반 CSRF 토큰 검사에서 제외할 수 있습니다. 대신 그 경로만 좁게 예외 처리하고 Stripe 서명 검증을 반드시 통과시킵니다.

입력은 저장할 때와 보여 줄 때 모두 봅니다

<script>라는 문자열이 저장됐다는 사실만으로 취약하다고 단정할 수는 없습니다. 서버에서 길이·형식·범위를 검증하고, 저장된 값을 HTML·URL·자바스크립트 등 출력 문맥에 맞게 인코딩해야 합니다.

스테이징에서 특수문자, 너무 긴 값, 경계값을 넣고 다른 화면에서도 실행되지 않고 텍스트로 보이는지 확인합니다. 에러 응답에 SQL, 내부 경로, 스택 트레이스가 섞이지 않는지도 함께 봅니다.

결제는 원본 웹훅부터 중복 처리까지 봅니다

브라우저가 보낸 금액과 수량은 사용자가 바꿀 수 있습니다. 서버는 신뢰할 수 있는 상품 ID를 기준으로 가격과 허용 수량을 다시 계산합니다. 화면의 성공 메시지가 아니라 결제사가 보낸 웹훅을 검증한 뒤 주문 상태를 바꾸세요.

Stripe 서명은 파싱하거나 다시 직렬화하지 않은 원본 요청 본문, Stripe-Signature 헤더, 해당 엔드포인트의 signing secret으로 검증합니다. 전역 JSON 파서가 먼저 본문을 바꾸지 않도록 웹훅 라우트 순서도 확인하세요.

서명 검증과 큐 저장처럼 필요한 최소 작업 뒤 빠르게 2xx를 반환하고, 시간이 걸리는 처리는 비동기 작업으로 넘깁니다. 웹훅은 중복될 수 있으므로 처리한 event.id를 기록하고, 별도 이벤트가 같은 객체를 가리키는 경우를 위해 event.typedata.object.id도 확인합니다. 주문·결제 갱신에는 데이터베이스 고유 제약과 트랜잭션을 둬 재실행돼도 한 번만 반영되게 만드세요. 서버가 Stripe API의 POST 요청을 재시도할 때는 동일 작업에 같은 idempotency key를 사용합니다.

테스트 모드에서 금액 변조, 잘못된 서명, 같은 이벤트 재전송, 처리 순서 변경, 작업 실패 뒤 재시도를 각각 확인합니다.

반복 요청과 비밀번호 저장을 확인합니다

가입, 로그인, 비밀번호 재설정, 인증 코드, 유료 API에는 요청 제한이 필요합니다. IP 하나만으로는 공유망과 분산 공격을 모두 다루기 어려우므로 계정·IP·기능별 한도를 설계하고, 한도를 넘겼을 때 429 응답과 안내가 나오는지 봅니다.

직접 비밀번호를 저장해야 한다면 OWASP의 우선 권고인 Argon2id를 사용합니다. 사용할 수 없는 환경에서는 scrypt를 검토하고, bcrypt는 기존 시스템 호환이 필요한 경우에 제한합니다. 평문, 복호화 가능한 암호문, 빠른 범용 해시는 비밀번호 저장에 맞지 않습니다. 관리형 인증을 쓰더라도 앱의 권한, 세션 만료, 로그인 리다이렉트 설정은 따로 검토해야 합니다.

로그는 단서만 남기고 비밀은 남기지 않습니다

브라우저 콘솔과 서버 로그에서 Authorization·Cookie 헤더, 접근·갱신 토큰, 세션 ID, 비밀번호, 데이터베이스 연결 문자열, 개인 키, 카드 정보를 찾습니다. 원문을 남기지 말고 필요한 식별자는 마스킹하거나 해시하세요. 이메일처럼 운영상 필요한 개인정보도 목적과 보관 기간, 접근 권한을 정해 최소화합니다.

반대로 인증 실패, 권한 거부, 키·권한 변경, 결제 상태 전환에는 시간, 결과, 내부 사용자·요청 ID처럼 조사에 필요한 단서를 일관되게 남깁니다. 로그 조회 권한과 보관 기간도 운영 설정에서 확인하세요.

고친 뒤 같은 실패 요청을 다시 보냅니다

노출된 secret은 지체 없이 폐기해야 하지만, RLS·GRANT·앱 코드처럼 함께 맞아야 하는 변경을 무작정 한 줄씩 운영에 넣으면 장애나 새 빈틈이 생길 수 있습니다. 스테이징에서 관련 변경을 한 묶음으로 검증하고, 짧은 배포 순서와 롤백 방법을 준비하세요.

반영 뒤에는 로그아웃 상태, 사용자 A·B, CSRF, 결제 성공·실패·중복 흐름을 다시 실행합니다. 이 목록을 통과해도 취약점이 없다는 보장은 없습니다. 개인정보나 결제를 다루는 공개 서비스라면 범위를 정한 독립 보안 검토를 추가하는 편이 좋습니다.