Go to list of users who liked
Share on X(Twitter)
Share on Facebook
More than 1 year has passed since last update.
パスワードがハッシュ値で保存されているサイトのSQLインジェクションによる認証回避の練習問題
SQLインジェクションによる認証回避
SQLインジェクションによる影響として、情報が漏洩するとか、データが勝手に更新されてしまうなどとともに、認証回避の例がよく紹介されます(私の本でも取り上げています)。
典型的な例は下記のとおりです。
// $id と $password は外部からの入力$sql="SELECT * FROM users WHERE id='$id' AND password='$password'";このケースで、\$idに適当な値、$passwordとして「'OR'a'='a」を入力すると、生成されるSQL文は下記となります。
SELECT*FROMusersWHEREid='XXX'ANDpassword=''OR'a'='a'ANDとORでは、ANDの方が先に計算されます。id='XXX' AND password='' は偽ですが、'a'='a'は常に真なので、WHERE句全体は常に真になります。これでパスワードを知らなくてもログインできるというわけです。
パスワードがハッシュ値で保存されているとどうなる?
しかし、今どきはパスワードはハッシュ値で保存することになっています。なのでログイン処理は以下のようになっているはずです(PHPの場合)。
$sql = "SELECT * FROM users WHERE userid = '$userid'";$stmt = $pdo->query($sql);$user = $stmt->fetch();if ($user && password_verify($password, $user['password'])) { echo "ログイン成功:" . htmlspecialchars($user['userid']);} else { // ログイン失敗この処理ですと、SQLクエリの結果、ユーザーがヒットして、かつユーザのパスワードハッシュ値が、入力値から計算したハッシュ値と一致していなければなりません。PHPのpassword_verify関数(マニュアル)がこのチェックをしています。なので、「'OR'a'='a」などで認証回避できません。
練習問題を作ってみた
この条件でSQLインジェクションによる認証回避の練習問題を作ってみました。以下はログイン処理の中身です。
<?php$userid=$_POST['userid'];$password=$_POST['password'];$options=[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,];try{$pdo=newPDO("sqlite:../db.sqlite3",null,null,$options);$sql="SELECT * FROM users WHERE userid = '$userid'";$stmt=$pdo->query($sql);$user=$stmt->fetch();if($user&&password_verify($password,$user['password'])){echo"ログイン成功:".htmlspecialchars($user['userid']);}else{echo"ログイン失敗";}}catch(Exception$e){echo"エラー:".htmlspecialchars($e->getMessage());}データベースの中身
CREATETABLEusers(idint,useridvarchar(64),passwordvarchar(128),emailvarchar(128),PRIMARYKEY(id));INSERTINTOusersVALUES(1,'admin','$2y$10$7sTu7b3VW6cMreA6QrdYkOQpPP9hSiXLDpuAzElGHios2N/y5Ct2C','admin@example.jp');INSERTINTOusersVALUES(2,'alice','$2y$10$fxOHmQr3NB4xfvkxOnTTqudbLGUQj4LCgScg9vmsNnpTpRuG4lWMW','alice@example.jp');ログインフォームを含めた完全なソースコードは、GitHubのリポジトリを用意しました。以下のコマンドでインストールできます。Windows、Mac、Linuxに対応しているはずです。
$ git clone https://github.com/ockeghem/SQLi-to-bypass-auth.git$ cd SQLi-to-bypass-auth$ docker compose up -dhttp://localhost:7890/ にアクセスすると以下のログインフォームが表示されます。
攻撃の結果、以下の状態(ログイン成功:admin)となれば攻撃成功です。
想定正解については、数日後に公開したいと思います。
2023/9/20追記。解答編を書きました。
発展問題(2023/9/18追記)
adminでログインできたという方向けに発展問題を。
- ソースコードの情報は使ってよい想定ですが、ソースコードの内容に依存しない解法も考えてください
- SQLインジェクションを使ってSQLiteのバージョンを表示してください
- SQLインジェクションを使ってテーブル情報(テーブル名や列名など)を表示してください
※ これから解かれる人もいるので、X(Twitter)やブックマーク、この記事のコメント欄などに解答やヒントは書かないでください。ご自身のブログやQiita等に書くのはかまいません。
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme