개인정보 웹 노출은 주민등록번호·카드번호·연락처 같은 식별정보가 인증 없이 접근 가능한 페이지·첨부파일·API 응답에 그대로 실려 나가는 상태를 말한다.
대부분 침해가 아니라 설정에서 발생한다. 그래서 공격 흔적도 없고, 검색엔진에 색인된 뒤 제3자 제보로 알게 되는 경우가 많다.
이 글은 자주 발생하는 실수 패턴, 표준 구현 코드, 30초 자가진단, 24시간 패치 순서를 정리했다.
한눈에 보는 핵심
Q. 실제로 과징금이 나오나요? A. 나옵니다. 개인정보보호위원회는 2026년 4월 법적 근거 없는 주민등록번호 수집과 안전조치 미흡을 이유로 3개사에 과징금 48억원 규모를 부과했습니다. 결혼정보업체 사례는 과징금 11억 9,700만원과 과태료 1,320만원, 약 43만명 규모였습니다. Q. 소량 노출이면 괜찮지 않나요? A. 규모가 작아도 처분은 나옵니다. 같은 시기 한 공원묘원은 과징금 5,420만원과 함께 취약점 점검·암호화 시정명령을 받았고, 처분 사실을 홈페이지에 공표하도록 명령받았습니다. 금액보다 공표 명령의 신뢰 손실이 더 큽니다. Q. 주민번호를 안 받으면 되나요? A. 그게 원칙입니다. 2014년 8월 주민등록번호 수집 법정주의 시행 이후 법령에 명시적 근거가 있는 경우에만 수집·이용이 가능합니다. 근거 없이 보유 중이라면 노출 여부와 무관하게 그 자체가 위반입니다.자주 발생하는 실수 패턴
| # | 패턴 | 위험도 |
|---|---|---|
| 1 | 게시판·문의 첨부파일이 인증 없이 URL만으로 다운로드 — 신청서·명단 파일 노출 | 치명 |
| 2 | 정산·명단 엑셀/CSV가 /uploads, /files 등 공개 디렉터리에 방치 | 치명 |
| 3 | API가 화면에 안 쓰는 필드까지 통째 반환 — 프론트에서만 가리고 응답엔 원본 | 치명 |
| 4 | 관리자 페이지·통계 화면이 인증 없이 접근 가능 | 치명 |
| 5 | 노출 페이지가 검색엔진에 색인 — 삭제 후에도 캐시로 잔존 | 높음 |
| 6 | 법적 근거 없이 주민등록번호를 수집·보관 (2014-08 법정주의 위반) | 치명 |
표준 구현 코드
# Django — 응답 필드 화이트리스트 + 저장 시 마스킹
RRN_RE = re.compile(r'\d{6}-?[1-4]\d{6}')
CARD_RE = re.compile(r'(?<!\d)\d{4}[\s-]\d{4}[\s-]\d{4}[\s-]\d{4}(?!\d)')
def mask_pii(text: str) -> str:
text = RRN_RE.sub(lambda m: m.group()[:8] + '******', text)
return CARD_RE.sub(lambda m: m.group()[:4] + '-****-****-' + m.group()[-4:], text)
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
# exclude가 아니라 fields 화이트리스트 — 모델에 컬럼이 늘어도 자동 노출되지 않는다
fields = ('id', 'name_masked', 'created_at')
# 첨부파일은 직접 URL 대신 권한 검사를 거친 뷰로만 서빙
@login_required
def download(request, pk):
att = get_object_or_404(Attachment, pk=pk)
if att.owner_id != request.user.id and not request.user.is_staff:
raise PermissionDenied
return FileResponse(att.file.open('rb'), as_attachment=True)
핵심은 "동작하는 코드"와 "안전한 코드"가 다르다는 점이다.
AI 코딩 도구는 기능 충족 코드를 우선 출력하므로 보안 분기는 별도로 강제해야 한다.
미들웨어·정책·CI 게이트로 모든 경로에 일괄 적용하는 것이 표준이다.
30초 자가진단
# 1) 검색엔진에 이미 색인됐는가 (구글·네이버에서 각각 확인)
# site:example.com filetype:xlsx / site:example.com 주민등록번호
# 2) 공개 디렉터리에 명단 파일이 있는가
for p in /uploads/ /files/ /download/ /backup/ /excel/; do
printf '%s ' "$p"; curl -s -o /dev/null -w '%{http_code}\n' "https://example.com$p"
done
# 3) API 응답에 화면에서 안 쓰는 식별정보가 실려 오는가
curl -s https://example.com/api/users/1 | grep -nE '[0-9]{6}-?[1-4][0-9]{6}|[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}'
위 시그널이 발견되면 결함이다. 개인정보보호위원회를 함께 참조하라.
24시간 패치 절차
| 순위 | 조치 |
|---|---|
| 1 | 노출 파일·엔드포인트를 즉시 비공개 전환 (삭제보다 접근 차단이 먼저 — 증거 보존) |
| 2 | 검색엔진 캐시 삭제 요청 — 구글 Search Console·네이버 서치어드바이저 각각 제출 |
| 3 | API 응답을 화이트리스트 방식으로 전환, 첨부파일은 권한 검사 뷰로만 서빙 |
| 4 | 보유 중인 주민등록번호의 법적 근거를 확인 — 근거 없으면 파기가 조치다 |
| 5 | 유출 규모·경위 확인 후 통지·신고 요건 검토 (미통지 자체가 별도 처분 사유) |
지금 해야 할 것
실무자: CodeScan 무료 스캔으로 30초 점검 후 우선순위대로 패치.
CISO·임원: 조직 전체 점검·규제 대응은 SENTRIX 30분 1:1 상담으로.
결함은 발견 즉시, 늦어도 24시간 안에 패치하는 것이 표준이다.