콘텐츠로 이동

보안 코드리뷰 체크리스트

이 체크리스트는 AI 에이전트 Skill과 같은 파일(skills/kr-privacy-review/references/code-review-checklist.md)을 씁니다. PR 템플릿에 붙여 넣어도 됩니다.

개인정보·보안 코드리뷰 체크리스트

각 항목 끝의 표시는 근거 종류입니다. **[법률]·[시행령]·[고시]**는 법적 의무, **[표준]**은 OWASP ASVS 5.0 권고, **[권장]**은 모범사례입니다.

수집과 데이터 모델

  • 새 개인정보 필드마다 수집 목적과 처리 근거가 정해져 있다 — [법률] 개인정보 보호법 제15조제1항
  • 목적에 필요하지 않은 필드를 수집하지 않는다 — [법률] 제3조제1항, 제16조
  • 민감정보·고유식별정보·주민등록번호는 별도 근거를 확인했다 — [법률] 제23조, 제24조, 제24조의2
  • 필드에 보존기간 또는 파기 조건이 정의되어 있다 — [법률] 제21조
  • 처리방침 갱신이 필요한 변경인지 표시했다 — [법률] 제30조
  • 광고성 정보 수신동의를 채널별(문자·이메일·푸시)로 저장하고, 동의·거부·철회 이력을 남긴다 — 정보통신망법 제50조제1항·제7항, 같은 법 시행령 제62조의2
  • 광고 발송 워커가 메시지마다 실제 전송 시각(KST)으로 수신동의·수신거부·야간(21~08시) 동의·2년 재확인 기한을 다시 확인한다 — 정보통신망법 제50조제2항·제3항·제8항, 같은 법 시행령 제61조, 제62조의3

인증과 세션

  • 비밀번호는 일방향 암호화(적응형 해시: scrypt/argon2/bcrypt)로 저장한다 — [고시] 제7조제1항 / [표준] ASVS V6·V11
  • 인증 실패가 일정 횟수를 넘으면 접근을 제한한다 — [고시] 제5조제6항
  • 개인정보취급자가 외부에서 접속하면 안전한 인증수단(인증서·보안토큰·OTP 등)을 쓴다 — [고시] 제6조제2항
  • 세션 쿠키에 HttpOnly·Secure·SameSite가 있고 로그인 시 세션 ID를 재발급한다 — [표준] ASVS V7
  • 세션에 유휴·절대 만료 시간이 있다 — [표준] ASVS V7
  • 토큰·비밀번호 비교에 상수 시간 비교를 쓴다 — [표준] ASVS V11

권한

  • 모든 개인정보 API가 서버에서 권한을 확인한다 (UI 숨김만으로 막지 않는다) — [표준] ASVS V8
  • 객체 ID로 조회할 때 소유자·소속을 검증한다 (IDOR/BOLA) — [표준] ASVS V8
  • 권한은 업무에 필요한 최소 범위로 역할별 차등 부여한다 — [고시] 제5조제1항
  • 권한 부여·변경·말소 내역을 기록하고 최소 3년 보관한다 — [고시] 제5조제3항
  • 계정을 취급자별로 발급하고 공유 계정을 쓰지 않는다 — [고시] 제5조제4항
  • 시스템 관리 권한과 개인정보 열람 권한이 분리되어 있다 — [권장]

응답·로그

  • API 응답은 DTO 허용목록으로 필요한 필드만 반환한다 (ORM 엔티티 직접 직렬화 금지) — [법률] 제3조 / [표준] ASVS V4·V14
  • 애플리케이션 로그·에러 트래커·APM에 개인정보와 인증 토큰이 남지 않는다 — [표준] ASVS V16 / [권장]
  • 개인정보처리시스템 접속기록(누가·언제·어디서·무엇을·어떤 처리)을 남기고, 보관 기간(1년 이상, 해당 시 2년 이상)을 지킨다 — [고시] 제8조제1항
  • 접속기록은 위변조·도난·분실 방지 조치가 되어 있다 — [고시] 제8조제3항
  • 개인정보 다운로드 상황을 내부 관리계획에 정한 주기·방법으로 확인·점검한다 — [고시] 제8조제2항
  • 대량 다운로드에 사유 입력·건수 제한을 둔다 — [권장]

암호화·전송

  • 이용자의 주민등록번호·여권번호·운전면허번호·외국인등록번호·신용카드번호·계좌번호·생체인식정보는 암호화해 저장한다 — [고시] 제7조제2항
  • 인증정보를 정보통신망으로 송수신할 때 암호화(TLS)한다 — [고시] 제7조제1항 / [표준] ASVS V12
  • 암호화 키가 코드·저장소·로그에 없다 — [표준] ASVS V11·V13

외부 전송·LLM

  • 외부 API·SaaS·LLM으로 보내는 필드가 허용목록으로 제한된다 — [권장]
  • 외부 전송이 제3자 제공(제17조)인지 업무위탁(제26조)인지 판단 기록이 있다 — [법률] (판단은 review-required)
  • 국외 이전 여부(처리 지역)를 확인했다 — [법률] 제28조의8
  • LLM 전송 전 개인정보 탐지·마스킹이 있고, 테스트로 payload를 검증한다 — [권장]
  • LLM 제공자의 학습 이용·보존 정책을 확인했다 — [안내서] (review-required)

삭제·파기

  • 탈퇴·삭제 시 주 DB뿐 아니라 캐시·검색 인덱스·벡터 DB·분석 저장소에서도 지운다 — [법률] 제21조 / [권장]
  • 다른 법령으로 보존해야 하는 데이터는 분리 저장한다 — [법률] 제21조제3항
  • 파기 배치는 결과를 기록하고 실패를 알린다 — [고시] 제13조 / [권장]

테스트

  • 권한 없는 사용자·다른 사용자의 객체 ID로 요청하는 음성 테스트가 있다
  • 응답 스키마에 금지 필드(password_hash 등)가 없는지 검사하는 테스트가 있다
  • 로그 마스킹 테스트가 있다
  • 보존기간 만료 데이터가 파기되는지 시계를 주입한 테스트가 있다
  • 테스트 픽스처·시드에 실제 개인정보가 없고, 주민등록번호·카드번호·휴대전화번호·이메일 패턴 검사가 CI에 있다 — [법률] 제3조제1항·제7항 / [권장]
  • 운영 데이터를 개발·스테이징으로 옮기면 모든 컬럼에 처리 규칙이 있고, 규칙 없는 컬럼에서 변환이 실패한다 — [고시] 제12조제1항 / [권장]

테스트 케이스 템플릿

검토 보고서의 “9. 테스트 케이스”는 아래 형식으로 씁니다. 실행 가능한 예시는 가이드 저장소의 examples/python-fastapi/tests, examples/typescript/test에 있습니다.

ID: TC-<영역>-<번호>
목적: <무엇을 막는지>
전제: <사용자·역할·데이터 상태>
절차: <요청/동작>
기대 결과: <상태 코드, 응답 필드, 로그, 감사기록>
근거: <[법률]/[고시]/[표준]/[권장] 조항>

템플릿

영역테스트기대 결과
객체 권한사용자 A가 사용자 B의 리소스 ID로 GET/PUT/DELETE404 또는 403, B의 데이터가 응답·로그에 없음
역할 권한권한 없는 역할로 개인정보 API 호출403, 접근 시도가 감사로그에 남음
관리자 분리시스템 관리자(개인정보 열람 권한 없음)가 회원 상세 조회403. break-glass 사용 시 사유·만료 시각·감사로그 필수
최소 응답응답 JSON 키 집합 검사허용목록 외 필드(password_hash, rrn 등) 없음
로그 마스킹이메일·전화번호·주민등록번호·카드번호·Bearer 토큰을 포함한 로그 출력로그 출력에 원문 없음
접속기록개인정보 조회·다운로드 수행계정·시각·접속지·처리 내용·정보주체 식별값이 기록됨, 변조 시 검증 실패
인증 실패비밀번호 N회 연속 실패계정 또는 접근 제한
세션로그인 전후 세션 ID 비교, 유휴 시간 경과 후 요청로그인 시 ID 변경, 만료 후 401
보존·파기보존기간 경과 시점으로 시계를 옮기고 파기 배치 실행대상 삭제, 법정 보존 대상은 분리 저장소에만 존재, 파기 기록 생성
삭제 요청탈퇴 후 주 DB·캐시·검색 인덱스·벡터 DB 조회결과 없음
내보내기본인 데이터 내보내기본인 데이터만 포함, 다른 사용자 데이터 없음
LLM 필터개인정보가 담긴 입력으로 기능 실행, 가짜 LLM 클라이언트로 payload 캡처payload에 원문 개인정보 없음, 허용목록 필드만 존재
다운로드대량 내보내기접속기록 생성(고시 제8조), 사유 입력·건수 제한([권장])