PHPでウェブの世界と繋がろう!
menu
ホーム > PHPセキュリティー > 6 フォームとURL > 6-1 フォームとURLからの攻撃と防護策

6-1 フォームとURLからの攻撃と防護策

Pocket

説明

フォームとURL
PHP と HTML は密接に関係しています。PHP は HTML を生成し、HTML で PHPへ送信する情報を記述できます。HTML から PHP へ値を送る場合は、フォームを使用します。また、URL からも PHP へ値を送ることが出来ます。つまり、PHP は、フォームやURLから値を取得することが出来ます。フォームやURLから送られてくる値はユーザーが指定する入力値なので、汚染されている可能性があります。ここではフォームやURLを利用した攻撃方法を説明します。

セマンティックURL攻撃
POINT

  • セマンティックURL攻撃とはユーザーが直接URLを変更すること

セマンティックURL攻撃とは、ユーザーが直接URLを変更することです。
例えば、

http://www.tryphp.net/member.php?user=suzuki

とURLがなっている場合、suzuki の値を変更すれば他の結果が得られるかもしれないと値を変更することです。これがセマンティックURL攻撃となります。悪意がなくとも興味本位で値を変更する人もいるでしょう。
セマンティックURL攻撃が成功すると、URLの値を変更することによって、アプリケーションで意図しない情報が流出してしまいます

URLはPHPにとって入力であり、フィルタの対象であるとフィルタリングの項で説明しましたが、入力に対してしっかりとフィルタを行えば、セマンティックURL攻撃を知らなくとも、アプリケーションをこのような攻撃から防ぐことが出来ます。

フォームとセマンティックURL攻撃の例
次の例は、ユーザーがパスワードを忘れた場合、ユニークな質問とその答えを指定させ、照合後、指定したメールアドレスに新たなパスワードを送る場合にある脆弱性です。

① 例は、「ユーザーがパスワードを忘れた場合に、ユニークな質問に答えてもらい、照合後、指定したメールアドレス宛にリセットしたパスワードを送る」という仕組みにおけるセマンティックURL攻撃に対する脆弱性です。

照合のためユーザーにユニークな質問に答えてもらい、照合が完了した場合、次回から使用する新しい電子メールアドレスを入力してもらいます。そして、そのアドレスへ新たなリセットされたパスワードを送ります。

② question.php 最初のユニークな質問の答えを要求するフォーム

<form action="mailinput.php" method="GET">
ユーザー名を入力してください。<br/>
<input type="text" name="user" value=""><br/>
質問の答え 質問・・・・・・・<br/>
<input type="text" name="answer" value=""><br/>
<input type="submit" value="照合">
</form>
ユーザー名を入力してください。

質問の答え 質問・・・・・・・


③ mailinput.php 照合後リセットパスワードを送信するメールアドレスを入力するフォーム

<form action="reset.php" method="GET">
<!-- 照合に答えたユーザー名 -->
<input type="hidden" name="user" value="suzuki">
リセットパスワードを送信する電子メールを入力して下さい。<br>
<input type="text" name="mail">
<input type="submit" value="送信">
</form>


リセットパスワードを送信する電子メールを入力して下さい。


④ reset.php 照合→メール入力後、reset.phpでパスワードをリセットして、新しい電子メール宛にリセットパスワードを送信します。
ここでは、必要な情報(パスワードをリセットするユーザー名と送り先のメールアドレス)をすべて持っています。ユーザーのユニークな質問を照合しているので、このフォームにたどり着いた場合、そのユーザーは偽者でないと認められます。
mailinput.phpフォームをサブミットした後、reset.phpに遷移すると次のURLに到達します。

http://www.tryphp.net/reset.php?user=suzuki&mail=suzuki%40mail.com

⑤ ユーザーは試しに次のURLを入力します。 user=の値を変更する

http://www.tryphp.net/reset.php?user=yamada&mail=suzuki%40mail.com

reset.phpがこのURLを信用するとセマンティックURLの攻撃の脆弱性が発声します。
ユーザー yamada が存在する場合は、yamadaアカウントのための新しいパスワードが生成されてsuzuki@mail.comへ送られ suzuki が yamada アカウントを盗むことになります。

⑥ 脆弱性の回避
このような脆弱性を回避するために、SESSION変数を使用してフォームを追跡し、reset.phpが最後まで読み込まれたら、URLを変更して再リクエストできないようにさせたりすることで、アプリケーションの脆弱性を回避しましょう。

⑦ 回避の例
例えば、最初の質問入力フォーム question.php で

$_SESSION['form'] = 'start';

とSESSION変数に値を格納します。
そして、最後の reset.php で

if($_SESSION['form'] === 'start'){
//処理開始
}else{
//不正な処理です
}
//reset.phpファイルを最後まで読み込んだらSESSION変数にendの値を入力する
$_SESSION['form'] = 'end';

こうすることで、URLを変更して再リクエストできないようにします。これは1つの例です。

セマンティックURL攻撃の防御策
POINT

  • URLの値をフィルタリングする

セマンティックURL攻撃を防ぐには、URLの値を監視して、適切にフィルタリングすることです
セマンティックURL攻撃はアプリケーションがURLの値をチェックしないで処理を実行する結果発生します。URLの値をしっかりフィルタして、意図しない値が送信された場合に情報が流出しないよう常に追跡しましょう。

クロスサイトスクリプト攻撃(XSS)
POINT

  • クロスサイトスクリプトは入力の表示(出力)による攻撃
  • 攻撃者は悪意のある HTML や JavaScript を入力値に含めてページに埋め込む
  • httpクライアントへの表示(出力)は必ずhtmlentities()関数を使用
  • 入力のフィルタを出力のエスケープで回避できる

クロスサイトスクリプティングは、入力値に HTML や JavaScript などが記述されている場合に、適切なエスケープをしないで表示(出力)してしまうことで発生します。攻撃者は、入力値に悪意のあるJavaScriptなどを記述し、アプリケーションのページ内にコードを埋め込みます。その結果、コードを埋め込まれたページを表示した他のユーザーのセッションIDを盗んだり、他のサーバーへ攻撃をさせたりします。

クロスサイトスクリプト攻撃に対する脆弱性は入力を適切にエスケープしないで出力した際に発生します。これは、入力を適切にフィルタし、出力を適切にエスケープすることで回避することが出来ます。
httpクライアント(ユーザー)への出力は必ず htmlentities()関数 を使用してエスケープしましょう。

例えば以下の入力項目に対してユーザーが「<a href="http://www.tryphp.net" target="_blank">URL</a>」と入力します。

<form>
username:
<input type="text" size="30" name="username">
<input type="submit" value="確認">
</form>
username:


これを確認画面でそのまま表示させると

username:URL

となります。確認画面では意図しないURLが表示されることになります。

これをエスケープ処理して表示させるとHTMLは無効化されて文字列のみ表示されます。
エスケープ処理

<?php echo htmlentities('<a href="http://www.tryphp.net" target="_blank">URL</a>', ENT_QUOTES, 'UTF-8'); ?>

表示

<a href="http://www.tryphp.net" target="_blank">URL</a>

ソースコードを見ると

<a href="http://www.tryphp.net" target="_blank">URL</a>

となっています。

この例では、攻撃とはなりませんが、ページにコードを埋め込まれエスケープせずに表示する危険性を表しています。例えば、掲示板などを考えてみましょう。運営者が入力された投稿文をエスケープせずに表示させていたとしましょう。そこへ悪意のある攻撃者が他のサーバーへ攻撃を仕掛ける危険なJavaScriptコードを投稿したとします。そのコードを含んだ投稿内容がエスケープされずに表示されていた場合、その掲示板を訪れたユーザーは、知らず知らずに他のサーバーを攻撃することになります。

クロスサイトスクリプト攻撃(XSS)の防御策

クロスサイトスクリプト攻撃は、入力をフィルタリングし、出力をエスケープすれば回避できます。入力のフィルタリング詳細はこちらを参照下さい。エスケープの詳細はこちらを参照下さい

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

  • クロスサイトリクエストフォージェリはHTTPリクエストを捏造する攻撃方法

攻撃者が他のユーザーからのHTTPリクエストを捏造する攻撃方法です。攻撃者によって捏造されたHTTPリクエストが攻撃者からでなく、他の犠牲者から送信され、犠牲者は意図しない処理を実行させられます。具体例として、掲示板に意図しない書き込みをさせられたり、オンラインショップで買い物をさせられたりなどの被害が起こります。

攻撃の例

①ユーザーが商品を購入できるフォーム

<form action="buy.php" method="POST">
アイテム:
<select name="item">
<option name="pen">ペン</option>
<option name="pencil">ペンシル</option>
</select><br>
数量:<input type="text" name="quantity" size="2"><br>
<input type="submit" value="購入">
</form>
アイテム:

数量:

②攻撃者がフォームの要素を確認
攻撃者のフォームの要素がアイテム(item)とその個数(quantity)であることを知ります。
また、アイテムに期待される値が pen と pencil であることを知ります。buy.phpはこの情報を処理することが分かります。

if(isset($_REQUEST['item']) && isset($_REQUEST['quantity'])){
	//フィルタ
	$clean = array();
	$clean['item'] = $_REQUEST['item'];
	$clean['quantity'] = $_REQUEST['quantity'];

	//購入処理
	if(BuyItem($clean['item'], $clean['quantity'])){
		echo 'ご購入ありがとうございます。';
	}
	//失敗
	else{
		echo '購入できませんでした。';
	}
}

③攻撃者が振る舞いを確認
攻撃者は振る舞いを知るためフォームを使用する。GETデータを使用して同じ動作をするか試します。

http://www.tryphp.net/buy.php?item=pen&quantity=1

これが成功した場合、攻撃者は認証ユーザーとして訪れた場合のアイテムの購入を行う際のURLのフォーマットを知ることになります。
攻撃者が犠牲者にこのURLを仕向けるだけで攻撃が成立します。URLを画像に埋め込み、犠牲者にクリックさせるよう仕向ける等して、CSRF攻撃を成立させます。

<a href="http://www.tryphp.net/buy.php?item=pen&quantity=1"><img src="./images/funny.gif"></a>

クロスサイトリクエストフォージェリ(CSRF)の防御策
POINT

  • 独自フォームの使用を強制する
  • 入力フォームにワンタイムトークンを使用する
  • htmlフォームではPOSTを使用する

このような攻撃を防ぐには、フォームにワンタイムトークンを含め、アプリケーションのフォームの使用を強制することが防護策となります。htmlフォームにはGETの替わりにPOSTを使用することでも攻撃を軽減できるでしょう。例えば、掲示板であれば、投稿者が掲示板に書き込む際、掲示板自身の入力フォームから投稿させるように強制することです。自身のフォーム以外からの値を受け入れてはいけません。

<?php
//無作為な文字列を使用してトークンを作成する。
$token = md5(uniqid(rand(), TRUE));
$_SESSION&#91;'token'&#93; = $token;
print "
<form action='security2.php' method='POST'>
<input type='text' name='username' size='20'>
<input type='hidden' name='token' value='".$token."'>
<input type='submit' value='送信'>
</form>
";
?>

security2.php

<?php
if(isset($_SESSION&#91;'token'&#93;) && $_POST&#91;'token'&#93; == $_SESSION&#91;'token'&#93;){
	print $_POST&#91;'username'&#93;.":有効なトークンです。";
}else{
	print "無効なトークンです。";
}
?>

タグ(=記事関連ワード)

日付

投稿日:2012年7月19日
最終更新日:2012年08月29日

関連記事

このカテゴリの他のページ

この記事へのコメント

トラックバックurl

http://www.tryphp.net/phpsecurity-formurl/trackback/