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

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

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

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

CSRF

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

Q&A

解決済

1回答

1738閲覧

CSRF対策がうまくいかない

退会済みユーザー

退会済みユーザー

総合スコア0

PHP

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

CSRF

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

0グッド

1クリップ

投稿2021/04/21 14:24

前提・実現したいこと

CSRF対策がうまくいきません。

以下のように、セキュリティを強化するためにPHPでCSRF対策を行いました。
しかし、ユーザの新規登録に成功しても不正なリクエストと表示されてしまいます。

イメージ説明
イメージ説明

新規登録画面(signup_form.php)

php

1会社新規登録画面 2<a href="register.php">新規登録完了</a> 3 4<?php 5 6// 直リンクの禁止 7if (empty($_SERVER["HTTP_REFERER"])) { 8 header('Location: /'); 9 exit; 10} 11 12session_start(); 13require_once(ROOT_PATH .'/Models/Company.php'); 14require_once(ROOT_PATH .'/function.php'); 15ini_set('display_errors', "On"); 16 17 18$err = []; 19 20if($_SERVER['REQUEST_METHOD'] === 'POST'){ 21 $company_name = filter_input(INPUT_POST, 'company_name'); 22 $company_url = filter_input(INPUT_POST, 'company_url'); 23 $company_tel = filter_input(INPUT_POST, 'company_tel'); 24 $company_postal = filter_input(INPUT_POST, 'company_postal'); 25 $company_adress = filter_input(INPUT_POST, 'company_adress'); 26 $company_email = filter_input(INPUT_POST, 'company_email'); 27 $company_pass = filter_input(INPUT_POST, 'company_pass'); 28 $company_passconf = filter_input(INPUT_POST, 'company_passconf'); 29 30 if(empty($company_name)) { 31 $err[] = "会社名は必須です。"; 32 } 33 if(empty($company_url)) { 34 $err[] = "会社のURLは必須です"; 35 } 36 if(empty($company_tel) || !preg_match("/^[0-9]+$/", $company_tel)) { 37 $err[] = "会社の電話番号は必須です。また半角数字で入力してください。"; 38 } 39 if(empty($company_postal) || !preg_match("/^[0-9]+$/", $company_postal)) { 40 $err[] = "会社の郵便番号は必須です。また半角数字で入力してください。"; 41 } 42 if(empty($company_adress)) { 43 $err[] = "会社の住所は必須です"; 44 } 45 if(empty($company_email) || !filter_var($company_email, FILTER_VALIDATE_EMAIL)) { 46 $err[] = "メールアドレスは必須です。また正しいメールアドレスで入力してください。"; 47 } 48 if(!preg_match("/^(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,20}$/i", $company_pass)) { 49 $err[] = "パスワードは半角英数字8文字以上20文字以内で入力して下さい。"; 50 } 51 if($company_pass !== $company_passconf) { 52 $err[] = "パスワードが一致しません。もう一度確認してください。"; 53 } 54 55 if(count($err) === 0) { 56 // ユーザを登録する処理 57 $hasCreated = Company::createCompany($_POST); 58 $token = filter_input(INPUT_POST, 'csrf_token'); 59 $csrf = $_SESSION['csrf_token']; 60 header('Location: register.php'); 61 exit(); 62 63 if(!$hasCreated) { 64 $err[] = "登録に失敗しました!"; 65 } 66 } 67} 68 69?> 70 71<!DOCTYPE html> 72<html lang="en"> 73<head> 74 <meta charset="UTF-8"> 75 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 76 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 77 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> 78 <link rel="stylesheet" href="/css/register.css"> 79 <title>会社新規登録画面</title> 80</head> 81<body> 82 <?php include(ROOT_PATH .'Views/common/header.php'); ?> 83 <div class="wrapper"> 84 <div class="container"> 85 <form action="" method="POST"> 86 <h1 id="register-title">新規登録</h1> 87 <?php if(count($err) > 0): ?> 88 <div class="alert alert-danger" role="alert"> 89 <?php foreach($err as $e): ?> 90 <ul> 91 <li><?php echo $e ?></li> 92 </ul> 93 <?php endforeach ?> 94 </div> 95 <?php endif; ?> 96 <div class="mb-3"> 97 <label for="kome"><font color="red">*</font></label> 98 <label for="company_name" class="form-label">会社名</label> 99 <input type="text" class="form-control" id="company_name" name="company_name" value="<?php if(isset($_POST['company_name'])) echo h($company_name) ?>" placeholder="会社名を入力してください。"> 100 </div> 101 <div class="mb-3"> 102 <label for="kome"><font color="red">*</font></label> 103 <label for="company_url" class="form-label">会社のURL</label> 104 <input type="text" class="form-control" id="company_url" name="company_url" value="<?php if(isset($_POST['company_url'])) echo h($company_url) ?>" placeholder="会社のURLを入力してください。"> 105 </div> 106 <div class="mb-3"> 107 <label for="kome"><font color="red">*</font></label> 108 <label for="company_tel" class="form-label">会社の電話番号</label> 109 <input type="text" class="form-control" id="company_tel" name="company_tel" value="<?php if(isset($_POST['company_tel'])) echo h($company_tel) ?>" placeholder="会社の電話番号を半角数字で入力してください。例:0399801120"> 110 </div> 111 <div class="mb-3"> 112 <label for="kome"><font color="red">*</font></label> 113 <label for="company_postal" class="form-label">会社の郵便番号</label> 114 <input type="text" class="form-control" id="company_postal" name="company_postal" value="<?php if(isset($_POST['company_postal'])) echo h($company_postal) ?>" placeholder="会社の郵便番号を半角数字で入力してください。例:1239900"> 115 </div> 116 <div class="mb-3"> 117 <label for="kome"><font color="red">*</font></label> 118 <label for="company_adress" class="form-label">会社の住所</label> 119 <input type="text" class="form-control" id="company_adress" name="company_adress" value="<?php if(isset($_POST['company_adress'])) echo h($company_adress) ?>" placeholder="会社の住所を入力してください。"> 120 </div> 121 <div class="mb-3"> 122 <label for="kome"><font color="red">*</font></label> 123 <label for="company_email" class="form-label">メールアドレス</label> 124 <input type="email" class="form-control" id="company_email" name="company_email" value="<?php if(isset($_POST['company_email'])) echo h($company_email) ?>" placeholder="メールアドレスを入力してください。"> 125 </div> 126 <div class="mb-3"> 127 <label for="kome"><font color="red">*</font></label> 128 <label for="company_pass" class="form-label">パスワード</label> 129 <input type="password" class="form-control" id="company_pass" name="company_pass" value="" placeholder="半角英数字で8文字以上20文字以内で入力してください。"> 130 </div> 131 <div class="mb-3"> 132 <label for="kome"><font color="red">*</font></label> 133 <label for="company_passconf" class="form-label">パスワード確認用</label> 134 <input type="password" class="form-control" id="company_passconf" name="company_passconf" value="" placeholder="半角英数字で8文字以上20文字以内で入力してください。"> 135 </div> 136 <div class="register_btn"> 137 <input type="hidden" name="csrf_token" value="<?php echo h(setToken()); ?>"> 138 <button type="submit" class="btn btn-primary">新規登録する</button> 139 </div> 140 </form> 141 </div> 142 </div> 143 <?php include(ROOT_PATH .'Views/common/footer.php'); ?> 144</body> 145</html>

登録完了画面(register.php)

新規登録完了画面 <a href="../../../index.php">ホーム画面</a> <a href="login_form.php">ログイン</a> <?php // 直リンクの禁止 if (empty($_SERVER["HTTP_REFERER"])) { header('Location: /'); exit; } if(!isset($csrf) || $token !== $csrf) { exit('不正なリクエストです。'); } unset($csrf); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> <link rel="stylesheet" href="/css/register.css"> <title>会員登録完了画面</title> </head> <?php include(ROOT_PATH .'Views/common/header.php'); ?> <body> <div class="r-wrapper"> <div class="r-container"> <div class="r-inner"> <div class="r-contents"> <h1 id="registered-title">会社情報を登録しました</h1> <h6 id="login-need"><font color="red">サービスを利用するにはログインが必要です</font></h6> <div class="r-box"> <div class="box-text"> <small>ログインしてサービスを利用する</small> </div> <div class="box-btn"> <a class="btn btn-primary" href="login_form.php" role="button">ログインする</a> </div> </div> <div class="r-box"> <div class="box-text"> <small>ログインせずにホーム画面へ戻る</small> </div> <div class="box-btn"> <a class="btn btn-primary" href="../../index.php" role="button">ホームに戻る</a> </div> </div> </div> </div> </div> </div> </div> </div> <?php include(ROOT_PATH .'Views/common/footer.php'); ?> </body> </html>

var_dum($token),var_dump($csrf)でチェックすると、どちらもnull
値を渡せていない?

$token = filter_input(INPUT_POST, 'csrf_token'); $csrf = $_SESSION['csrf_token'];

ここが原因?

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

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

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

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

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

guest

回答1

0

ベストアンサー

新規登録画面(signup_form.php)の以下の処理が気になります。

$token = filter_input(INPUT_POST, 'csrf_token'); $csrf = $_SESSION['csrf_token']; header('Location: register.php'); exit();

変数$tokenと$csrfに代入されていますが、直後にリダイレクトされるので、これら変数は使われません。
そして、登録完了画面(register.php)では、

if(!isset($csrf) || $token !== $csrf) { exit('不正なリクエストです。'); } unset($csrf);

未定義の変数$tokenと$csrfが使われています。
$token = と$csrf = の処理をこのif文の直前に移動すべきではないでしょうか。

投稿2021/04/21 14:53

ockeghem

総合スコア11701

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

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

退会済みユーザー

退会済みユーザー

2021/04/24 08:13

ご連絡遅くなり申し訳ございません。 ご回答いただきありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問