アプリケーションをクラックする攻撃方法知っていますか?
本投稿記事では実際にハッキングの方法、いわゆる攻撃方法をあえて紹介します。
攻撃方法を知る事で、防御する方法、セキュアなコーディングが出来るようになります。

私自身はPHPが危険とは思いませんが、最も分かりやすいと思いPHPで紹介しています。
セキュリティには気をつけている方も、フレームワーク任せな方も一度備忘録としてご活用下さい。

ディレクトリトラバーサル攻撃

例えばテンプレートファイルを読み込む場合等、プログラムからローカルファイルにアクセスしたい事って有りますよね?
決まったローカルファイルにアクセスするのであれば、問題ないですが変数を受け取って読み込む場合に危険が有ります。

echo file_get_contents("path/to/". $_GET["file"]);

コードにしてみると何が起きてしまうか分かり易いですね。
例えば/?file=../../etc/passwdとブラウザに打ち込んでいけば、権限によっては/etc/passwdが表示されてしまいます。
無理やり日本語に直せばディレクトリ遡り攻撃と言われ、意図しないファイルアクセスを許してしまう脆弱性です。

ディレクトリトラバーサル攻撃の防御手段
basename関数で受け取ったパラメータをサニタイズ(無害化)してあげる必要があります。

echo file_get_contents("path/to/". basename($_GET["file"]));

※basename関数はPHP5.0よりバイナリセーフとなっています。

NULLバイト攻撃

先ほど同様に任意のローカルファイルにアクセスしたい場合ですが、少しセキュアになったコードの例です。

echo file_get_contents("path/to/". $_GET["file"] .".php");

一見同様に、/?file=../../etc/passwdとアクセスしたとしても、.phpが末尾に付くため
/etc/passwd.phpとなって攻撃者は重要なファイルにアクセスできないように思えます。

然し、クエリが/?file=../../etc/passwdとしてアクセスした場合、何と/etc/passwdが表示されてしまいます。
クエリに付けたNULLバイトというファイルネームの終端であると解釈されてしまうのです。

NULLバイト攻撃の防御手段
入力された値からNULLバイトを除去すれば安全にアクセスが可能となります。

echo file_get_contents("path/to/". str_replace(chr(0), '', $_GET["file"]) .".php");

SQLインジェクション攻撃

かなり有名な攻撃方法ですが、同様に変数入力を与える攻撃となります。
例えば、ログイン時に入力されたIDとPASSWORDから会員を探す処理を書くとします。

"SELECT * FROM `users` WHERE `id`= '{$_GET["id"]}' AND `pass` = '{$_GET["pass"]}'";

GETクエリに?id=' OR 1 = 1 OR '等と必ず真になる条件を書かれてしまえば、必ずログインされてしまう事になります。

PHPではMySQLのクエリ用に入力された値をエスケープする関数 mysql_real_escape_string が用意されています。
然しながらPHP5.5から非推奨となっており、PDO_MySQLを使うべきであるとアナウンスされています。

$st = $db->prepare('SELECT * FROM `users` WHERE `id` = :id;');
$st->bindValue(':id', $_GET['id'], PDO::PARAM_INT);
$st->execute();

入力された値を自動的にエスケープしてから置換してくれる優れ物です。
然し、入力されたタイミングでエスケープしたとしても、未だ危険性が潜んでいます。

セカンドオーダーSQLインジェクション攻撃

一旦入力値をサニタイズして安全にクエリが処理できたとしても、内なる危険性が残っている可能性が有ります。
皆さん入力される値は検証していても、既に入力済みの内部にある値は信頼してしまっていませんか?

例えば、IDとパスワードを自分で決められるユーザー登録機能を実装します。
ID : admin'--
PASS : password
という値を入力すれば、正しくエスケープをしても以下のようなSQL命令が走ると思います。

INSERT INTO `users` VALUES(, 'admin''--', 'password');

ここで、登録した攻撃者はパスワードを変更する機能を使った場合、以下のようなコードが有るとしましょう。

"UPDATE `users` SET `pass` = '{$_GET['pass']}' WHERE `id`='{$id}'";

このコードが解釈されると、以下の様なSQLになるはずです。

UPDATE `users` SET `pass` = 'newpassword' WHERE `id`='admin'--';

このSQLは何とID : admin'--ではなくID : adminのパスワードを変えてしまいます。
--はSQLではコメントの意味であり、その後の文章を無効化してしまいます。

セカンドオーダーSQLインジェクション攻撃の防御手段
システム内部から取得した値で有っても必ずサニタイズしなくてはいけません。
前項で触れたPDO_MySQLでのデータバインディングを使用する事を徹底すれば問題有りません。

ファイルアップロード攻撃

開発者の予期しないプログラムをアップロードされてしまう事で、例えばプロフィール画像をアップロードさせる為に
アップローダーが実装されているシステムは、沢山あると思います。

本当にアップロードされたファイルが画像ファイルであるか確認が必要です。
代表的なコードは$_FILESのMIME Type を確認する事だと思いますが、危険です。

if ($_FILES["file"]["type"] === "image/jpeg"){

基本的に$_FILESはユーザーが自由にリクエストに指定できてしまう為、PHPファイルを指定したとしても
HTTPリクエストの改造によってimage/jpegのPHPがアップロードされてしまう危険があります。
前項のディレクトリトラバーサル攻撃や設定次第で、アップロードされたPHPファイルが実行されてしまう恐れが有ります。

ファイルアップロード攻撃の防御手段
画像ファイルである事を検証する事が重要です。弊社の場合ではトリミングしないケースでも必ず ImageMaigck に通しています。

try {
  $adapter = new Imagick($_FILES["file"]["tmp_name"]);
  $adapter->writeImage("path/to/file.jpg");
}
catch (ImagickException $error){
  //画像ファイルではない可能性が高い
}

ImageMaigckで何もオプション指定せずにコンバートさせる事で、確実に画像ファイルである事が保証されます。
特に画像ファイルにXSSの脆弱性が潜んでいるケースも有り、同時にXSS脆弱性の無害化も実現できます。

画像スクリプトインジェクション攻撃

PHPではないのですが、画像ファイルにJavascriptを埋め込む事でXSS脆弱性を招いてしまう攻撃手法が有ります。

imagejs – Small tool to package javascript into a valid image file.
imagejs

imagejsは正しくgif画像ファイルにJavaScriptのコードを埋め込んでしまいます。
Google Chromeでは実行されませんでしたが、IE11ではJavaScriptが実行されてしまいました。
※因みに Microsoft Edge では実行されませんでした。

アップロードされた画像は前項と同じ様にコンバートする事でサニタイズしましょう。

セキュリティ対策

サーバー側のセキュリティ対策について攻撃方法を交えて説明させて頂きました。
肝心な事はユーザーが入力する値を信じず、必ずサニタイズしてしまう事が重要です。

特に、英数字しか受け付けないような入力だと分かっている場合にはフィルターを設けてしまう、
心配であれば Web Application Firewall を導入してミドルウェアで守ってもらう事も一つの選択技です。