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

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

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

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

PHP

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

Q&A

解決済

3回答

1239閲覧

別のサーバーへフォーム送信時のcsrf対策について

donkuri

総合スコア81

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

PHP

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

0グッド

0クリップ

投稿2023/04/27 12:48

実現したいこと

フォーム作成の練習をしていまして、気になったことがあるので質問させていただきます。

htmlで作成したフォームから、別のサーバーに置いてあるPHPMailerにデータ送信したいのですが、
この場合のcsrf対策はどうしたらいいのでしょうか?

   

前提

多くの場合フォームとPHPMailerは同じサーバーに置いてあり、
セッションを使ってトークンを保存しcsrf対策をしているようでした。

私の場合はフォームとPHPMailerは違うサーバー(違うドメイン)のため、
セッションを使わずにcsrf対策をしたいのです。

試したこと

いろいろググって調べた結果、「Access-Control-Allow-Origin ヘッダ」を使い、
ドメイン指定してリクエストを受け付ければcsrf対策はなんとかなりそうかなと思っています。

質問なのですが、このやり方で合ってますでしょうか?
こういった場合は一般的にどうやってcsrf対策するものなんでしょうか?

周りに聞けそうな人がいないのでこちらで質問させていただきます。
よろしくお願いいたします。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2023/04/27 13:36

> 別のサーバーに置いてあるPHPMailerにデータ送信したいのですが、この場合のcsrf対策はどうしたらいいのでしょうか? 別サーバーでないときはどうしているのですか? > いろいろググって調べた結果、「Access-Control-Allow-Origin ヘッダ」を使い、 それは CORS 対応のもので CSRF 対応とは関係ないと思います。
donkuri

2023/04/27 14:29

別サーバーでないときは、 セッションを使ってトークンを保存し、フォームからもトークンを送って、同じかどうかチェックする、みたいな方法が多かったです。 >それは CORS 対応のもので CSRF 対応とは関係ないと思います。 確かにその2つを混同していました。 なんとなく2つとも送信元を確認するということかと思ってました。
退会済みユーザー

退会済みユーザー

2023/04/27 22:03 編集

> 別サーバーでないときは、セッションを使ってトークンを保存し、フォームからもトークンを送って、同じかどうかチェックする、みたいな方法が多かったです。 その文面からは、「フォームとPHPMailer」のフォームの方も PHPMailer の方もこれから CSRF 対策を考えて実装するというように読めますが、そういうことですか? そして、両方とも質問者さんの管理下にあって、質問者さんの自由に CSRF 対策を取れるということで良いのでしょうか? 質問者さんが開発に利用しているフレームワークに CSRF 対策の機能が備わっていて、それが利用できるという話はありませんか?
donkuri

2023/04/28 04:14

はいそうです。 フォームの方も PHPMailer の方も自由にできます。 フォームをvue.jsで作っていますが、それ以外はフレームワーク使っていないです。 csrf対策とかフォームの勉強のためにフレームワーク使わずにゼロから作っています。
guest

回答3

0

ベストアンサー

いろいろググって調べた結果、「Access-Control-Allow-Origin ヘッダ」を使い、
ドメイン指定してリクエストを受け付ければcsrf対策はなんとかなりそうかなと思っています。

PHP側にそのヘッダーを、ということでしたら、
そちらのヘッダは非同期通信にしか効果を発揮しないため、
フォームによるPOST通信に対しては無意味です。

こういった場合は一般的にどうやってcsrf対策するものなんでしょうか?

そもそもフォームと送信先のドメインが違うケース自体がイレギュラーですので、やり方は限られると思います。

A = フォーム送信先
B = フォーム設置先

案1

A、Bのサーバサイドで同じ鍵により、生成と検証ができるトークンが前提。

  1. Bでトークンを発行し、フォーム生成時に送信値としてFORMタグに含める。
  2. そのトークンをAで認証できるようにする。

案2

BがPHPなどで作成できない場合は、
Aにトークンを発行するためのAPIを作成、ヘッダにAccess-Control-Allow-OriginでBのドメイン指定、
Bのフロントから非同期でそのAPIを叩き、フォームの送信値にセットするようにする。

トークン生成と認証(簡易例)

入力値のエラーハンドリングなど不完全ですが、参考にはなると思います。
セキュリティ強度などは要調整。

PHP

1<?php 2 3define("KEY", "test123"); //共通利用の鍵 4define("EXPIRE", 60); //トークンの有効期限(秒) 5 6//トークン取得 7$token = get_token(); 8echo $token; 9echo '<hr>'; 10 11//トークン認証 12$auth = auth_token($token); 13echo $auth ? 'OK' : 'NG'; 14 15//トークン発行 16function get_token() 17{ 18 $time = time(); 19 $id = uniqid(); 20 $hash = md5($id . KEY . $time); 21 $token = $hash . '_' . $id . '_' . $time; 22 23 return $token; 24} 25 26//トークン認証 27function auth_token($token) 28{ 29 list($hash, $id, $time) = explode('_', $token); 30 31 //改竄検知 32 if ($hash !== md5($id . KEY . $time)) { 33 return false; 34 } 35 36 //有効期限確認 37 if ($time < (time() - EXPIRE)) { 38 return false; 39 } 40 41 return true; 42}

投稿2023/04/27 14:42

編集2023/04/28 05:25
pippi19

総合スコア679

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

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

donkuri

2023/04/27 15:58

2案も答えて頂いてありがとうございます! 頂いた案を調べながら見ていました。 案1はちょっと私には難しそうですが、案2ならなんとか出来そうです。 案2の場合なのですが、発行したトークンをデータベースに保存して、フォームの送信値と照合する、みたいな使い方で合ってますでしょうか?
pippi19

2023/04/28 05:20

> 発行したトークンをデータベースに保存して、フォームの送信値と照合する、みたいな使い方で合ってますでしょうか? 同じトークンを1度しか使わせたくない場合は、DB管理になると思います。 不可逆暗号をうまく使えば、一定期間しか使えないトークンを発行できますので、DBは不要です。 コード例を追加しました。
donkuri

2023/04/28 05:46

おおすごい!コード例ありがとうございます! 理解が追いついていませんが、 いろいろ触ってみて確認してみます!
guest

0

同じサーバーであってもCSRF対策は必要です。
外部から送信プログラムを悪用されたら、スパムをまき散らしてしまいます。
環境変数のHTTP_REFERERでリンク元のURLが取得できます。
if ( strpos($_SERVER['HTTP_REFERER'],"正しいリンク元URL")===FALSE )
のようにして、正しくないリンク元からのリクエストを識別することが出来ます。

投稿2023/05/02 17:58

HidekoSaeki

総合スコア42

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

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

donkuri

2023/05/03 02:07

ありがとうございます!
guest

0

フォームの方も PHPMailer の方も自由にできます。

「フォーム」の方はよくあるパターン、即ち CSRF 対策トークンを隠しフィールドに含めてクライアント(ブラウザ)に送信し、クライアントがフォームを送信したら隠しフィールドのトークンを取得して、それとどこか別の場所に保持している情報と比較して検証するということになるのではと想像してます。

「PHPMailer」の方はメールアドレスや本文等を受信してメールを送信する機能を持つ Web API のようなものと理解しています。であれば、Web API でよくあるパターンのトークンベースの認証方式を採用してはいかがですか? (注: そのトークンというのは上に書いた CSRF 対策トークンではありません)

「PHPMailer」の方(または専用の認証サーバーがある?)でユーザー認証を受けて認証トークンを受領したら、それをブラウザのローカルストレージに格納し、API に要求を出す際はローカルストレージから取得してベアラートークンとして使用するということです。

認証トークンがブラウザのローカルストレージに格納される場合は、CSRF の脆弱性について心配は不用です。 CSRF が問題になるのは認証トークンがクッキーに格納されるときです。なので、それで CSRF 攻撃に対する保護にもなるはずです。

ただ、認証トークンはセキュリティ上要求ヘッダに入れて HTTPS 通信で送信する必要があります。「フォーム」を POST するような形ではそこが問題かもしれません。と言ってクエリ文字列に認証トークンを設定して送るのはトークンが丸見えになるので NG です。

なので、jQuery ajax とか HTML5 fetch API を利用して要求ヘッダに認証トークンを設定して送信するということになると思いますが、「PHPMailer」の方は「違うサーバー(違うドメイン)」とのことなので、CORS を実装しなければならないというのが質問者さんにとって問題かもしれませんが。

投稿2023/04/28 05:27

編集2023/04/28 05:30
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

donkuri

2023/04/28 06:16

ありがとうございます。 私もいろいろググって調べた感じ、ユーザー認証機能をつけてトークンを発行する方法(Web API でよくあるパターン?)もあるみたいだったのですが、ユーザー認証機能が難しいので断念しました。 (Laravelなどのフレームワーク使えばなんとかできそうですが。。) 頂いた回答も調べてみて勉強します。ありがとうございました。
退会済みユーザー

退会済みユーザー

2023/04/28 06:34

ユーザー認証なし、すなわち匿名アクセスが可能、すなわちどこからでも誰からでも情報を「PHPMailer」の方に送信すればメールを送信するということですか? そうではなくて、クッキーベースのユーザー認証は実装されているのでそれを使いたいということですか?
donkuri

2023/04/28 07:46

今のところ、ユーザー認証なしで、vue.jsで作った自分のフォームからの送信のみを受け付けたいといった感じです。
退会済みユーザー

退会済みユーザー

2023/04/28 08:17 編集

> 今のところ、ユーザー認証なしで、vue.jsで作った自分のフォームからの送信のみを受け付けたいといった感じです。 そういうことは一番最初の質問に書いておいてほしかったです。 ユーザー認証なしということは、匿名アクセスが可能、どこからでも誰からでも情報を「PHPMailer」の方に送信すればメールを送信するということだと理解してますが、それで「PHPMailer」に CSRF 対策を取ってどういう意味があるのでしょうか? どう考えても意味がないと思いますけど・・・
donkuri

2023/04/28 08:55

すみません csrf対策という言葉の意味を間違っているかもしれませんが、 「自分のフォームからの送信のみを受け付けて、それ以外からの送信を拒否する」 ということがしたいのです。
退会済みユーザー

退会済みユーザー

2023/04/28 09:31 編集

> 「自分のフォームからの送信のみを受け付けて、それ以外からの送信を拒否する」 それだけ言ってもらって(加えてユーザー認証なしということも)、CSRF 対策とは言わないようにしてもらえていたら話はもっと通じやすかったと思います。
donkuri

2023/04/28 10:12

すみません その辺りの言葉の意味もまた勉強しておきます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問