PHPでセキュリティ対策する方法
※当サイトの記事は、プロモーションを含む場合があります。

クロスサイトスクリプティング(XSS)対策

XSSの基本はhtmlspecialchars()を使う

クロスサイトスクリプティング(XSS)に対して脆弱なサイトは、POSTやGETの値を直接データベースに入力していることが問題です。
入力値に演算子やタグになるような要素があると危険です。

これを回避するには、以下のように記述します。
$str=$_POST[‘email’];
$str = htmlspecialchars($str);

htmlspecialchars()で改善されますが、それだけだと「’」や「”」が使用されたままなので、エンコードする必要があります。

htmlspecialchars()でENT_QUOTESを使わないほうがいい。その代わりにaddslashesを使う

そこで、ENT_QUOTESを追加してシングルクォート「’」を'に変換する方法がネットでよく紹介されていますが、これには問題があるそうです。

なぜなら、'はJacascriptにてやりようによってはすり抜けることが可能だからです。

そのため、ENT_QUOTESを使わない方法で「\」「NULL」「”」「’」をエスケープするにはhtmlspecialchars()の次に以下の文を追加すると良いです。
$str = addslashes($str);

念のため、$strにて以下の単語をはじくようにしておくとさらに良いでしょう。

・html内の命令(javascript含む)
javascript:
cookie
iframe
href=
src=
background
onsubmit
action

・URIスキーム
http:
https:
clsid:
data:
ftp:
feed:
file:
tel:
mailto:

参考URL:

・addslashesを使う
http://phpspot.org/blog/archives/2008/01/phphtmlspecialc.html

・strip_tags()よりhtmlspecialchars()のほうが良い
http://www.asahi-net.or.jp/~wv7y-kmr/memo/php_security.html

・エスケープしても表示されてしまう例
http://d.hatena.ne.jp/ockeghem/20070511/1178899191

・はてなダイアリーXSS対策
http://hatenadiary.g.hatena.ne.jp/keyword/%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%80%E3%82%A4%E3%82%A2%E3%83%AA%E3%83%BCXSS%E5%AF%BE%E7%AD%96

クリックジャッキング対策

iframeによってクリックジャッキングへ誘導するサイトから守るにはiframeを表示させないことが重要です。

しかしながら、iframeを使いたい場合もありますので、以下のようにケースバイケースで使用すると良いです。

サイト内でiframeを全く使えないようにする

PHPのソースのヘッダー部に以下の文を追加します。
これは、あらゆるiframeの表示を禁止することになります。
header(‘X-FRAME-OPTIONS: DENY’);

サイト内で同一ドメインに限りiframeを使えるようにする

PHPのソースのヘッダー部に以下の文を追加します。
これは、同一ドメイン以外ではiframeの表示を禁止することになります。
header(‘X-FRAME-OPTIONS: SAMEORIGIN’);

CSRF対策

まず、IT Proさんに記述されていた方法をピックアップします。

その方法では、ユーザーログイン直後にセッション変数にトークンが無い場合、dev/urandomを使ってできるだけ真の乱数に近い疑似乱数を発生させ、BASE64エンコードでトークン$tokenを作成します。

このトークンの値$tokenをセッション変数$_session(‘token’)に格納しておきます。

フォームにてセッション変数の値$_session(‘token’)をhiddenタグにて送信します。

フォームをPOSTで受け取る場合、フォームの送信値と$_session(‘token’)とを比較すればログインユーザーからの送信であることがわかります。

ただし、この方法ではセッション変数にトークンが無い場合にしかトークンを生成していません。セッションを終了しない限りトークンは同じままです。

これだと、セッションIDを盗まれた場合、トークンまで盗まれてしまう可能性があります。

また、formの送信後の画面では「送信終了の表示」と同時に「送信処理」をすることがありますが、firefoxなどでブラウザをしばらく離れて有効期限を迎えた後、再度ブラウザのタブをクリックすると、セッション変数は残っているのにinputフォームでのデータは消えている、ということがあります。
そして、そのデータで送信されてしまう可能性があります。

このようなことを回避するためには、上記の「セッション変数にトークンが無い場合に限ってトークンを生成する」のは良い方法とはいえないです。

そこで、フォームを表示する度にトークンの値をワンタイムで変えたほうがさらにセキュリティが増します。また、トークンの有効期限があると尚良いです。

参考サイト
http://itpro.nikkeibp.co.jp/article/COLUMN/20130218/456764/?ST=security&P=2

参考サイト
http://gihyo.jp/dev/serial/01/php-security/0023

画像アップロードの対策

画像アップロードを許可するサイトでは以下の対策をします。

画像URLについて

ファイル拡張子をbmp以外の画像ファイル(png jpg jpeg gif)に限定します。

・WEBで公開することになる画像URLの部分では半角英数、アンダーバー、ハイフンに限定します。

・画像ファイルの中身は画像ファイルであっても、上記のXSSで述べたURIスキームを排除すると良いでしょう。また、以下のサイトが参考になります。

・ファイルをアップロードするサーバを別サーバーにする方法があります。これだと画像表示は別ドメインになりますが、データベースにアクセスされることは無くなりますので安全性が高まります。

JPEGのEXIF

JPEGのEXIFを削除しておきましょう。

・jpegのEXIFはImagick拡張で消す方法がありますが、レンタルサーバー等のサーバー側で設定できない環境では使用できません。

・PELなどのライブラリーを使うとEXIFを消せるらしいですが、少し敷居が高いようです。

画像をPHPでリサイズするとEXIFを消せます。(すべて消去できているかどうかは未確認です。)

参考サイト

http://d.hatena.ne.jp/teracc/20070715#1184515587
http://www.tokumaru.org/d/20071210.html

画像のヘッダ情報(バイナリデータ)
http://www.yomotsu.net/wp/?p=504

マジックバイトの抽出方法
http://into.cocolog-nifty.com/pulog/php/index.html

JPEGのEXIFヘッダの脆弱性の回避
http://qiita.com/stk2k/items/e13ae6670e7482d4c630

バイナリファイルの中身を文字列に格納する場合はfile_get_contents()を使うほうが良い
http://www.php.net/manual/ja/function.fread.php

PHPでバイナリプログラミング その1