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

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

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

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

セキュリティー

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

PHP

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

リダイレクト

プログラムの入力元や出力先を通常とは別の場所に転送させることをリダイレクトと呼びます。

Q&A

3回答

2078閲覧

[php]CSRF対策している場合、リダイレクトはNGでしょうか?

mariko705

総合スコア6

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

セキュリティー

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

PHP

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

リダイレクト

プログラムの入力元や出力先を通常とは別の場所に転送させることをリダイレクトと呼びます。

0グッド

4クリップ

投稿2018/01/08 18:02

編集2018/01/08 18:03

初めて投稿させて頂きます。
初心者なので、初歩的な質問で申し訳ございません。

ユーザーが氏名を最大50個まで入力し、確認ボタンを押すと、内容確認画面(入力値一覧)に遷移する処理を書いています。

会員式のシステムなので、今までログインいていることのみをチェックしておりましたが、
今回初めてCRSF対策でトークンを発行して、チェックが必要なページに対して入れています。

そこで今回迷っているのが、氏名が既に登録されているかどうかのチェックをDBに対して行うのですが、現状以下の様な流れとなっています。

1.氏名を入力
2.「確認」ボタンを押下(自分に対して、トークンとhidden値「check」をPOST)
3.if (isset($_POST['check']) { でifの中に入り、その中でDB既存チェック
4.エラーならdie('エラー');
5.OKなら確認画面へリダイレクト
といった感じです。

この時、リダイレクト先へトークンを渡せない為に、確認画面に対して氏名をPOSTされると外部から氏名を登録されてしまいます。
やはりこの場合は、確認画面でチェックして、エラーだと「エラーメッセージ+入力画面に戻る」のみ表示させるべきなのでしょうか?
別画面に遷移⇒戻る よりも、そのページ内でチェックして弾いてあげるほうが使いやすいかと思い悩んでおります。

説明が下手ですみません。
以下はソースです。

PHP

1【input.php】 2<?php 3 // セッション開始 4 session_start(); 5 6 <!-- チェッククラスの読み込み --> 7 include_once('check.php'); 8 9    // ログイン状態チェック + CSRF対策 10 if ( ( !isset($_SESSION["NAME"]) ) || 11 ( !CsrfValidator::funcTokenValidate(filter_input(INPUT_POST, 'token'))) ) {  ←トークンのチェックです 12 // ログイン画面へリダイレクト 13 header("Location: login.php"); 14 } 15 16 if ( isset($_POST['check']) ) { 17 チェックフラグがセットされている場合 18 19 $UserName = 入力された氏名を配列として格納(); 20 21 //------------------ 22 // 氏名既存チェック 23 //------------------ 24 $pdo = new PDO(DSN, ユーザー名, パスワード, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION)); 25 26 $stmt = $pdo->prepare("SELECT NAME FROM 氏名テーブル WHERE NAME = ?"); 27 foreach ( $UserName as $name ) { 28 $stmt->execute(array($name)); 29 30 //チェック 31 if ( $row = $stmt->fetch(PDO::FETCH_ASSOC) ) { 32 // 既に登録された氏名の場合 33 die("{$row['NAME']}は既に登録されています。"); 34 } 35 } 36 37 // 確認フォームへ 38 header("Location: confirm.php"); // この時、トークンを渡せない 39 } 40 ?> 41 42<!doctype html> 43<html> 44<head> 45<meta charset="utf-8"> 46<title>入力画面</title> 47</head> 48 49<body> 50 <!----------------  51 入力フォーム 52 -----------------> 53 <div class="form_wrapper cont"> 54 <form action="menber_add_conf.php" method="post" name="frmAdd" onSubmit="return 入力チェック()"> 55 <table class="formTable_v"> 56 <tbody> 57 <tr> 58 <th> 59 氏名 60 </th> 61 </tr> 62 <!-- この下に入力領域を50個用意 --> 63 ・・・ 64 ・・・ 65 ・・・ 66 ・・・ 67 </tbody> 68 </table> 69 <!-- トークンのセット --> 70 <?=CsrfValidator::funcSetToken()?> <!-- このセットが、リダイレクトではできないですよね・・? --> 71 <!-- チェックフラグのセット --> 72 <input type="hidden" name="check" value="check"> 73 74 <button type="submit" id="submit">確認画面へ</button> 75 </form> 76 </div> 77</body> 78</html> 79

一応連携しておきますが、トークンチェックに関するクラスは以下の通りです。

php

1【check.php】 2 class CsrfValidator { 3 4 // ハッシュ用のアルゴリズムをセット(SHA256) 5 const HASH_ALGO = 'sha256'; 6 7 //======================================= 8 // トークン生成処理 9 //======================================= 10 private static function Generate() 11 { 12 if (session_status() === PHP_SESSION_NONE) { 13 throw new \BadMethodCallException('Session is not active.'); 14 } 15 return hash(self::HASH_ALGO, session_id()); 16 } 17 18 //======================================= 19 // トークンチェック処理 20 //======================================= 21 public static function Validate($token, $throw = false) 22 { 23 $success = self::funcTokenGenerate() === $token; 24 if (!$success && $throw) { 25 throw new \RuntimeException('CSRF validation failed.', 400); 26 } 27 return $success; 28 } 29 30 //======================================= 31 // トークンセット処理 32 //======================================= 33 public static function SetToken() { 34 return '<input type="hidden" name="token" value="' . self::funcTokenGenerate() . '">'; 35 } 36 }

わかりにくくて申し訳ございませんが、ご指摘の程宜しくお願いいたします。

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

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

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

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

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

guest

回答3

0

画面遷移の確認ですが、以下のようになっているのでしょうか?

(1) (2) (3) (4) 入力画面 ------> 氏名の重複チェック -------> 確認画面 -------> 登録処理 ------> 登録しました表示 POST リダイレクト POST リダイレクト

現在は、(1)のトークン確認はしているが、(2)ではトークン確認をしていないということですね。
結論から言えば、(2)でのトークン確認は必要ありません。そして、実は、(1)のトークン確認も必須ではありません。
本当にトークン確認が必要なのは(3)のみです。ここでトークンを確認しないと、「氏名をPOSTされると外部から氏名を登録されてしまいます」。
ついでに言えば、重複確認は登録処理でも(登録処理でこそ)必要ですが、やっていますか?そうしないと、同時に(1)→(2)の遷移を通ると、その時点では氏名は登録されていないので、その後(3)で重複した登録がなされます。DBの一意制約があれば大丈夫で、そうするべきですが。

投稿2018/01/08 22:06

ockeghem

総合スコア11701

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

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

mariko705

2018/01/09 02:49

画面遷移のフロー、とてもわかりやすいです。 まさしくその通りで、説明の仕方の参考にもなります・・。ありがとうございます。 >(2)でのトークン確認は必要ありません。そして、実は、(1)のトークン確認も必須ではありません。 確かに、冷静に考えるとそうですね・・。 (1)、(2)では表示やチェックのみで、登録など発生しません。 目先のリダイレクトの疑問にとらわれて根本的なミスに気づいておりませんでした。 >ついでに言えば、重複確認は登録処理でも(登録処理でこそ)必要ですが、やっていますか? ご指摘ありがとうございます。 上記に関しては、登録処理の直前に共に行っています。 ――――――――――――――――――――――――――――――――――― トークンチェックのタイミングとチェックさえきっちり行っていれば、 画面遷移的には、上記の画面遷移で問題ありませんでしょうか?
ockeghem

2018/01/24 00:01

画面遷移は特に問題ありませんが、(2)でリダイレクトするのは冗長な気もします。問題があるとまでは言えませんが
guest

0

ockeghem さんの回答と同じですが、解説を追加して書いてみます。
まず、すべてのセキュリティ対策に共通のことですが、対象となる脅威について具体的に考えて見る必要があります。

###脅威
質問者様のサイトでは、CSRF攻撃によってユーザの意図しない氏名が登録されてしまう。

###方法
以下の手順が成立すると攻撃が成功する

  1. ユーザが質問者様のサイトにログインする(ブラウザに Cookie がセットされる)
  2. 悪意のあるサイトの運営者がユーザに攻撃ページを開かせる
  3. 攻撃ページに隠しの form を埋め込みスクリプトで submit する→このフォームは氏名の登録を行うPOSTと同じ内容をPOSTであり、Cookieも付与されているので、質問者様のサイトがリクエストを受け入れてしまい、氏名が登録されてしまう

###防御方法
氏名の登録を実行する form には隠し input タグでトークンを付与し、トークンが付与されていないものや不正なものはリクエストを受け入れないようにする

###リダイレクトなどで複数の手順がある場合の対処方法
CSRF攻撃では単発のリクエストを発行することはできても、その結果を参照したり操作したりすることはできません。これはブラウザの仕様です。したがって、

2.「確認」ボタンを押下(自分に対して、トークンとhidden値「check」をPOST)

と同様のPOSTを悪意のあるページから発行することはできても、返ってきた確認画面の form をスクリプトなどから submit することはできません。
ですので、攻撃するのであれば、最後の確認画面からのPOSTのほうを真似して攻撃ページに埋め込むことになります。
CSRF攻撃では、悪意のあるページからリクエストの結果を参照したり操作したりできないので、一般的に参照しかしないリクエストは防御の必要はありません。
更新リクエストとなる最後の確認画面からのPOSTのみ防御すればよいということになります。

投稿2018/01/09 02:48

mit0223

総合スコア3401

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

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

0

直接的な回答にはなっていません^^;

フローを確認していないのですが、コードをざっと眺めた時に気になったことがあります。
このコード、ネットにあるコードを継ぎ接ぎして作ってませんか?
filter_input と $_POST が混在していたり、new PDO で、charset がなかったり、重複程度で die で出力し表示を崩してしまっていたり、いろいろチグハグです。。。

個人情報を取り扱うようなので、堅牢なコードがかける前提を整えたほうが良いです。フレームワークの適用や、適切なライブラリの選定を行ってみてはいかがでしょうか?

氏名の重複チェックが少しイレギュラーに見えますが、多分入力値の確認の一部に組み込めば、フレームワークやライブラリの用意する通常のフローで処理できると思います。

個人情報を扱うサイトをフルスクラッチで制作するのは、かなりしんどいと思います。再検討されることをオススメします。

投稿2018/01/08 22:44

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

mariko705

2018/01/09 03:04

大変わかりやすくご回答いただきまして、ありがとうございます! >ネットにあるコードを継ぎ接ぎ ソースまでしっかりチェックして頂きましてありがとうございます・・。 その通りです^^; たとえば >filter_input と $_POST が混在していたり など、なぜこの参考サイトは「filter_input」を使用しているのか・・・などをしっかりできていないまま引用したので、ここを$_POSTで統一させて良いのか、ここを変更するとこのサンプルは動かなくなるのか・・などで迷っているうちに「触らぬ神にたたりなし」で放っておいてしまっている箇所が結構あります・・。 現状成功している動作が崩れるのを恐れての事ですが、そもそもよくわからないで引用している時点でいつか想定外の処理で止まることは時間の問題ですよね・・。 すでにこの低レベルなスクラッチでいろいろ作ってしまっているのですが、、 一点幸いなのはこうやって作っているうちに一つ一つの意味を理解し始めているので、チグハグな箇所についてはそろそろ修正(チグハグを統一)していけそうです。 >フレームワークの適用や、適切なライブラリの選定を行ってみてはいかがでしょうか? >氏名の重複チェックが少しイレギュラーに見えますが、多分入力値の確認の一部に組み込めば、フレームワークやライブラリの用意する通常のフローで処理できると思います。 やはりそうですか。 個人で趣味+勉強で作っているので、とりあえず勉強も兼ねてスクラッチで作ってますが、 一通りやりたいことが実装できれば、今度はフレームワーク使用Verに改造する勉強をしてみようと考えております。 ちなみにte2ji様のご経験上、私の現状(スキルやシステム内容)にオススメのライブラリやフレームワークはございますでしょうか? ※フレームワーク開発の経験がなく、本を買って勉強し始めるレベルだと思います・・。 なるべく易しいものがいいです・・^^;
退会済みユーザー

退会済みユーザー

2018/01/09 03:28

私自身は、コードレベルでのセキュリティを担保する自信がないので、個人情報を扱う案件は手を出してません。 セキュリティ設計の時点で、個人情報を取り扱わないように設計しています。 自身のスキルに合格点が与えられるようになるまでは、個人情報の取扱をやめるか、キチンと識者にコードレビューしてもららわないと危険です。 また、フレームワークですが、私自身は素の php に近い感覚で書けるため、CodeIgniter を使用してきましたが、会員管理システムを作るなら別のフレームワークを使用すると思うので、オススメを紹介できません。 実務で会員管理を行っている方からアドバイスを貰うほうが良いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問