validator.js의 isLength가 뚫렸다 — 유니코드 제로 폭 문자가 만든 CVSS 7.5 취약점
npm 주간 다운로드 900만 건 이상인 validator 라이브러리. 이메일 검증, URL 검증, 문자열 길이 체크까지 — Node.js 백엔드에서 거의 "기본 장착"처럼 쓰이는 패키지다. 이 라이브러리의 isLength() 함수에서 유니코드 Variation Selector를 이용한 길이 검증 우회 취약점이 발견됐다. CVSS 7.5(High). 비유하면 공항 보안 검색대를 투명 망토를 입고 통과한 것과 같다.
무슨 취약점인가
isLength(str, { max: 5 }) — 이 함수는 문자열 길이가 5 이하인지 검사한다. 단순해 보이지만, 유니코드 Variation Selector(U+FE0E, U+FE0F)를 문자 사이에 끼워 넣으면 실제 길이가 5,000자인 문자열이 길이 검사를 통과한다.
const validator = require('validator')
const payload = 'a' + '\uFE0F'.repeat(5000)
console.log('실제 길이:', payload.length)
// 실제 길이: 5001
console.log('isLength 결과:', validator.isLength(payload, { max: 5 }))
// isLength 결과: true (취약 버전에서)5,001자짜리 문자열이 max: 5 검사를 통과한다. isLength()가 Variation Selector를 길이 계산에서 제외했기 때문이다.
[💡 잠깐! 이 용어는?]
Variation Selector(변이 선택자): 유니코드 문자 U+FE0E(텍스트 표현)와 U+FE0F(이모지 표현)는 앞 문자의 렌더링 방식을 바꾸는 제어 문자다. 화면에는 보이지 않지만 문자열의 바이트 크기에는 포함된다.
왜 위험한가
| 공격 시나리오 | 설명 | 심각도 |
|---|---|---|
| 서비스 거부(DoS) | 길이 제한을 우회해 수 MB짜리 페이로드를 서버에 전송 | 높음 |
| 데이터 절삭 | DB의 VARCHAR(255) 컬럼에 초과 데이터가 들어가면 잘림 발생 | 중간 |
| 보안 우회 | API 요율 제한, WAF 규칙 등 길이 기반 필터링을 통과 | 높음 |
DoS 시나리오
웹 서버에서 isLength(body.comment, { max: 1000 })으로 댓글 길이를 제한한다고 하자. 공격자가 'a' + '\uFE0F'.repeat(10000000)을 보내면 길이 검사는 통과하지만, 실제 메모리에는 10MB 이상이 적재된다. 비유하면 "짐 5kg 제한"인 비행기에 투명 보자기로 감싼 100kg 짐을 들고 탑승하는 것이다.
데이터 절삭 시나리오
-- 테이블: comments (content VARCHAR(255))
-- validator가 통과시킨 5,001자 문자열이 INSERT되면
-- DB에서 255자로 잘림 → 데이터 무결성 파괴
INSERT INTO comments (content) VALUES ('a️️️️️...(5001자)');애플리케이션은 "길이 검사를 통과했으니 안전하다"고 판단하지만, DB 레이어에서 데이터가 잘리면서 예기치 않은 버그가 발생한다.
취약점 정보
| 항목 | 내용 |
|---|---|
| CVE | CVE-2025-12758 |
| CVSS | 7.5 (High) |
| CWE | CWE-792 (특수 요소의 불완전한 필터링) |
| 영향 버전 | validator < 13.15.22 |
| 패치 버전 | 13.15.22 |
| 보고일 | 2025-10-18 |
| 패치 배포일 | 2025-11-09 |
| CVE 공개일 | 2025-11-27 |
[💡 잠깐! 이 용어는?] CVSS(Common Vulnerability Scoring System): 취약점의 심각도를 0~10 점수로 수치화하는 표준. 7.0 이상이면 "High" 등급이다.
내 프로젝트가 영향을 받는지 확인하는 법
npm ls validator출력에서 버전이 13.15.22 미만이면 취약하다. 수정은 단순하다:
npm install validator@latest간접 의존성(다른 패키지가 validator를 내부적으로 사용하는 경우)도 확인해야 한다. npm audit을 실행하면 CVE-2025-12758이 검출되는지 볼 수 있다.
npm audit더 넓은 교훈
이 취약점은 isLength() 하나의 버그이지만, 근본적인 문제는 "문자열 길이"라는 개념 자체가 복잡하다는 것이다.
| 길이 측정 방법 | 'a\uFE0F'의 길이 | 용도 |
|---|---|---|
String.length | 2 | UTF-16 코드 유닛 수 |
[...str].length | 2 | 유니코드 코드포인트 수 |
Intl.Segmenter | 1 | 시각적 문자(grapheme) 수 |
| 바이트 길이 | 4 (UTF-8) | 네트워크/저장소 크기 |
"길이"를 어떻게 정의하느냐에 따라 같은 문자열의 길이가 1이 되기도, 4가 되기도 한다. isLength()의 원래 의도가 "사용자가 인식하는 글자 수"였다면 Intl.Segmenter를 써야 맞지만, "바이트 크기 제한"이 목적이었다면 Buffer.byteLength를 써야 한다. 라이브러리가 이 두 가지를 혼동한 것이 취약점의 근본 원인이다.
[💡 잠깐! 이 용어는?]
Grapheme Cluster(자소 군집): 사람이 "하나의 글자"로 인식하는 단위. 이모지 👨👩👧👦는 7개의 코드포인트로 구성되지만 시각적으로는 1글자다. Intl.Segmenter로 정확히 셀 수 있다.
정리
validator라이브러리의isLength()가 유니코드 Variation Selector를 길이 계산에서 누락하는 취약점이 발견됐다- CVSS 7.5(High), DoS·데이터 절삭·보안 우회가 가능하다
- 13.15.22 이상으로 업데이트하면 해결된다
- "문자열 길이"는 측정 방법에 따라 결과가 다르다 — 보안 검증에서는 바이트 길이와 시각적 길이를 구분해야 한다
isLength()하나에만 의존하지 말고, DB 컬럼 제한과 애플리케이션 검증을 이중으로 적용하는 것이 안전하다
참고:
- NVD — CVE-2025-12758: https://nvd.nist.gov/vuln/detail/CVE-2025-12758
- Full Disclosure 메일링 리스트: https://seclists.org/fulldisclosure/2026/Jan/27
- validator.js GitHub: https://github.com/validatorjs/validator.js
관심 있을 만한 포스트
배포 전 출국 심사 — Publint로 npm 패키지 실수를 원천 차단하는 법
npm 패키지의 exports, entry points, 모듈 포맷을 배포 전에 검증하는 Publint 도구 사용법.
AI 코딩의 맹점 — Artifacts 없이 에이전트는 기억을 잃는다
PRD, ADR, TDD가 AI 코딩 워크플로우에서 왜 선택이 아닌 필수인지, 실전 구조와 함께 살펴본다.
Next-Translate 3.0 — Turbopack과 App Router를 위한 i18n 재건
1년간 공백 후 돌아온 Next-Translate 3.0이 Turbopack 지원, 비동기 params, App Router 안정화를 한 번에 처리하는 방법.
V8 WasmGC 투기적 최적화 — 가상 메서드를 인라인으로 만드는 법
V8이 WasmGC의 가상 메서드 디스패치에 투기적 인라이닝을 도입해 Dart와 Java 앱에서 최대 8% 성능을 끌어낸 방법.
Vinext — Vite 위에서 Next.js를 1주일 만에 다시 만든 이야기
Cloudflare가 AI와 함께 단 일주일, $1,100의 API 비용으로 Next.js 호환 프레임워크를 Vite 위에 구축한 과정.
Tsonic — TypeScript를 네이티브 바이너리로 컴파일하는 실험
TypeScript → C# → NativeAOT 파이프라인으로 네이티브 실행 파일을 만드는 Tsonic. 어떻게 동작하고, 어떤 한계가 있는지 살펴봤다.
VS Code 팀의 AI 에이전트 병렬화 — 월간 릴리스를 주간으로 만든 워크플로우
VS Code 팀이 월간 릴리스에서 주간 릴리스로 전환한 비결. 에이전트 세션 병렬화, 자동화 파이프라인, 품질 게이트 설계 전반을 공개했다.
React Compiler의 한계 — 뭘 최적화하고 뭘 못 하는가
React Compiler가 자동 메모이제이션으로 해결하는 것과 해결하지 못하는 것. 컴파일러 기반 UI 프레임워크의 능력 경계를 정리했다.