はじめに
本記事は、下記書籍を読んだ所感などをまとめたものです。
(以下本書)
読み終わった感想
もっと早く読めばよかった……
すべてのWebアプリケーション開発者におすすめできます!
Webアプリケーションにおける脆弱性や攻撃手法、そして対策などを体系的に学ぶことができました!
今後、新たにWebアプリケーションを開発する際に役立てたいと思います。
もし未読の方がいらっしゃいましたら、ぜひお手に取っていただくことをおすすめします!
学んだこと、心に残った箇所など
それではここからは、本書を読み進める中で学んだことや、心に残った箇所などを記載します。
なぜ脆弱性があるといけないのか?
そもそも、なぜ脆弱性があるとダメなのでしょうか?
「脆弱性?」
「うーん」
「もし何かあったとしても、利用者に補償すればいいんでしょ?」
などと、軽くとらえてはいけません!
あなたのWebアプリに脆弱性があってはならない理由は、以下の通りです。
経済的損失
- 利用者への金銭的補填
- 社会的信頼・売上の減少
- 損害賠償
法的な要求
- 個人情報保護法
利用者への、回復不能なダメージ
- 一度流出した個人情報は、回復不可能。
- 利用者の不安や苦痛は、お金では解決できない。
攻撃インフラの構築を助けてしまう
- 迷惑メール送信やDos攻撃などのインフラになってしまう
個人的に、最後の「攻撃インフラの構築を助けてしまう」というのが心に刺さりました。
犯罪行為の片棒を担がないように、しっかりと知識をつけようと決意しました。
クッキーを盗まれるとどうなるのか?
クロスサイト・スクリプティング攻撃などによってクッキーが盗まれると、どうなるかというと……
他ユーザーへのなりすましが可能になります!!
クッキーには、「セッションID」が含まれます。
セッションIDが盗み出せれば、なりすましができるようになります。
なりすましをされると、例えば以下のような攻撃が想定できます。
-
Twitterなどで勝手にツイートができる
- 罠サイトのリンクを貼ったりも可能
-
迷惑メールを送信できる
- メールサービスにログインされると、あなたが迷惑メールの送信者となってしまいます
-
通販サイトで買い物される
- これほんとこわい
対策としては、外部からの入力には注意することです!
- まずはエスケープ
- 厳密なバリデーションチェック
JavaScriptの動的生成
上記のように、フォームなどで外部からの入力を受け付ける場合は要注意です。
JavaScriptなどにより攻撃されてしまいます!
(というより、攻撃されることを前提として開発しましょう)
入力値は適切なエスケープを行うようにします。
具体的には、以下の記号は必ずエスケープします。
エスケープ必須の記号
- \
- '
- "
- 改行
- <
- >
エラーメッセージは画面に表示しないようにする
エラーメッセージとは、Laravelでいう以下のような表示のことです。
なぜエラーメッセージを表示してはいけないかというと、以下の理由からです。
-
エラーメッセージには、アプリケーションの内部情報が含まれるから
- 攻撃者に有利な情報を与えてしまう
-
意図的な攻撃として、エラーメッセージに個人情報などを表示させられるから
- SQLインジェクションなど
Webアプリごとに専用のエラー画面を用意しておきましょう!
SQLインジェクションの恐怖
SQLインジェクションとは、「SQLの不備に関連する脆弱性」です。
SQLインジェクションがあると、以下のような攻撃を受ける可能性があります。
SQLインジェクションにより発生する攻撃
- データベース内の情報が盗まれる
- データベース内の情報が書き換えられる
- 認証を回避される
- データベースサーバー上のファイルへが読み込み、書き出し、実行される
おそろしい……!!
対策は後ほど記載します。
SQLの静的プレースホルダーと動的プレースホルダーの違い
プレースホルダーとは、SQLに動的な値を組み込む記述方法です。
// SQLのプレースホルダーを指定
$sql = 'SELECT * FROM users where name = :name';
// SQLをプリコンパイル
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をバインド
$stmt->execute([
':name' => $name,
]);
このバインドを……
- データベースエンジン内で行う → 静的プレースホルダー(推奨!)
- Webアプリ内で行う → 動的プレースホルダー(十分安全だが、できれば静的にしたい)
というわけで、まずはプレースホルダーを使用しましょう(must
)。
可能であれば静的プレースホルダーを設定しましょう(want
)。
SQLインジェクションへの対策
SQLインジェクションへの具体的な対策は、以下の通りです。
SQLインジェクションへの対策
- エラーメッセージを詳細に表示しない
- php.iniの設定を以下のようにします
display_errors = off
- 入力値の検証
- 可能な限り、文字列は入力させない
- プレースホルダーを使用する
- データベースの権限設定
- Webアプリのデータベースユーザの権限を必要最小限にする
- 検索ページであれば、参照権限のみにするなど
- Webアプリのデータベースユーザの権限を必要最小限にする
CSRFへの対策
クロスサイト・リクエストフォージェリ(CSRF)とは、罠サイトなどを利用して「勝手に重要な処理を実行する」ことをいいます。
重要な処理の例は以下の通りです。
- 利用者のクレジットカードでの決済
- 利用者の口座からの送金
- パスワード変更
- 個人情報変更
CSRFへの対策は以下の通りです。
利用者の意図したリクエストであることを確認する
- トークンの埋め込み(フレームワークに搭載されている機能を使用)
- パスワード再入力(正規利用者であることを確認)
- Refererのチェック(直前のページに関する情報を検証する)
なお、CSRF対策はすべての画面に必要というわけではありません。
CSRF対策が必要な画面は以下の通りです。
- 重要な処理を行う画面
- パスワード変更
- マイページ
- 個人情報編集
- 商品の決済
セッション管理の重要性
セッションIDは、第三者が推測または盗み出せないようにする必要があります。
大抵のフレームワークにはセッション関連の機能があらかじめ備わっているはずなので、できるだけそちらを使用するようにしましょう!
もしもプレーンなPHPでセッションを管理する場合は、php.iniの設定を以下のように見直しましょう。
[Session]
session.entropy_file = /dev/urandom
session.entropy_length = 32
オープンリダイレクトへの対策
オープンリダイレクトとは、外部からURLを指定してリダイレクトさせる攻撃方法です。
フィッシング詐欺でよく用いられる攻撃方法ですね。
例えば、攻撃者はスパムメールなどで、以下のようなURLをクリックさせます。
https://example.jp?url=http://trap.com/trap.php
オープンリダイレクトへの対策をしていない場合、利用者は罠サイトを訪問していることに気づかずに操作してしまうかもしれません!
対策は以下の通りです。
オープンリダイレクトへの対策
- リダイレクト先のURLを固定にする
- リダイレクト先のURLをホワイトリスト管理する
- リダイレクト先のドメインをチェックする
OSコマンドインジェクションへの対策
Webアプリ内で実行されるOSコマンドを悪用する攻撃をOSコマンドインジェクションといいます。
OSコマンドを悪用されるということは、OSを乗っ取られるようなものなので、なんでもできてしまいますね……。
本書では、メールアドレスを入力するフォームに関する脆弱性が詳細されています。
$mail_address = filter_input(INPUT_POST, 'mail_address');
system("/usr/sbin/sendmail -i < mail_body.txt $mail_address");
上記の処理に、↓の文字列を渡すとどうなるでしょうか?
bob@example.jp;cat /etc/passwd
そうです。
catコマンドが実行され、/etc/passwdファイルが画面に表示されてしまいます
OSコマンドインジェクションへの対策は以下の通りです。
OSコマンドインジェクションへの対策
- OSコマンドを使わない実装を検討する
- メール送信であれば、mb_send_mail関数を使う
- 外部から入力された文字列をパラメータに渡さない
- 文字列をエスケープする
DoS攻撃への対策
DoS攻撃(Denial of Service attack)とは、Webアプリケーションに対し大量のデータを送信する攻撃方法です。
サイトの応答速度が低下したり、サーバーの停止といった影響があります。
DoS攻撃に対しては、「アップロードファイルの容量を制限する」という対策が有効です。
PHPであればphp.iniの設定を見直し、できるだけ小さな値にしておくとよいでしょう。
クロスサイト・スクリプティングへの対策
クロスサイト・スクリプティングとは、ファイルをダウンロードした際に、悪意のあるスクリプトを実行させる攻撃手法です。
簡単な流れは以下の通りです。
① まず攻撃者が、悪意のあるスクリプトファイルexample.pdf
をWebアプリ上にアップロードしておきます。
example.pdf
は、見た目はPDFですが、中身はスクリプトファイルです。
② 続いて他のユーザーが、example.pdf
をダウンロードします。
(このとき、ユーザーはスクリプトファイルだと気づいていません)
③ ダウンロードが完了すると、ユーザーのPC上で悪意あるスクリプトが実行されます!
example.pdf
がPDFではないと気づいた時には、個人情報が抜き取られていた……。
といったイメージです!
このクロスサイト・スクリプティングへの対策は以下の通りです。
クロスサイト・スクリプティングへの対策
- ファイルのContent-Typeをチェックする
- PDFであれば
application/pdf
- 拡張子をチェックすることも大事
- PDFであれば
- WEBサーバーのレスポンスヘッダ設定を見直す
- Apache
Header always append X-Content-Type-Options: nosniff
- nginx
add_header X-Contents-Type-Options: nosniff;
- Apache
JSONPは取扱注意!
JSONP(JSON with padding)は、他のオリジンサーバーからJSONデータを取得するための仕組みです。
JSONPの使用例としては以下のような記述が挙げられます。
$call_back = $_GET['callback'];
$json = json_encode(['time' => date('G:i')]);
echo "$callback($json)";
JSONPはとても便利な仕組みですが、使い方を間違えると脆弱性となりえます!
JSONP使用時は、JSON文字列を適切にエスケープしましょう!
APIレスポンスにはMIMEタイプを指定する
MIMEタイプの指定を怠ると、脆弱性となります。
例えば、以下のようなWebAPIがあったとします。
$zip = $_GET['zip'];
echo json_encode(["message" => "郵便番号が見つかりません: " . $zip);
本来ならばMIMEタイプとしてapplication/json
であるべきですが、指定を怠っています。
このとき、レスポンスはPHPデフォルトのapplication/html
となります。
MIMEタイプとしてapplication/html
が指定されているため、下記のリクエストを送るとJavaScriptが実行できてしまいます!
https://example.jp?zip=<img+src=1+onerror=alert(document.domein)>
上記リクエストを実行すると、レスポンスでJavaScriptのalert
メソッドが実行できてしまいます。
APIレスポンスには、MIMEタイプを指定しましょう!
レインボーテーブルの恐怖
現代においては、パスワードのハッシュ値を一覧にまとめた表が存在します。
この表をレインボーテーブルといいます。
レインボーテーブルにより、8文字までのMD5によるハッシュ化は短時間で解読可能です。
もはやMD5は安全ではありません!
レインボーテーブルへの対策
- パスワードを長くする(20文字)
- ソルト(salt)を使用するとよい
- ソルトは、元データに追加する文字列のこと
- ソルトにより、見かけ上はパスワードを長くできる
データベースの文字コードは統一する
データベースは、以下の箇所で文字コードを指定できます。
文字コードを指定できる箇所
- レコード作成時
- レコード更新時
- データベースエンジンとの接続時
常に正しい文字コードを指定するようにしましょう!
UTF-8
かUTF-16
が推奨されています。
データベースの文字コードが統一されているかを判定するために、以下のテストを実行することをおすすめします。
尾骶骨(びていこつ)テスト
尾骶骨
という3文字をデータベースに登録し、画面に表示するテスト。
きちんと尾骶骨
と表示されていればOK!
尾〓骨
、尾 骨
などと表示された場合は、途中で文字コードがShift-JISなどに変わっている可能性があります。
同様に、「𠮷(つちよし)テスト」もおすすめです。
𠮷
という文字を登録し、画面に表示するテストです。
画面に𠮷
が表示されればOKです!
みなさまも、もし機会があればお試しください
終わりに
今回は、「安全なWebアプリケーションの作り方」を読んだ所感などをお伝えしました。
セキュリティに関して、体系的に学べる良書だと感じました!
もし未読の方がいらっしゃいましたら、ぜひお手に取っていただくとよいと思います!
それでは、ここまで読んでいただきありがとうございました