どーも、ばぁどです。
数年ぶりに徳丸本で勉強させていただいております。 2019年の年末に同僚に徳丸本を貸して、早2年久々に手元に戻ってきたので勉強し直しております。
XSSとはなんでしょう?や、CSRFの脆弱性などは一通り把握している(つもり)ので、7章の脆弱性診断入門で脆弱性診断を行っていた時のお話です。
本の7章に脆弱性診断入門という章があり、今の僕にめちゃくちゃありがたい!!!と思って本を読みながら作業を進めていたのですが、ユーザー登録時に「只今サイトが大変混雑しています。もうしばらく立ってからアクセスしてください」と表示されてしまったので、それを解消した時のお話です。
エラーでユーザー登録ができない
脆弱性診断入門では脆弱性診断を行うためのTODOアプリが同梱されています。
ユーザー登録入力画面
ユーザー情報の入力画面です。 入力する内容は、ユーザー名、パスワード、メールアドレス、アイコンの画像です。
ユーザー登録確認画面
ユーザー登録入力画面で、入力されたデータを画面上で確認することができる画面です。
出力されたエラー
あからさまにサーバー側で何かしらのエラーが出ているようなエラー文が表示されてしまいました。
基本的な入力値に対するバリデーションは入っているようなので、おそらくクライアント側からの入力値エラーではないと踏んでおり、また該当スクリプトは筆者のVM上で動いているスクリプトなので、大量に外部からアクセスが行われサーバーがダウンしている可能性はゼロです。何かしらのエラーがスクリプト側で起きていると判断していました。
只今サイトが大変混雑しています。もうしばらく経ってからアクセスしてください
うーむ。。少し苦手だが、PHPファイルを覗いてみるか・・・・
PHPスクリプトを覗いてみる
筆者はPHPが非常に苦手です。
変数前の$
が苦手なのと、行末にある;
(セミコロン)がアレルギーです。
とはいえ、アレルギーを起こしていても仕方がないので、PHPを読みます。
URLが下記なので、adduser.php
という PHP ファイルが実行されていることは分かっていたので、該当ファイルをVMサーバーから探しました。
http://example.jp/todo/adduser.php
adduser.php ファイルの内容
下記が今回読んでみた adduser.php
です。
1 <?php 2 require_once './common.php'; 3 $id = $_POST["id"]; 4 $pwd = substr($_POST["pwd"], 0, 6); 5 $email = $_POST["email"]; 6 $icon = $_POST["iconfname"]; 7 8 try { 9 $dbh = dblogin(); 10 11 $sql = 'INSERT INTO users VALUES(NULL, ?, ?, ?, ?, 0)'; 12 $sth = $dbh->prepare($sql); 13 $rs = $sth->execute(array($id, $pwd, $email, $icon)); 14 } catch (PDOException $e) { 15 $logger->add('?~B??~B??~C??~A?失?~U~W?~A~W?~A??~A~W?~A~_: ' . $e->getMessage()); 16 die('?~O??~J?~B??~B??~C~H?~A~L大?~I混?~[~Q?~A~W?~A??~A~D?~A??~A~Y?~@~B?~B~B?~A~F?~A~W?~A??~B~I?~A~O?~L?~A??~A??~A~K?~B~I?~B??~B??~B??~B??~A~W?~A??~A~O?~A| ?~A~U?~A~D'); 17 } 18 ?> 19 <html> ====== 以下省略 =============== xx </html>
$_POST
からIDやパスワードを受け取っており(3行目~6行目)、その後DB関連の処理(9行目~13行目)がtry-catch
で囲われています。
今回はエラーが起きているので、catch
している後の14行目の後にvar_dump()
を使って$e
の中身を覗いてみます。
====== 省略 =============== 13 $rs = $sth->execute(array($id, $pwd, $email, $icon)); 14 } catch (PDOException $e) { 15 var_dump($e); 16 $logger->add('?~B??~B??~C??~A?失?~U~W?~A~W?~A??~A~W?~A~_: ' . $e->getMessage()); 17 die('?~O??~J?~B??~B??~C~H?~A~L大?~I混?~[~Q?~A~W?~A??~A~D?~A??~A~Y?~@~B?~B~B?~A~F?~A~W?~A??~B~I?~A~O?~L?~A??~A??~A~K?~B~I?~B??~B??~B??~B??~A~W?~A??~A~O?~A| ?~A~U?~A~D'); 18 } 19 ?> 20 <html> ====== 以下省略 =============== xx </html>
IDにNULLはダメだよ!!というエラーが出てきている
上記、var_dump()
の結果が下記でした。
object(PDOException)#5 (8) { ["message":protected]=> string(80) "SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'id' cannot be null" ["string":"Exception":private]=> string(0) "" ["code":protected]=> string(5) "23000" ["file":protected]=> string(30) "/var/www/html/todo/adduser.php" ["line":protected]=> int(13) ["trace":"Exception":private]=> array(1) { [0]=> array(6) { ["file"]=> string(30) "/var/www/html/todo/adduser.php" ["line"]=> int(13) ["function"]=> string(7) "execute" ["class"]=> string(12) "PDOStatement" ["type"]=> string(2) "->" ["args"]=> array(1) { [0]=> array(4) { [0]=> string(4) "test" [1]=> string(4) "test" [2]=> string(15) "test@example.jp" [3]=> string(14) "black_bird.png" } } } } ["previous":"Exception":private]=> NULL ["errorInfo"]=> array(3) { [0]=> string(5) "23000" [1]=> int(1048) [2]=> string(26) "Column 'id' cannot be null" } }
一部抜粋
"Column 'id' cannot be null"
なるほど。ID列にNULL
を渡しているからエラーになっていのか。
11 $sql = 'INSERT INTO users VALUES(NULL, ?, ?, ?, ?, 0)'; 12 $sth = $dbh->prepare($sql);
確かに、11行目のINSERT句を見てみるとVALUESにNULLを渡していそうな匂いがしますね。
試しにNULL以外を明示的に指定してみる
試しにNULLと記述されている部分に明示的に数値を固定で割り当ててみます。
11 $sql = 'INSERT INTO users VALUES(11, ?, ?, ?, ?, 0)'; 12 $sth = $dbh->prepare($sql);
実行結果
できた!!!
もちろん、ユーザー登録後はTODOアプリケーションにログインをすることもできます。
マイページをみてみると、固定で割り当てた11
がユーザーのIDとして割り当てられているのも確認できました。
※赤枠内のid
が11
になっていることを確認。
http://example.jp/todo/mypage.php?rnd=607bfe5917cd6&id=11
これでユーザーアカウントの登録と登録したユーザーによるログインができました。一安心。
まとめ
久々にPHPを読みました。
var_dump()
って今でも使われている関数なんですかね?
5年くらい前に教わったPHPの古いデバック手法を引きずっている気がする。
とはいえ、なんで本事象が起きたのですかね? ソースコードでは明示的にNULLが渡されていたので、解決はできたのですが腑に落ちません。NULLが渡されたら自動的に採番されるような仕組みがうまく動いていないとか??うーん、、、
私のVM特有の事象でしょうか。なんか軽くGoogleで調べたのですが、同じような事象にあった人を確認できず、公式サイトを見ても同じような事象について記述がなかったため、PHPファイルを読みましたが、もし何か私のPHP側の設定忘れとかであれば非常に申し訳ないです。
何はともあれ、明日以降引き続き脆弱性診断入門を進めていこうと思います。