質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

XSS

XSS【クロスサイトスクリプティング】は、 ソフトウェアのセキュリティホールの一つで、Webサイトに脆弱性が あることからその脆弱性を利用し攻撃する手法です。 主に、入力フォームなどから悪意あるスクリプトを挿入し 該当ページを閲覧したブラウザ上でそのスクリプトを実行します。

CSRF

クロスサイトリクエストフォージェリ (Cross site request forgeries、CSRF)は、 外部Webページから、HTTPリクエストによって、 Webサイトの機能の一部が実行されてしまうWWWにおける攻撃手法です。

Q&A

解決済

3回答

10127閲覧

PHPメールフォールのXSSとCSRF対策について

退会済みユーザー

退会済みユーザー

総合スコア0

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

XSS

XSS【クロスサイトスクリプティング】は、 ソフトウェアのセキュリティホールの一つで、Webサイトに脆弱性が あることからその脆弱性を利用し攻撃する手法です。 主に、入力フォームなどから悪意あるスクリプトを挿入し 該当ページを閲覧したブラウザ上でそのスクリプトを実行します。

CSRF

クロスサイトリクエストフォージェリ (Cross site request forgeries、CSRF)は、 外部Webページから、HTTPリクエストによって、 Webサイトの機能の一部が実行されてしまうWWWにおける攻撃手法です。

0グッド

4クリップ

投稿2014/11/26 14:46

編集2015/02/12 04:16

###皆様から回答を受けて

皆様回答ありがとうございました。
まだ調べている途中ですが、この質問を見た方のために参考にしているサイトと書籍をシェアしたいと思います。

** メールフォーム作成の参考 **

※第7章の551頁に「メール送信フォームを作成したい」の記述があります。
質問した構成と同じく入力→確認→送信と遷移するメールフォームです。
メールフォームを作成したい方はこの書籍を参考にするとよろしいかと存じます。

** サニタイズやバリデーション関連 **

** ※jQueryを利用して入力等のチェックを行っていても、HTTPリクエストは偽装できるため、PHP側でも入力チェック(バリデーション)を行う必要があります。jQueryでの入力チェックは、ユーザビリティ向上の役割しかありません。 **
入力処理に関しては、上記のサイトもしくは、セキュリティ分野の大家である徳丸氏の著書(体系的に学ぶ 安全なWebアプリケーションの作り方)が参考になるかと思います。

** セキュリティ全般 **

※第10章にセキュリティに関する記述がありますが、初心者の私でも説明が分かりやすいです。

※パーフェクトPHPの書籍の9章にセキュリティ関する記述があります。
書籍のCSRFへの対応方法として「ワンタイムトークンによるチェック」との記述がありますが、
徳丸氏から指摘があります。詳細は下記をご覧ください。
CSRF対策のトークンをワンタイムにしたら意図に反して脆弱になった実装例

** 補足 **
なんと徳丸氏がPHPの書籍に関して、SQLエスケープの解説状況を記載しております。
書籍別にレビューとセキュリティの状況が記載されているので非常にありがたいです。
本件とはあまり関係ありませんが、書籍を選ぶときの参考にもなります。

【注意】質問で記述したコードには脆弱性があります。絶対にコピペしないでください。
--------------- ここからが最初に投稿した質問内容です ---------------

###はじめに
お世話になります。
現在PHPの勉強をしながらセキュアなメールフォームを作成しております。
「入力画面」→「確認画面」→「送信画面」と遷移するメールフォームの作成をしております。
作成したコードを記載しますので、XSSやCSRFなどのセキュリティ面で問題があるか教えてください。

XSS対策のためにhtmlspecialcharsを使用して特殊文字はエスケープしております。
CSRF対策としてopenssl_random_pseudo_bytesでトークンを作成してチェックしております。

###入力画面

lang

1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4<meta charset="utf-8"> 5<title>PHP送信フォームテスト(入力)</title> 6</head> 7<body> 8<form method="post" action="check.php"> 9氏名:<input type="text" name="name"> 10メールアドレス:<input type="text" name="mail"> 11<input type="submit" value="送信確認"> 12</form> 13</body> 14</html>

###確認画面(check.php)

lang

1<?php 2session_start(); 3// HTML特殊文字をエスケープする関数 4function h($str) { 5 return htmlspecialchars($str,ENT_QUOTES,'UTF-8'); 6} 7// CSRFトークンを作成 8$TOKEN_LENGTH = 16; 9$bytes = openssl_random_pseudo_bytes($TOKEN_LENGTH); 10$_SESSION["TOKEN"] = bin2hex($bytes); 11?> 12<!DOCTYPE html> 13<html lang="ja"> 14<head> 15<meta charset="utf-8"> 16<title>PHP送信フォームテスト(確認)</title> 17</head> 18<body> 19<?php 20foreach($_POST as $key => $value){ 21 $$key = $value; 22} 23// 値のチェック 24$errFlg = 0; 25if($name === "") { 26 echo "氏名を入力してください。"; 27 $errFlg = 1; 28} 29if($mail === "") { 30 echo "メールアドレスを入力してください。"; 31 $errFlg = 1; 32} 33if($errFlg === 1) { 34 echo "<input type='button' value='入力画面へ戻る' onClick='history.back()'></body></html>"; 35 exit(); 36} 37?> 38<form method="post" action="fin.php"> 39氏名:<?php echo h($name); ?> 40メールアドレス:<?php echo h($mail); ?> 41<input type="submit" value="送信"> 42<input type="hidden" name="token" value="<?php echo h($_SESSION["TOKEN"]); ?>"> 43<input type="hidden" name="name" value="<?php echo h($name); ?>"> 44<input type="hidden" name="mail" value="<?php echo h($mail); ?>"> 45</form> 46</body> 47</html>

###送信画面(fin.php)

lang

1<?php 2session_start(); 3mb_language("ja"); 4mb_internal_encoding("UTF-8"); 5?> 6<!DOCTYPE html> 7<html lang="ja"> 8<head> 9<meta charset="utf-8"> 10 <title>PHP送信フォームテスト(送信)</title> 11</head> 12<body> 13<?php 14foreach($_POST as $key => $value){ 15 $$key = $value; 16} 17// tokenのチェック 18if($_SESSION["TOKEN"] !== $_POST["token"]) { 19 echo "不正です。</body></html>"; 20 exit(); 21} 22$to = "hoge@hoge.jp"; 23$subject = mb_convert_encoding("メールフォームからの送信","UTF-8"); 24$mailbody = mb_convert_encoding("氏名:$name\nメールアドレス:$mail","UTF-8"); 25 26if($name !== "" && $mail !== "") { 27 if(mb_send_mail($to,$subject,$mailbody,"From:$mail")) { 28 echo "メール送信しました。"; 29 } else { 30 echo "メール送信に失敗しました。"; 31 } 32} else { 33 echo "入力されていません。"; 34} 35?> 36</body> 37</html>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

細かく見ていませんが、致命的そうなところが1点。

lang

1foreach($_POST as $key => $value){ 2 $$key = $value; 3}

例えばこのスクリプトに対して、

lang

1<?php 2session_start(); 3 4foreach($_POST as $key => $value){ 5 $$key = $value; 6} 7 8var_dump($_POST); 9var_dump($_SESSION); 10 11if ($_POST) { 12 if($_SESSION["TOKEN"] !== $_POST["token"]) { 13 echo "invalid token !!!"; 14 exit(); 15 } 16 echo "token OK"; 17}

このようなフォームから POST すると・・・

lang

1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4 <meta charset="utf-8"> 5 <title></title> 6</head> 7<body> 8 <form method="post" action="z.php"> 9 <input type="hidden" name="_SESSION[TOKEN]" value="oreore"> 10 <input type="hidden" name="token" value="oreore"> 11 <input type="submit" value="SEND"> 12 </form> 13</body> 14</html>

投稿2014/11/27 00:51

ngyuki

総合スコア4514

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2014/11/27 01:18

回答ありがとうございます。 Qiitaでも記事を拝見しております。 foreachでの取り出し方に問題があるんですね。 後ほど確認・検証いたします。 取り急ぎ、お礼申し上げます。
TaMaMhyu

2014/11/27 01:40

$$の展開は基本的に推奨されないですね。 PHPに限った話ではないですが、入力パラメータはホワイトリスト方式などでフィルタリングするか、key固定で使うべきかと思います。
退会済みユーザー

退会済みユーザー

2014/11/27 06:12

TaMaMhyu さん コメントありがとうございます。 入力フィルタリングする必要があるんですね。 調べてみたらfilter_input関数で出来そうな気がします。 $$の展開とフィルタリングについてもう少し調べてみます。 勉強になりました。ありがとうございます。
退会済みユーザー

退会済みユーザー

2014/11/27 08:41

CertaiN さん 参考URLありがとうございます。 mpywさんですよね。いつもQiitaで記事を拝見しております。 参考URLを読んで勉強します。 ありがとうございます。
guest

0

foreach($_POST as $key => $value) { $$key = $value; }

については、既に指摘がありますが、古いPHPにあったregister_globals = onと同じ効果なので、害悪については「register_globals」でググルと良いでしょう。

他の脆弱性としては、

if(mb_send_mail($to,$subject,$mailbody,"From:$mail")) {

で、$mail の内容のバリデーションを行っていないので、改行を含めることでメールに任意のヘッダのインジェクションが可能。
つまり、任意のアドレスにメールを送るなどができます。

細かいところでは、POSTデータにmailやnameが無かった場合、変数未定義エラーになります。
これはおそらく、最初のPOSTデータ取り出しのところの改善の中で対処するのでしょう。

投稿2014/11/28 03:04

otn

総合スコア84380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2014/11/28 03:16

otn さん ご回答ありがとうございます。 調べ方まで記載頂き助かります。 ご指摘頂いた箇所を調べて勉強します。 取り急ぎ、お礼申し上げます。
ockeghem

2014/12/01 13:40

横から失礼します。_SESSION["TOKEN"] と $_POST["token"] がともに指定されていない場合にも_SESSION["TOKEN"] === $_POST["token"] が成立するので、CSRF脆弱性になりますよ。
退会済みユーザー

退会済みユーザー

2014/12/02 04:53

ockeghem さん 徳丸さんですよね。コメントありがとうございます。 確かに指定されていない場合でも成立してしまいますね。 徳丸本を読んで勉強します。 ご指摘ありがとうございました。
guest

0

プログラム的な所は、識者の皆様がお答え下さっているので
別の方面から。
私は自作のプログラムを、N-Stalkerのフリー版で検査しています。
http://www.nstalker.com/
フリーながら、なかなかきちんと見てくれるので重宝しています。
惜しむらくは全部英語って事ですが、まぁ見れば判るレベルかと。

投稿2014/12/02 04:44

tsuntsun

総合スコア199

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2014/12/02 05:06

tsuntsun さん 回答ありがとうございます。 プログラムを検査できるツールがあるんですね。はじめして知りました。 後ほど、ご紹介頂いたツールを確認してみます。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問