綺麗に死ぬITエンジニア

PHPでの最低限のセキュリティ対策のやり方(後編)

2016-01-22

PHPにおける基本的なセキュリティ対策の方法について、備忘録としてまとめます。

PHPでこれからWebアプリケーションやWebサイトを作成しようと考えている方は、各項目について対策は万全か、是非一度ご確認ください。

本記事(後編)では、以下に関する脆弱性について取り扱います。

  • クロスサイトリクエストフォージェリ(CSRF)
  • HTTPヘッダインジェクション
  • メールヘッダインジェクション
  • クリックジャッキング

以下の脆弱性については、前編の記事をご覧ください。

  • SQLインジェクション
  • OSコマンドインジェクション
  • ディレクトリトラバーサル
  • セッション管理の不備
  • クロスサイトスクリプティング(XSS)

参考サイト

安全なウェブサイトの作り方:IPA 独立行政法人 情報処理推進機構

クロスサイトリクエストフォージェリ(CSRF)

悪意のある外部サイトによりユーザーに意図しない操作をさせることが可能な状態となっている、「クロスサイトリクエストフォージェリの脆弱性」を淘汰するために、PHPで必要とされる処置は以下のとおりです。

処理を実行する際、「処理を要求したリクエストのReferer」「処理を要求する際に求めたユーザーのパスワード」「処理を要求するページ上に動的生成されたキーワード(POST:hiddenパラメーター)」のいずれかが正しいかを確認し、正しい場合のみ処理を実行する(根本的解決)

悪意のある外部サイトを経由した不正な処理要求の場合は、直前の処理を要求するためのページ(フォーム)が正常ではないことを意味するので、直前のページ(フォーム)が正しいものかを確認する必要があります。

そのためには、「処理を要求したリクエストのReferer」「処理を要求する際に求めたユーザーのパスワード」「処理を要求するページ上に動的生成されたキーワード(POST:hiddenパラメーター)」のいずれかが正しくある必要があります。

「処理を要求したリクエストのReferer」を確認する場合は、処理の前で$_SERVER['HTTP_REFERER']に正しい値が格納されているかを確認します。

「処理を要求する際に求めたユーザーのパスワード」を確認する場合は、パスワードを入力させるフォームを作成し、処理の直前でその値がユーザーのパスワードと一致するかを確認します。

「処理を要求するページ上に動的生成されたキーワード(POST:hiddenパラメーター)」を確認する場合は、入力フォームにてhiddenで動的に乱数を生成し、処理の直前でその値が正しいものを返しているかを確認します。

重要な処理を行った際に、その旨を登録済みのメールアドレスに自動送信する(保険的対策)

意図しない処理が実行されてしまったことをユーザーに認識させるために、重要な処理が行われる度にユーザーに通知する仕組みを実装します。

HTTPヘッダインジェクション

ユーザーによって開発者の意図しないHTTPヘッダの出力が可能な状態となっている、「HTTPヘッダインジェクションの脆弱性」を淘汰するために、PHPで必要とされる処置は以下のとおりです。

header関数を利用してHTTPヘッダを生成する(根本的解決)

HTTPヘッダを生成する場合は、必ずheader関数を用いるようにし、それ以外の方法での出力をしないようにすることで回避できます。

<?php
// PDFを出力します
header('Content-Type: application/pdf');

// downloaded.pdf という名前で保存させます
header('Content-Disposition: attachment; filename="downloaded.pdf"');

// もとの PDF ソースは original.pdf です
readfile('original.pdf');

ヘッダに使用する文字列に改行コードを許可しないよう、処理を実装する(根本的解決)

HTTPヘッダは改行ごとに1要素とされ、開発者の意図に反してHTTPヘッダに定義される文字列内に改行コードを挿入されることにより脆弱性が発生するため、HTTPヘッダとして出力する文字列には改行コードを許可しないバリデーション処理を自身で実装します。

メールヘッダインジェクション

ユーザーによって開発者の意図しないメールヘッダの出力が可能な状態となっている、「メールヘッダインジェクションの脆弱性」を淘汰するために、PHPで必要とされる処置は以下のとおりです。

メールヘッダにユーザーの入力変数を用いない(根本的解決)

そもそも、ユーザーからの入力値をメールヘッダに含めるような実装をやめることで回避することができます。

ヘッダに使用する文字列に改行コードを許可しないよう、処理を実装する(根本的解決)

どうしてもヘッダに変数を用いなければならない場合は、改行コードを受け付けない、もしくは改行コードを削除する実装を追加します。

<?php
$from = str_replace(PHP_EOL, '', $from);

mb_language('ja');
mb_internal_encoding('UTF-8');                        
mb_send_mail('example@example.com', 'Title', 'Body',"From: {$from}");

クリックジャッキング

悪意のある加工された外部サイトにより利用者が意図しない機能を実行させられる、「クリックジャッキング攻撃」を回避するために、PHPで必要とされる処置は以下のとおりです。

HTTPレスポンスヘッダにX-Frame-Optionsヘッダフィールドを出力し、他ドメインのサイトからのframe要素やiframe要素による読み込みを制限する(根本的解決)

HTTPレスポンスヘッダにX-Frame-Optionsヘッダフィールドを出力し、他ドメインのサイトからのframe要素やiframe要素による読み込みを制限する記述を、HTMLのドキュメント出力前に実行します。

<?php
header('X-FRAME-OPTIONS: DENY');

まとめ

本記事では、

  • クロスサイトリクエストフォージェリ(CSRF)
  • HTTPヘッダインジェクション
  • メールヘッダインジェクション
  • クリックジャッキング

について、PHPにおける具体的なセキュリティ対策を述べていきました。

よろしければ、前編の記事もご覧ください。