FlawOpen

flawopen.com/JavaScript の SQL Injection/Prisma

Prisma $queryRaw は SQL Injection に安全?

リファレンスガイド
要点

クエリパラメーターは、SQL フォームに封印された値欄のようなものです。データベースは値を構文ではなくデータとして扱います。$queryRaw の tagged template は補間値をバインドします。placeholder と引数を分けて渡せば $queryRawUnsafe でも値をバインドできますが、信頼できない文字列を連結して SQL を作ると危険です。どちらの方法もテーブル名や列名はパラメーター化できません。

危険 — 入力値を SQL に連結
const userId = request.query.userId;
const query =
  `SELECT id FROM users WHERE id = ${userId}`;
const rows = await prisma.$queryRawUnsafe(query);
強化 — 値をバインドし SQL 構造は allowlist で限定
const rows = await prisma.$queryRaw`
  SELECT id FROM users WHERE id = ${userId}
`;

// PostgreSQL example: $1 binds data, not an identifier.
const columns = { name: 'name', created: 'created_at' } as const;
const column = columns[requestedSort as keyof typeof columns] ?? 'created_at';
const pageSize = 25;
const sortedRows = await prisma.$queryRawUnsafe(
  `SELECT id FROM users ORDER BY ${column} LIMIT $1`,
  pageSize
);
// The selected SQL structure comes from a fixed map.

Prisma のパラメーターでできることと限界

$queryRaw では tagged template の呼び出しを直接使います。補間値はそれぞれデータベースパラメーターになります。SQL 文字列を先に作り、それだけを引数に渡してはいけません。Prisma は $queryRawUnsafe(query, ...values) の placeholder もサポートしており、値をバインドできますが SQL テキスト自体は信頼できる必要があります。パラメーターはデータ値用で、識別子、演算子、SQL 断片には使えません。選択可能な列はアプリケーション管理の固定 allowlist に対応付けます。多くの型安全な raw クエリ用途では Prisma は TypedSQL を推奨しています。

Prisma raw クエリの監査方法

grep -rn "\$queryRawUnsafe\|\$executeRawUnsafe" --include="*.ts" --include="*.js" .

よくある質問

$queryRawUnsafe はなぜ存在するのですか?

raw SQL 文字列を受け取り、値を別引数で渡す placeholder にも対応するためです。柔軟な分、危険な文字列生成を招きやすいので SQL と引数を一緒に確認します。動的識別子が安全になるわけではありません。固定 allowlist から選んでください。

参考資料

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com Railsのwhere()文字列補間は安全? Sequelize query() は SQL Injection に安全? SQLAlchemy text() は SQL Injection に安全? sqlx のコンパイル時クエリ検査は SQL インジェクションを防ぎますか? ストアドプロシージャは SQL インジェクションを防ぎますか? TypeORM QueryBuilder は SQL Injection に安全?