セキュリティ_基本、攻撃手法など(PerfectPHP 9章 前編)
なぜセキュリティなのか。
例えば
弱いサイト作る→悪人がサイト改ざん→閲覧→勝手にリダイレクトされて攻撃される。
結果
損害賠償なんてことも。
■基礎知識
妥当性チェック(バリデーション)
→想定外の値が渡されたら処理中断、エラー表示をさせる。
例
必須入力チェック、数値や日付チェック、有効範囲(最大値など)チェック、
除外(null、不正文字列など)チェック
エラーメッセージ
→エラーであることがわかるメッセージだけを表示するべき。
親切すぎるエラーメッセージはだめ。危険。
×「パスワードが間違っています」
○「メアドもしくはパスワードが間違っています」
権限設定
→PHPのWEBアプリケーションは基本Apacheモジュールとして動作させるのが一般的。
仮にApacheの実行ユーザがrootだとrootで動くので何かあった時危険。
■攻撃手法
・スクリプト挿入攻撃(script insertion)
脆弱性あるサンプルコード(スクリプト挿入攻撃) - MyController
→コンテンツ内に悪意あるスクリプトを直接埋め込んで攻撃する。
※ユーザから投稿を許可している場合特に注意。(掲示板、ブログコメントなど)
・悪意あるサイトへのリダイレクト
・cookie情報をとられてサイトの管理者権限を乗っ取られる。
例
getメソッドでURLに~~.php?id=1ある場合などで、
javascriptコードを書かれるとコードが実行されてしまうため危険。
↓
対策
htmlspecialchars()関数を使うことで無害化。
更に第二引数にはENT_QUOTESを指定するべき。
というか第二引数以降は、、
絶対これにする→htmlspecialchars($comment["text"], ENT_QUOTES, 'UTF-8');
→指定しないとシングルクォートはエスケープされなくなる為。
※自分で初期化した変数以外は、基本全ての変数にはこれで対策したほうがベター。
脆弱性あるサンプルコード(スクリプト挿入攻撃) - MyController
複数のサイトを横断させたから→クロスサイトと呼ばれる。
リンクを踏ませるなど間接的な方法で悪意あるスクリプトを実行させる攻撃のこと。
スクリプト挿入攻撃と基本は同じくユーザ投稿型で特に注意。
スクリプト挿入攻撃との違い
→XSSは間接的(攻撃対象と異なるサイト、メールなどを介する)であるという点。
例
他のサイトからリンクが貼られていて、リンクURLにjavascriptコードなどを埋め込まれると悪意あるサイトにリダイレクトされてしまうなど。
↓
対策
同上。スクリプト挿入と同じくhtmlspecialchars()にてエスケープする。
正規の権限を持つユーザに対して、意図しない操作を行わせること。
ユーザ自身に操作させるため、権限チェックだけでは防げない。
例
サイト管理者のみ記事削除可能な場合、
管理者としてログイン→攻撃用のサイトへアクセスしてしまうと・・・
→正規の権限で記事が削除される。
↓
対策
ワンタイムトークンによるチェック
投稿・編集・削除等の時はパスワード認証をさせる
※2つを併用するのが望ましい。
※ワンタイムトークンの注意点
タブブラウザで同じページを複数回開いた場合、最新タブ以外は不正なトークン扱いになるので利便性は低下。
対策
トークンを保持するセッション変数を配列型にして、トークンチェック時にin_array()などを使うことで対応可能。
ただし、無制限にトークンを保持するよりはトークン保持数を制限したり有効時間を決めたりした方が良い。
※ 追記
PerfectPHPでこう書いてあったが、どうもワンタイムトークンは必要ないようだ。
理解を深めるために下記がいい。
高木浩光@自宅の日記 - CSRF対策に「ワンタイムトークン」方式を推奨しない理由, hiddenパラメタは漏れやすいのか?
結論、
PHPのセッション強度をあげることで対応すればよいぽい。
PHPのセッションID = 「現在時刻」+「IPアドレス」+「乱数」
php.iniの設定変更でさらに強度を上げることができる。
①hash_function:ハッシュ化するアルゴリズムを指定できる
推奨:SHA512
(開発環境はMD5)
②session.entropy_file:追加するランダムな値を指定できる
(初期設定では/dev/urandomが設定されているがコメントアウトされている)
/dev/urandom:乱数を生成してくれる擬似デバイス
③session.entropy_length:entropy_fileから読み込むバイト数を指定できる
(初期設定ではコメントアウトされている)
DBに対する命令文を改ざんして意図しない操作を行うこと。
DBの内容をとられたりすると被害が甚大なので確実に防衛しよう。
例
select * from users where name='ユーザ名' and pass='パスワード'という文がプログラム中にあった場合。
ユーザ名:admin
パスワード:'OR '1' = '1
上記入力すると ユーザがadminでパスが空白、
もしくは1と1が等しいユーザ情報を取得する問い合わせとなってしまうので危険。
↓
対策
プリペアドステートメントを利用。
→値と置き換える為のプレースホルダを含んだSQLを事前に準備して、
プリペアド=準備済み の意味。
※環境によってプリペアドステートメントを利用することが出来ない場合は、
DBエンジンに合わせたエスケープ関数を実行する。
pg_escape_string()関数
mysql_real_escape_string()関数など。
PDO(PHP Data Objects)の利用をおすすめ。
※PHP5.1.0以降だとデフォルトでPDOが有効。DBエンジンを意識せずに共通のAPIでDB操作可能となる。
プリペアドステートメントについてもう少し詳しく。
脆弱性のあるコード例
$name = $_POST['name'];
$sql = "INSERT INTO user(name) VALUES('" . $name . "');
これエスケープされてないので下記のようにするのが望ましい。
$sql = 'INSERT INTO user(name) VALUES(:name);
$stmt = $con->prepare($sql); //PDOクラスのprepare()メソッド実行でPDOStatementクラスのインスタンスになる。
$params = array(':name' => $_POST['name']);
$stmt->execute($params); //PDOStatementクラスのexecute()メソッド実行で、クエリがDBに発行される。引数にはプレースホルダを指定。
:name というのは動的パラメータが入るという指定のこと。プレースホルダとよばれる。
■セッション固定攻撃
任意のIDを強制的に利用させること。
→セッションハイジャックを成立させる手段になってしまうので、
セッションを使う際にはセットで対策が必要。
例
正規のユーザ以外ログインユーザ専用ページは表示されないように見えるページがあるとする・・・・
セッションIDが分かっていてURLの後ろに付与してアクセスするとまたログイン画面が出てくる。
次にクッキーを削除してもう一度URLの後ろに付与してアクセスすると今度はログインユーザ専用ページが表示されてしまう。(これがセッションアダプション)
※ここまでを正規のユーザが行ってしまうと、攻撃者はこのセッションIDを取得して正規のユーザ権限で不正な操作を行う(これがセッションハイジャック)
分かり辛い場合はperfectPHP P353とかを参照。
セッションアダプション
自身が発行したものではないセッションIDを受け入れてセッションが初期化されてしまう。
Cookie Monsterバグ(クロスドメインクッキーインジェクション)
クッキーに意図するIDを埋め込んでセッション固定攻撃を仕掛ける。
古いブラウザの不具合を利用して「.co.jp」や「.gr.jp」などのセカンドレベルドメインに対して任意のクッキーをセットする攻撃方法。
※最新のブラウザなら基本対応されてるが一応覚えておいて損はなし。
対策
ログイン時の認証成功した段階でセッションIDの再発行を行う。
session_regenerate_id()関数が使える。
何らかの方法で取得したセッションIDを使って正規ユーザのセッションを乗っ取る。
クレジットカード使われたり、個人情報をとられたりしちゃう。
これも確実に防ぐ必要がある。
主な攻撃者の手口
・リファラによる漏えい
→PHPのデフォルトではクッキー利用するが、利用出来ない場合はGET、POST変数が利用されるので注意。
・XSSによるセッションID入手
→リダイレクトさせて盗んで、元のサイトにリダイレクトさせちゃえばユーザが気づく事もないので怖い。
・セッション固定攻撃によるセッションIDの強制
→もろいサイトに対して指定したセッションIDを含むリンクを貼っておくだけで、
あとは勝手にリンクを踏んでサイトに移動後、ログインを待ってればセッションIDがとられてしまう。
対策
XSS、セッション固定攻撃に対して対策した上で更に
・セッションIDをクッキーのみで扱う
→session.cookie_httponly=1 にするなど。
・セッションハイジャックのチェック
→Accept-Charset 、 Accept-Language、User-Agentは通常セッション途中で変更されないのでこれを元にチェックすると多少はリスク低下できる。
・パスワード入力による多重チェックの実装
→重要な作業前に再度パスワードチェックさせる。ユーザビリティ低下はする。
他にも細かく具体的に攻撃方法が書いてあるいいサイトがあった。