☰ 분류

코드 보안 점검받는 프롬프트

외부 입력이 들어오는 경로를 따라가며 확인합니다. 발견 못 한 영역도 말합니다.

분류개발 › 코딩
태그검토코드개발자
프롬프트 (영어 본문 · 답은 한국어로 옵니다)
Review this code for security issues.

Work by following untrusted input, not by scanning for patterns:
1. **Entry points** — every place external input enters. Request bodies, query strings, headers, file uploads, webhooks, message queues, environment.
2. For each, trace where it flows: into queries, into commands, into file paths, into templates, into deserialization, into logs, back into a response.
3. At each destination, say whether it is neutralized and how. Name the specific mechanism, not "it is validated".
4. **Authorization** — for every operation, is the check present, and is it checking the right subject against the right object. Missing object-level checks are the most common real finding and the least likely to be caught by scanners.
5. Secrets — hardcoded, logged, returned in errors, committed.
6. What errors reveal to the caller.

Report per finding: the path from entry to impact, what an attacker gets, and the fix.

Rules:
- *Never conclude that the code is secure.* Report what you checked and what you could not. Absence of a finding is not evidence of safety.
- **State explicitly what the code alone cannot tell you** — framework defaults, middleware applied elsewhere, infrastructure controls, what the caller already authenticated.
- Separate confirmed issues from things that depend on context you cannot see.
- Do not pad with generic advice. If there is nothing at a given step, say so.
붙여 넣으면 맨 아래에 채울 칸(코드 · 실행 환경·신뢰 경계 · 다루는 데이터)이 나옵니다

어떤 프롬프트인가

보안 점검은 "안전합니다"가 제일 위험한 답이다. 이 프롬프트는 입력 경로를 따라가며 확인하고, 코드만으로는 판단 못 하는 부분을 분명히 남긴다.

모델 비교

ChatGPT는 흐름 추적과 불확실성 구분이 가장 충실하다. Gemini는 구체적이나 과도한 확정과 장황함이 있고, [C]는 답이 없다.

ChatGPT종합 1위
44/ 50

+ 확정 사항과 문맥 의존 위험을 가장 정확히 구분했다.

- 인가 누락을 확정 항목에 묶은 부분은 다소 모호하다.

Gemini
36/ 50

+ 입력에서 SQL·응답까지 흐름과 수정안을 구체화했다.

- 추정 위험을 확정 취약점·공격 결과처럼 과장했다.

기준ChatGPTGemini앞선 모델
지시 준수97ChatGPT +29%
정확성96ChatGPT +50%
구체성99동점
구성·가독성98ChatGPT +13%
분량 적정성86ChatGPT +33%

gpt-5.6-sol가 모델 이름을 가린 채 1~10점으로 채점했습니다 (2026-09-24). 측정값이 아니라 AI 심사 결과입니다.

답 전문 보기

같은 입력을 세 모델에 넣어 받은 답을 그대로 옮겼습니다. 각 CLI(에이전트 하네스)에서 실행했습니다. ChatGPT·Claude 앱이나 웹에서는 답이 다를 수 있습니다.

넣은 입력
코드: // routes/admin.js
router.get('/api/admin/users/:id/orders', (req, res) => {
  const userId = req.params.id;
  const query = `SELECT * FROM orders WHERE user_id = ${userId}`;
  db.query(query, (err, rows) => {
    if (err) return res.status(500).json({ error: err.message });
    res.json(rows);
  });
});

router.post('/api/admin/refund', (req, res) => {
  const { orderId, amount } = req.body;
  refundService.refund(orderId, amount);
  res.json({ ok: true });
});

실행 환경·신뢰 경계: 내부 관리자용 Node.js/Express API, AWS EC2 단일 인스턴스에 배포. 프론트는 React 어드민 페이지에서 호출. 로그인 시 발급한 JWT를 헤더에 실어 보내고 미들웨어에서 검증은 하지만, role(super_admin/staff) 체크는 일부 라우트에만 걸려 있습니다.

다루는 데이터: 고객 이름, 전화번호, 배송지 주소, 주문 금액, 환불 내역 등 결제·개인정보를 다룹니다.
gpt-5.6-luna (codex CLI · 에이전트 하네스) · 실행일 2026-09-24

확인된 문제

1. SQL 인젝션 + 관리자 API의 객체 수준 권한 누락

경로:

req.params.id → 문자열 템플릿 삽입 → SQL 쿼리 → 주문 목록 반환

const query = `SELECT * FROM orders WHERE user_id = ${userId}`;

userId를 숫자로 검증하거나 이스케이프하지 않고 SQL 문에 직접 삽입합니다. 공격자는 다음처럼 쿼리 구조를 변경할 수 있습니다.

/api/admin/users/1 OR 1=1/orders

영향:

  • 전체 고객의 주문 및 개인정보 조회 가능
  • DB 권한이 넓으면 추가 SQL 조작 가능성
  • SELECT *로 주문에 포함된 불필요한 민감 필드까지 노출 가능

또한 JWT 검증만으로는 충분하지 않습니다. 이 코드에는 현재 요청자가 super_admin 또는 허용된 staff인지, 그리고 해당 userId 객체에 접근할 권한이 있는지 확인하는 코드가 없습니다. 내부 관리자 API라도 직원 계정 간 접근 범위가 다르다면 IDOR/BOLA입니다.

수정:

router.get('/api/admin/users/:id/orders', requireJwt, requireAdminRole,
  async (req, res) => {
    const userId = Number(req.params.id);
    if (!Number.isSafeInteger(userId) || userId <= 0) {
      return res.status(400).json({ error: 'invalid user id' });
    }

    // 정책에 따라 객체 접근 권한 확인
    if (!canViewUser(req.user, userId)) {
      return res.status(403).json({ error: 'forbidden' });
    }

    const [rows] = await db.query(
      'SELECT id, user_id, amount, status, created_at FROM orders WHERE user_id = ?',
      [userId]
    );
    res.json(rows);
  }
);

숫자 변환만으로 SQL 인젝션 방어를 대체하지 말고, 반드시 DB 드라이버의 파라미터 바인딩을 사용해야 합니다.

2. DB 오류 메시지의 외부 반환

경로:

DB 오류 → err.message → HTTP 응답

res.status(500).json({ error: err.message });

공격자는 SQL 문, 테이블명, 컬럼명, DB 호스트 정보 또는 드라이버 내부 세부사항을 얻을 수 있습니다. 특히 위 SQL 인젝션을 시도할 때 오류 기반 탐색에도 사용됩니다.

수정:

  • 외부에는 고정된 일반 오류만 반환
  • 상세 오류는 접근 제한된 서버 로그에 기록
  • 로그에도 요청 입력값이나 개인정보를 무제한 남기지 않기
logger.error({ err, requestId: req.id }, 'order lookup failed');
return res.status(500).json({ error: 'internal server error' });

문맥에 따라 확인이 필요한 문제

3. 환불 API의 권한 및 객체 검증이 코드상 없음

경로:

req.body.orderId, req.body.amount → refundService.refund(orderId, amount) → 환불 처리

이 라우트에는 다음 검사가 보이지 않습니다.

  • 요청자가 super_admin인지, 환불 가능한 staff인지
  • 해당 요청자가 이 주문을 처리할 권한이 있는지
  • orderId가 실제 주문인지
  • 주문 상태가 환불 가능한 상태인지
  • 환불 금액이 주문 금액 이하인지
  • 이미 환불된 금액을 초과하지 않는지
  • 통화와 소수점 정밀도가 올바른지
  • 중복 요청을 막는 멱등성 처리

refundService 내부에서 모두 처리한다면 일부 문제는 없을 수 있지만, 제공된 코드만으로는 확인할 수 없습니다. 서비스에 검사가 없다면 공격자는 유효한 JWT를 가진 낮은 권한 직원으로 다른 고객의 주문을 환불하거나, 금액 조작·중복 환불을 발생시킬 수 있습니다.

수정 방향:

  • 라우트 또는 서비스 경계에서 역할 검사
  • DB에서 주문을 조회해 요청자의 접근 범위와 주문 소유자를 확인
  • 환불 가능 잔액을 서버 측 주문 데이터로 계산
  • 클라이언트가 보낸 금액을 신뢰하지 않기
  • 트랜잭션과 행 잠금 또는 원자적 조건 업데이트 사용
  • 결제 제공업체 호출에 멱등성 키 사용
  • 허용된 스키마로 orderId, amount 타입·범위 검증

4. 환불 성공 응답이 실제 결과보다 먼저 반환됨

refundService.refund(orderId, amount);
res.json({ ok: true });

refund()가 비동기 함수라면 완료를 기다리지 않습니다.

영향:

  • 실제 환불 실패 후에도 클라이언트는 성공으로 인식
  • Promise rejection이 별도 처리되지 않으면 프로세스 오류나 관찰되지 않는 실패 가능
  • 프론트엔드가 성공 응답을 근거로 주문 상태를 변경할 수 있음

수정:

try {
  const result = await refundService.refund(orderId, amount);
  return res.json({ ok: true, refundId: result.id });
} catch (err) {
  logger.error({ err, requestId: req.id }, 'refund failed');
  return res.status(502).json({ error: 'refund failed' });
}

단, 서비스가 동기 함수인지와 내부 트랜잭션 처리는 코드 밖의 정보입니다.

입력·출력별 확인 결과

  • 경로 파라미터 id: SQL에 직접 삽입되므로 확인된 SQL 인젝션입니다.
  • JSON body의 orderId, amount: 서비스 내부 구현과 검증이 보이지 않아 잠재적 권한 우회·금액 조작 문제입니다.
  • JWT 헤더: 미들웨어에서 서명 검증은 한다고 했지만, 이 코드에는 역할 및 객체 권한 검사가 없습니다.
  • 응답: 주문 조회 결과 전체를 반환하므로 DB 행의 민감 필드가 그대로 노출될 수 있습니다.
  • 로그: 이 코드에서 명시적 비밀정보 로깅은 없습니다.
  • 하드코딩된 비밀: 제시된 코드에는 보이지 않습니다.
  • 환불 라우트의 오류 반환: 직접적인 오류 노출은 보이지 않지만, 전역 Express 오류 처리기와 서비스 예외 처리 방식은 확인할 수 없습니다.

코드만으로 알 수 없는 사항

  • JWT가 어떤 알고리즘·키·issuer·audience 정책으로 검증되는지
  • 이 두 라우트에 실제로 인증·역할 미들웨어가 연결되어 있는지
  • refundService가 권한, 금액, 상태, 중복 환불을 검증하는지
  • DB 계정 권한과 SQL 드라이버의 파라미터 처리 방식
  • 프록시/WAF, 네트워크 보안 그룹, EC2 접근 제한
  • 전역 오류 처리기와 요청 로깅 설정
  • 브라우저 인증 방식 및 CSRF 방어 여부

따라서 이 코드만으로 가장 확정적인 취약점은 SQL 인젝션과 DB 오류 정보 노출입니다. 환불 API의 권한 우회 및 금액 조작은 refundService와 실제 라우터 미들웨어를 추가로 확인해야 확정할 수 있습니다.

같은 분류의 프롬프트

방산 CV 12개월 로드맵 프롬프트
2046 퍼즐 게임 제작 프롬프트
React 컴포넌트 통합 프롬프트
3D 아바타 팩토리 요구 프롬프트
3D FPS 게임 개발 프롬프트