validator.js의 isLength가 뚫렸다 — 유니코드 제로 폭 문자가 만든 CVSS 7.5 취약점

7 min read
보안validator.jsCVE유니코드npm
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자인 문자열이 길이 검사를 통과한다.

cve-2025-12758-poc.js
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 레이어에서 데이터가 잘리면서 예기치 않은 버그가 발생한다.


취약점 정보

항목내용
CVECVE-2025-12758
CVSS7.5 (High)
CWECWE-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" 등급이다.


내 프로젝트가 영향을 받는지 확인하는 법

validator 버전 확인
npm ls validator

출력에서 버전이 13.15.22 미만이면 취약하다. 수정은 단순하다:

validator 업데이트
npm install validator@latest

간접 의존성(다른 패키지가 validator를 내부적으로 사용하는 경우)도 확인해야 한다. npm audit을 실행하면 CVE-2025-12758이 검출되는지 볼 수 있다.

npm audit으로 취약점 탐지
npm audit

더 넓은 교훈

이 취약점은 isLength() 하나의 버그이지만, 근본적인 문제는 "문자열 길이"라는 개념 자체가 복잡하다는 것이다.

길이 측정 방법'a\uFE0F'의 길이용도
String.length2UTF-16 코드 유닛 수
[...str].length2유니코드 코드포인트 수
Intl.Segmenter1시각적 문자(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 컬럼 제한과 애플리케이션 검증을 이중으로 적용하는 것이 안전하다

참고:

관심 있을 만한 포스트

배포 전 출국 심사 — Publint로 npm 패키지 실수를 원천 차단하는 법

npm 패키지의 exports, entry points, 모듈 포맷을 배포 전에 검증하는 Publint 도구 사용법.

npmPublint

AI 코딩의 맹점 — Artifacts 없이 에이전트는 기억을 잃는다

PRD, ADR, TDD가 AI 코딩 워크플로우에서 왜 선택이 아닌 필수인지, 실전 구조와 함께 살펴본다.

AI 코딩Artifacts

Next-Translate 3.0 — Turbopack과 App Router를 위한 i18n 재건

1년간 공백 후 돌아온 Next-Translate 3.0이 Turbopack 지원, 비동기 params, App Router 안정화를 한 번에 처리하는 방법.

Next.jsi18n

V8 WasmGC 투기적 최적화 — 가상 메서드를 인라인으로 만드는 법

V8이 WasmGC의 가상 메서드 디스패치에 투기적 인라이닝을 도입해 Dart와 Java 앱에서 최대 8% 성능을 끌어낸 방법.

V8WebAssembly

Vinext — Vite 위에서 Next.js를 1주일 만에 다시 만든 이야기

Cloudflare가 AI와 함께 단 일주일, $1,100의 API 비용으로 Next.js 호환 프레임워크를 Vite 위에 구축한 과정.

VinextNext.js

Tsonic — TypeScript를 네이티브 바이너리로 컴파일하는 실험

TypeScript → C# → NativeAOT 파이프라인으로 네이티브 실행 파일을 만드는 Tsonic. 어떻게 동작하고, 어떤 한계가 있는지 살펴봤다.

TypeScriptNativeAOT

VS Code 팀의 AI 에이전트 병렬화 — 월간 릴리스를 주간으로 만든 워크플로우

VS Code 팀이 월간 릴리스에서 주간 릴리스로 전환한 비결. 에이전트 세션 병렬화, 자동화 파이프라인, 품질 게이트 설계 전반을 공개했다.

VS CodeAI

React Compiler의 한계 — 뭘 최적화하고 뭘 못 하는가

React Compiler가 자동 메모이제이션으로 해결하는 것과 해결하지 못하는 것. 컴파일러 기반 UI 프레임워크의 능력 경계를 정리했다.

ReactReact Compiler