
- AI 코딩 시대, 보안은 자동으로 따라오지 않는다
“바이브 코딩”이 뭔데, 왜 보안이 이슈가 되나?
바이브 코딩(Vibe Coding)은 개발자가 자연어로 “이 기능 만들어줘”라고 말하면 LLM 기반 생성형 AI가 즉시 실행 가능한 코드를 만들어주는 방식으로, 회원가입/로그인/인증 같은 기능부터 웹앱 개발까지 빠르게 만들어 생산성이 커졌습니다.
핵심 문제는 “동작하는 코드 = 안전한 코드”가 아니라는 점입니다.
보안업계는 AI가 만든 코드가 문법/기능은 그럴듯해도 입력값 검증 누락, 과도한 권한 부여, 인증 우회 가능성, 예외 처리 중 내부정보 노출 같은 전통적 취약점을 포함하는 사례가 많다고 지적합니다.
핵심 요약
AI 생성 코드의 45%가 보안 테스트 실패
LLM이 생성한 코드 샘플 분석결과 전체 45%가 보안 테스트를 통과하지 못했고, OWASP 10대 취약점을 다수 내포했다고 합니다.
언어별 실패율 높음
- Java: 72% (가장 높음)
- Python: 38%
- JavaScript: 43%
- C#: 45%
XSS 방어 실패가 특히 심각
XSS에 대해 AI 생성 코드의 86%가 적절한 방어 로직을 구현하지 못했다고 나옵니다.
모델이 고도화돼도 “보안 코딩 성능”은 개선이 거의 없음
“기능적 정확성과 문법 완성도는 향상되지만, 보안 취약점을 줄이는 진전은 거의 없다”는 평가입니다.
확산 범위: 사내를 넘어 공급망으로 번짐
AI 생성 코드는 이미 사내 개발을 넘어 오픈소스/외주/서드파티 소프트웨어로 빠르게 확산 중이며, AI를 직접 쓰지 않는 기업도 공급망을 통해 AI가 작성한 취약 코드에 노출될 수 있다고 강조합니다.
대응 방향: “기본 불신 + 자동 검증 내재화”
AI 생성 코드를 기본적으로 신뢰하지 말고, SAST/DAST 포함 자동 검증을 개발 프로세스에 내재화하고, CI/CD 단계에서 보안 점검·배포 통제를 자동화해야 합니다.
왜 이런 문제가 구조적으로 생기나?
원인 A — AI의 최적화 목표가 “보안”이 아니라 “작동/완성”에 가까움
AI는 사용자가 요구한 기능을 빨리 “돌아가게” 만드는 데 강합니다. 반대로 보안은 비기능 요구사항(NFR)이라, 프롬프트/정책/검증이 없으면 빠지기 쉽습니다.
원인 B — 보안은 ‘컨텍스트(정책/권한/데이터 분류/위협모델)’가 없으면 코딩으로만 해결 불가
예: “관리자만 접근”, “개인정보 마스킹”, “내부망 전용” 같은 규칙은 조직별로 다릅니다. AI는 조직 정책을 모르니, 일반적인(그리고 종종 위험한) 기본 구현을 내놓기 쉽습니다.
원인 C — 속도 격차: 사람이 리뷰/테스트로 따라잡기 어려움
AI는 코드를 폭발적으로 생성합니다. “생성 속도를 사람이 따라잡기 어렵다 → 자동화된 점검/통제 체계 필요”로 연결합니다.
실제로 어떤 보안 사고로 이어질 수 있나? (공격 시나리오)
시나리오 1: 입력 검증 누락 → SQLi / Command Injection / SSRF
- “검색 API 만들어줘” → 쿼리 파라미터를 그대로 DB에 붙임
- “URL fetcher 만들어줘” → 내부 메타데이터(클라우드)로 SSRF 가능
시나리오 2: XSS 방어 실패 → 세션 탈취/계정 장악
- 댓글/게시판/관리자 콘솔에서 저장형 XSS 발생
- 관리자 쿠키/토큰 탈취 → 관리자 기능 악용
시나리오 3: 과도한 권한/인증 우회 → IDOR/권한상승
- “내 글 수정 API”인데 userId를 파라미터로 받아 그대로 수정
- JWT 검증을 “있으면 통과”처럼 구현
시나리오 4: 예외 처리 중 내부정보 노출
- stack trace, SQL error, 내부 경로/키 정보가 응답에 출력
- 공격자가 구조를 학습해 더 정교하게 공격
시나리오 5: 공급망 확산
- 외주/오픈소스에 AI 생성 취약 코드가 섞임
- 우리 조직은 AI 안 써도 “가져다 쓰는 순간” 노출
보안 관점 가이드
(원칙 1) AI 생성 코드는 “기본 불신(Default Deny)”
규정 문구 예시
- “AI 생성/수정 코드 및 AI 추천 패치/스니펫은 외부 코드 반입과 동일 등급으로 취급한다.”
- “보안 점검(SAST/DAST/리뷰) 통과 전까지 운영 반영 금지.”
(원칙 2) “보안 요구사항을 프롬프트에 강제 포함”
프롬프트 템플릿(예시)
- “입력값 검증(allowlist), 출력 인코딩, 인증/인가(least privilege), 비밀정보는 환경변수/Secret Manager, 에러 응답은 표준 포맷으로 내부정보 제외, 로깅은 민감정보 마스킹, OWASP Top 10 고려해서 코드를 작성해줘.”
(원칙 3) 자동 검증을 CI/CD에 “내재화(Embedded)”
사람의 선의에 맡기지 말고, 파이프라인에서 자동으로 걸러야 합니다.
점검 포인트 체크리스트
아래 항목은 AI 코드 여부와 상관없이 “특히 AI 코드에서 잘 터지는 지점” 위주입니다.
인증/인가
- 엔드포인트마다 “인증 필수/불필요”가 명확한가?
- 리소스 접근 시 소유권 검증(IDOR 방지)이 있는가?
- 관리자 기능은 권한 체크가 서버에서 강제되는가?
입력/출력 처리
- 입력값 검증이 “denylist”가 아니라 allowlist/타입 기반인가?
- HTML/JS 컨텍스트별 출력 인코딩이 있는가? (XSS)
- 파일 업로드는 확장자+MIME+매직바이트+저장경로+AV 스캔이 있는가?
비밀정보/설정
- 키/토큰이 코드/로그/에러에 노출되지 않는가?
- 기본 계정/기본 비밀번호/테스트용 백도어가 없는가?
예외/로깅
- 에러 응답에 stack trace/쿼리/내부 경로가 포함되지 않는가?
- 감사로그에 누가/언제/무엇을 했는지 남는가? (PII 마스킹 포함)
의존성/공급망
- 신규 라이브러리 추가 시 승인/취약점 스캔이 있는가?
- SBOM 생성/저장/추적이 되는가?
CI/CD에 바로 넣는 자동화 예시 (SAST/DAST/Dependency)
아래는 “개념”이 아니라 그대로 적용 가능한 뼈대 예시입니다.
GitHub Actions 예시(개념 뼈대)
name: security-gates
on: [push, pull_request]
jobs:
sast-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 1) 의존성 취약점 스캔 (예: Trivy)
- name: Trivy FS scan
uses: aquasecurity/trivy-action@0.20.0
with:
scan-type: fs
format: table
exit-code: 1
severity: CRITICAL,HIGH
# 2) SAST (예: Semgrep)
- name: Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: "p/owasp-top-ten"
dast:
runs-on: ubuntu-latest
needs: sast-deps
steps:
# 3) DAST (예: OWASP ZAP) - 테스트 URL에 대해 스캔
- name: ZAP baseline
uses: zaproxy/action-baseline@v0.12.0
with:
target: "https://staging.example.com"
fail_action: true
포인트는 “도구가 뭐냐”보다 실패 시 배포를 막는 게이트(gate)를 두는 것입니다.
AI가 자주 만드는 취약 구현 vs 안전한 구현
XSS 방지: “출력 인코딩”을 강제하기
위험 패턴(문자열을 그대로 HTML로 삽입)
res.send(`<div>${comment}</div>`);
안전 방향(템플릿 엔진의 auto-escape 사용 + DOM 조작 시 textContent)
// 서버 템플릿 엔진(예: Nunjucks/Handlebars 등) auto-escape 켜기 권장
// 프론트에서 DOM 반영 시:
element.textContent = comment; // innerHTML 금지
IDOR 방지: “userId를 파라미터로 믿지 않기”
위험 패턴
# user_id를 요청 파라미터로 받아 해당 사용자의 데이터를 수정
update_post(user_id=request.args["userId"], post_id=pid, body=body)
안전 방향(세션/JWT에서 인증된 사용자 기준 + 소유권 체크)
user_id = auth_context.user_id # 토큰에서 추출
post = get_post(pid)
if post.owner_id != user_id:
raise Forbidden()
update_post(user_id=user_id, post_id=pid, body=body)
예외 처리: “내부정보 노출 금지”
위험 패턴
except Exception as e:
return {"error": str(e)}, 500
안전 방향(표준 에러코드 + 내부 로그에만 상세)
except Exception:
logger.exception("Unhandled error") # 내부 로그에만 stacktrace
return {"error": "INTERNAL_ERROR"}, 500
현실적인 단계
🟢 1단계: 가이드라인 선포
- AI 생성 코드는 “외부 반입 코드” 등급
- 운영 반영은 보안 게이트 통과 필수
🟡 2단계: 파이프라인 게이트
- Dependency Scan + SAST 최소 세트
- PR 머지 조건: High/Critical 0개(또는 예외 승인)
🟠 3단계: DAST + 시크릿/토큰 누출 탐지
- 스테이징 자동 스캔
- Git leaks 탐지(예: gitleaks) 추가
🔴 4단계: 공급망(SBOM) + 정책 기반 승인
- SBOM 생성/보관
- 외주/서드파티 납품물에도 동일 기준 적용
실무용 템플릿 구성
- “AI 코드 사용 보안 가이드(사내 정책 문서)”
- PR 리뷰 체크리스트(권한/입력/에러/로그/시크릿/의존성)
- CI/CD 보안 게이트 표준안(도구 후보 + 실패 기준 + 예외 승인 절차)
댓글