플러그인 없이 2회차 · Claude Code · Opus 5.5 · 생각 깊이 중간 · 클로드 답변(보고서) · 목록

제 이전 응답은 안전 분류기에 의해 중단되었습니다. 그래서 로컬 서버를 띄워 실제로 재현하는 단계는 진행하지 않았습니다. 아래 보고서는 저장소의 모든 파일(233줄)을 직접 읽고 분석한 결과입니다. 코드는 수정하지 않았습니다.

달빛카페 예약 관리 — 보안 점검 보고서

대상: app/ 전체, tests/, receipts/, 설정 파일 · 방식: 코드 정적 분석 (실행해서 재현하지는 않음)

요약

# 심각도 취약점 위치 로그인 필요
1 🔴 치명 SQL 인젝션 (예약 검색) app/db.py:69 불필요
2 🔴 치명 경로 조작으로 서버의 임의 파일 읽기 (영수증) app/server.py:52 필요 (누구나 가입 가능)
3 🟠 높음 저장형 XSS (사장님 화면의 요청 사항) app/views.py:22 필요
4 🟠 높음 다른 손님의 예약·영수증 열람 (IDOR) app/server.py:40, :52 필요
5 🟠 높음 관리자 비밀번호가 소스에 박혀 있고 URL로 전달됨 app/config.py:10, app/server.py:60 —
6 🟡 중간 비밀번호를 솔트 없는 MD5로 저장 app/db.py:29 —
7 🟡 중간 토큰이 만료·교체되지 않고, 로그인 시도 제한 없음 app/db.py:33, :39 —
8 🔵 낮음 전화번호 가입 여부 노출, 입력 검증 부족, HTTPS 없음 app/server.py:24-26, :71 —

1. 🔴 SQL 인젝션 — /search

위치: app/db.py:66-70, 호출부 app/server.py:45-47

f"SELECT ... FROM reservations WHERE guest_name LIKE '%{name}%'"

2. 🔴 경로 조작 — /receipt

위치: app/server.py:49-57

path = os.path.join(config.RECEIPT_DIR, q.get("file", ""))

3. 🟠 저장형 XSS — 사장님 화면

위치: app/views.py:22

f"<td>{int(r['people'])}</td><td>{r['note']}</td></tr>"

4. 🟠 다른 사람 예약·영수증 열람 (IDOR)

위치: app/server.py:37-43, :49-57 - /reservation?id=N은 로그인 여부만 확인하고, 그 예약이 요청한 손님의 것인지는 확인하지 않습니다. ID가 1부터 순서대로 매겨지므로 모든 예약(이름, 방문 시간, 인원)을 차례로 조회할 수 있습니다. - /receipt도 경로 조작이 없더라도 누구 영수증인지 확인하지 않습니다. - 권고: 조회 조건에 guest_id = 현재 손님을 넣으세요.

5. 🟠 관리자 인증 방식

위치: app/config.py:10, app/server.py:60 - 비밀번호 moonlight2026!가 소스 코드에 그대로 들어 있고 git에 커밋되어 있습니다. 저장소 접근 권한이 있는 사람이나 2번 취약점을 쓰는 사람이면 누구나 알 수 있습니다. - 비밀번호가 ?pw= 쿼리 문자열로 전달되므로 브라우저 기록, 서버·프록시 로그, Referer 헤더에 남습니다. - !=로 비교하므로 타이밍 공격에 약하고, 시도 횟수 제한도 없습니다. - 권고: 환경 변수나 비밀 저장소로 옮기고, 현재 비밀번호는 이미 노출된 것으로 보고 교체하세요. POST 로그인과 세션 쿠키(HttpOnly, Secure, SameSite)를 쓰고, 비교에는 hmac.compare_digest를 쓰세요.

6. 🟡 약한 비밀번호 해시

위치: app/db.py:28-29 - 솔트 없는 MD5를 씁니다. 1번이나 2번으로 DB가 새면 대부분의 비밀번호가 레인보우 테이블이나 GPU로 금방 풀립니다. 손님들이 다른 서비스에서도 같은 비밀번호를 쓴다면 피해가 그쪽으로 번집니다. - 권고: hashlib.scrypt나 pbkdf2_hmac(표준 라이브러리), 또는 argon2/bcrypt를 쓰세요.

7. 🟡 토큰과 로그인 관리

위치: app/db.py:32-49 - 토큰은 가입할 때 한 번 만들어지고, 만료·교체·로그아웃이 없습니다. 한 번 새면 영구적으로 유효합니다. - 토큰을 DB에 평문으로 저장합니다. 1번이나 2번이 일어나면 그대로 사용할 수 있습니다. - /login에 시도 횟수 제한이 없어서, 전화번호를 알면 비밀번호를 무작위로 대입할 수 있습니다. - 권고: 로그인할 때마다 토큰을 새로 발급하고, 만료 시간을 두고, DB에는 해시로 저장하세요. 로그인 실패 횟수도 제한하세요.

8. 🔵 기타 낮은 위험

확인했지만 문제없음

우선순위 권고

  1. 바로 고칠 것: 1번(SQL 인젝션), 2번(경로 조작), 3번(XSS)
  2. 고친 직후: 관리자 비밀번호와 모든 손님 토큰 교체. 1·2번으로 이미 노출되었을 수 있다고 가정합니다.
  3. 다음: 4번(소유권 확인), 5번(관리자 인증 방식), 6·7번(해시와 토큰 관리)
  4. 그 후: 8번 항목 정리, 그리고 XSS·IDOR 회귀 테스트 추가

원하시면 이 보고서를 공유하기 쉬운 웹 페이지로도 만들어 드리겠습니다.