Skip to Content
ドキュメントWebコースバックエンド・DB🔒 SQLインジェクションを理解して対策する

🔒 SQLインジェクションを理解して対策する

このページで学ぶこと

  • SQLインジェクションとは何かがわかる
  • 実際に攻撃を体験して危険性を実感できる
  • Prisma を使えば自動的に安全である理由がわかる

SQLインジェクションとは

SQLインジェクション は、ユーザーの入力を通じて悪意のある SQL を実行させる攻撃です。

攻撃者が入力: ' OR '1'='1

実行されるSQL: SELECT * FROM users WHERE email = '' OR '1'='1'
plaintext

OR '1'='1' は常に true なので、すべてのユーザーのデータが取得されてしまいます。


攻撃の前に、usersテーブルへシードデータを入れる

このあとの攻撃デモで結果がわかりやすいように、先に users テーブルへ10件ほどデータを入れます。

prisma/seed.mjs を作成して、次のコードを書きましょう。

// prisma/seed.mjs
import 'dotenv/config' // .env を読み込む
import { PrismaClient } from '../generated/prisma/client.js' // ESM では拡張子 .js まで書く
import { PrismaPg } from '@prisma/adapter-pg'
 
// Prisma 7 では adapter を渡して PrismaClient を作る
const adapter = new PrismaPg({
  connectionString: process.env.DATABASE_URL,
})
 
const prisma = new PrismaClient({ adapter })
 
async function main() {
  // 入れたいユーザーデータを配列でまとめる
  const users = [
    { name: 'Seed User 1', email: 'test1@example.com' },
    { name: 'Seed User 2', email: 'test2@example.com' },
    { name: 'Seed User 3', email: 'test3@example.com' },
    { name: 'Seed User 4', email: 'test4@example.com' },
    { name: 'Seed User 5', email: 'test5@example.com' },
    { name: 'Seed User 6', email: 'test6@example.com' },
    { name: 'Seed User 7', email: 'test7@example.com' },
    { name: 'Seed User 8', email: 'test8@example.com' },
    { name: 'Seed User 9', email: 'test9@example.com' },
    { name: 'Seed User 10', email: 'test10@example.com' },
  ]
 
  // createMany: 複数件をまとめて作成する
  await prisma.user.createMany({
    data: users,
    skipDuplicates: true, // 同じemailがあれば重複作成しない
  })
 
  console.log('usersテーブルにシードデータを約10件入れました')
}
 
main()
  .catch((error) => {
    console.error(error)
    process.exit(1)
  })
  .finally(async () => {
    await prisma.$disconnect() // 接続を閉じる
  })
javascript

実行コマンド:

npx prisma generate
node prisma/seed.mjs
bash

Supabase の Table Editor で users テーブルに test1@example.comtest10@example.com が入っていれば準備完了です。


攻撃を体験してみよう

脆弱なコードを書く

まずわざと脆弱なコードを書いて、SQLインジェクションを体験しましょう。

app/api/injection/route.js を作成して、次のコードを書いてください。

// ⚠️ app/api/injection/route.js(脆弱なコード — 絶対に本番で使わない)
import { NextResponse } from 'next/server'
import { prisma } from '../../../lib/prisma'
 
export async function GET(request) {
  const { searchParams } = new URL(request.url)
  const email = searchParams.get('email')
 
  // ❌ 危険:ユーザー入力をSQLに直接埋め込んでいる
  const result = await prisma.$queryRawUnsafe(
    `SELECT * FROM users WHERE email = '${email}'`
  )
 
  return NextResponse.json(result)
}
javascript

以下のURLにアクセスしてみましょう(localhost:3000 で開発サーバーが動いている状態で)。

# 通常のリクエスト
http://localhost:3000/api/injection?email=test1@example.com

# 攻撃リクエスト(シングルクォートを入れる)
http://localhost:3000/api/injection?email=' OR '1'='1
plaintext

2番目のURLで全ユーザーのデータが返ってきたら、攻撃成功です。
実際の攻撃では、パスワードのハッシュや個人情報が丸ごと盗まれます。


Prisma は自動で安全

Prisma の通常の操作は**プレースホルダー(パラメータ化クエリ)**を使うため、SQLインジェクションが起きません。

app/api/injection/route.js を次のように書き換えて、http://localhost:3000/api/injection?email=' OR '1'='1にアクセスし、攻撃が成功しないことを確認してみましょう。

// ✅ 安全(Prismaの通常操作)
const result = await prisma.user.findFirst({
  where: { email: email }  // email がどんな文字列でも安全
})
javascript

内部では以下のようなSQLが実行されます。

-- プレースホルダーを使ったSQL(安全)
SELECT * FROM users WHERE email = $1
-- $1 に email の値が別途渡される
sql

$1 と値を分離して渡すため、値の中に SQL の記号が含まれていても、SQL として解釈されません。


$queryRaw を使うときの注意

複雑な条件で生のSQLを書きたいときは $queryRaw を使います。
この場合もテンプレートリテラル(タグ付きテンプレート)を使えば安全です。

// ✅ 安全(テンプレートリテラルを使う)
const result = await prisma.$queryRaw`
  SELECT * FROM users WHERE email = ${email}
`
 
// ❌ 危険($queryRawUnsafe に直接文字列を渡す)
const result = await prisma.$queryRawUnsafe(
  `SELECT * FROM users WHERE email = '${email}'`
)
javascript

対策まとめ

状況対応
通常のCRUD操作Prismaの通常操作を使えばOK(自動で安全)
生のSQLを書きたい$queryRaw + テンプレートリテラルを使う
$queryRawUnsafeユーザー入力を絶対に渡さない

Prisma を使っている限り、通常の操作では SQLインジェクションは起きません。
安心して開発に集中してください。

ただし、「生のSQL($queryRawUnsafe)を使わなければいけない状況」になったら必ずプレースホルダーを意識してください。


確認しよう

  • 脆弱なエンドポイントを作り、SQLインジェクションを体験した
  • Prismaの通常操作がなぜ安全かを説明できる
  • プレースホルダー(パラメータ化クエリ)とは何かを説明できる

AIに聞いてみよう

「SQLインジェクション以外にもDBに関連するセキュリティリスクにはどんなものがありますか?」


次のステップ

Supabase Authで認証・認可を実装する

Last updated on