PHP


掲示板投稿プログラム insert.php があります。insert.phpにアクセスするとトピック名や本文などを入力するテキストエリアが表示され OK をクリックすると<form action= で指定したプログラムへデータをPOSTします。

しかし別のサイトやローカルで作成した別の投稿用HTMLの<form actionのところをフルURLを指定してやるとどこからでも投稿できてしまいます。これを防止するにはセッションなどを使うと良いそうですが全く仕組みが分かりません。この仕組みを教えてください。

又、簡単なプログラムなどを書いていただけると高ポイント差し上げます。

よろしくお願いします。

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2006/08/07 16:25:24
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:bonlife No.3

回答回数421ベストアンサー獲得回数75

ポイント45pt

このあたりの内容についてはO'reillyの「入門PHPセキュリティ」が詳しいです。

「入門」とついていますが、幅広い内容を丁寧に説明してあります。

一度立ち読みで内容を確認してみて、気に入ったら購入してみてはいかがでしょうか。

手元に一冊あると安心です。

(ページ数の割にちょっと高い気がしますが、読み終わる頃にはそれだけの価値がある本だと思えるはずです。)

この質問の件については、2章の「フォームとURL」にて説明されています。

id:miyatyuさんの例をもう少し発展させた例(単純にsession_idをそのまま使うのではなく、簡単な暗号化を行う例)がP.28に載っています。

そのまま全ソースを引用することの是非が判断できませんでしたので、部分的に引用してご紹介いたします。

まず、フォーム側でトークンを生成し、POSTで送信します。

$token=md5(uniqid(rand(),TRUE));
$_SESSION['token'] = $token;
$_SESSION['token_time'] = time();

このようにuniqidとrandを使って極めて予測困難な 32 文字のIDを生成します。

チェック方法する方法は以下の通りです。

サンプル中の & amp ; は&に読み替えてください。

if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])
{
  /* 有効なトークン */
}

トークンの期限を設定し、それによって処理を制限するためには、以下のようにトークン生成日時と処理日時の比較を行います。

$token_age = time() - $_SESSION['token_time'];
if ($token_age <= 300)
{
  /* 5分以内の短い値が渡された。 */
}

トークンの有効期限の値は短ければそれだけ危険が少なくなりますが、このあたりは利便性とのトレードオフになりますので、適切な値をご検討ください。

また、HTTPリクエストの偽装は簡単であるため、$_SERVER['HTTP_REFERER']をチェックするロジックは必ずしも偽装を防ぐことにはならない、という説明がP.30、P31にありました。

参考になれば幸いです。

id:esecua

大変参考になりました。

一つ確認をさせてください。

POST先のスクリプトで

if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])

{

$token_age = time() - $_SESSION['token_time'];

if ($token_age <= 300)

{

/* 動作を実行 */

} eles {

echo "もう一度戻ってPOSTしてください。";

exit();

} else {

echo "エラー";

exit();

}

のような感じでよろしいでしょうか?よろしくお願いします。

2006/08/07 13:20:22

その他の回答3件)

id:miyatyu No.1

回答回数31ベストアンサー獲得回数2

ポイント15pt

いわゆるCSRFという脆弱性ですね。

http://e-words.jp/w/CSRF.html

この中の、

「フォームの一部にランダムな数値を隠しておいてアクセスの一貫性をチェックしたり」の一手法として、ワンタイムチケット法があります。

ユーザーと1対1対応するキーワードを生成してから、ユーザーにそのキーワードを渡します。次にユーザーからアクション(投稿処理など)があったときに、そのキーワードが一致しているかどうかを見るというものです。

簡単に手を抜くのであれば、PHPについているセッションのsession_idをそのまま使うことで解決できます。

投稿側にもPHPを用います。

投稿側PHP

<?php

session_start();

?>

...

<form ...>

<input ...>

<input type="hidden" name="checkid" value="<?php echo(session_id()); ?>>

</form>

受信側

if ($_POST['checkid'] == session_id()){

session_destroy();

投稿処理;

} else {

session_destroy();

エラー処理;

}

#ただ、session_id自身には脆弱性が多い(らしい)ことや、これだと複数の投稿画面などがあってユーザーが好き勝手にうろちょろする場合などには対応しきれない場合がありますので、あくまでも、「応急処置」程度に。

id:esecua

応急処置程度ですか。

やはり大手ポータルサイト(Yahoo!やGoogle)やはてななどもこの方法で防いでいるのでしょうか?

それよりももっと信頼性が高く、安全で正確な方法があるのでしょうか?

2006/08/04 02:41:28
id:m-nisi No.2

回答回数159ベストアンサー獲得回数3

refererを使えば出来ますよ。

id:esecua

ブラウザによってはダメな物もあるでしょうが。

2006/08/04 11:37:49
id:bonlife No.3

回答回数421ベストアンサー獲得回数75ここでベストアンサー

ポイント45pt

このあたりの内容についてはO'reillyの「入門PHPセキュリティ」が詳しいです。

「入門」とついていますが、幅広い内容を丁寧に説明してあります。

一度立ち読みで内容を確認してみて、気に入ったら購入してみてはいかがでしょうか。

手元に一冊あると安心です。

(ページ数の割にちょっと高い気がしますが、読み終わる頃にはそれだけの価値がある本だと思えるはずです。)

この質問の件については、2章の「フォームとURL」にて説明されています。

id:miyatyuさんの例をもう少し発展させた例(単純にsession_idをそのまま使うのではなく、簡単な暗号化を行う例)がP.28に載っています。

そのまま全ソースを引用することの是非が判断できませんでしたので、部分的に引用してご紹介いたします。

まず、フォーム側でトークンを生成し、POSTで送信します。

$token=md5(uniqid(rand(),TRUE));
$_SESSION['token'] = $token;
$_SESSION['token_time'] = time();

このようにuniqidとrandを使って極めて予測困難な 32 文字のIDを生成します。

チェック方法する方法は以下の通りです。

サンプル中の & amp ; は&に読み替えてください。

if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])
{
  /* 有効なトークン */
}

トークンの期限を設定し、それによって処理を制限するためには、以下のようにトークン生成日時と処理日時の比較を行います。

$token_age = time() - $_SESSION['token_time'];
if ($token_age <= 300)
{
  /* 5分以内の短い値が渡された。 */
}

トークンの有効期限の値は短ければそれだけ危険が少なくなりますが、このあたりは利便性とのトレードオフになりますので、適切な値をご検討ください。

また、HTTPリクエストの偽装は簡単であるため、$_SERVER['HTTP_REFERER']をチェックするロジックは必ずしも偽装を防ぐことにはならない、という説明がP.30、P31にありました。

参考になれば幸いです。

id:esecua

大変参考になりました。

一つ確認をさせてください。

POST先のスクリプトで

if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])

{

$token_age = time() - $_SESSION['token_time'];

if ($token_age <= 300)

{

/* 動作を実行 */

} eles {

echo "もう一度戻ってPOSTしてください。";

exit();

} else {

echo "エラー";

exit();

}

のような感じでよろしいでしょうか?よろしくお願いします。

2006/08/07 13:20:22
id:bonlife No.4

回答回数421ベストアンサー獲得回数75

ポイント30pt

およそ処理のイメージは合っていますが、色々と工夫する余地はあります。

まず、処理を続けさせない場合、session情報を破棄しておかないとログインしたままの状態になってしまい、意味がありません。

通常、$_SESSION = array(); として全てのsession情報の定義をクリアした後、session_destroy()でsession情報を破棄し、ログインページにリダイレクトさせます。

このあたりはマンモス本の実践編に載っていたと思います。

また、こういった処理はログインを必要とするCMSでは必ず実装されていますので、XOOPSやOpenPNEなどのオープンソースのコードを眺めてみると良いヒントがあると思います。

また、すでに以前質問されていたようにPEAR::Authも参考になると思います。


あと、一箇所typoがありましたね。

(elesではなくelse)

参考になれば幸いです。

id:esecua

早速実装してみたいと思います。

ありがとうございました。これからもどうぞよろしくお願いします。

2006/08/07 16:23:21

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません