内部$_SERVER[“REQUEST_METHOD”]表单的CSRF


CSRF for internal $_SERVER["REQUEST_METHOD"] form

首次尝试实现CSRF令牌。。。

我有一个内部$_SERVER["REQUEST_METHOD"]表格。。。

网上的大多数内容都是关于通过_$POST php表单完成的。。。

所以。。。在阅读了所有这些之后,我推断,既然我有它作为内部部分,我应该在那里设置令牌。。。

所以这就是我所做的。。。但很明显,我误解了这里的一些东西,因为它不起作用。。。不仅仅是误解。。。我也不确定我应该做什么。。。所以如果你能解释一下我应该做什么,这样我就可以正确设置。。。我真的很感激

<?php
session_start(); //allows use of session variables
  $token = md5(uniqid(rand(), TRUE));
  $_SESSION['token'] = $token;
  $_SESSION['token_time'] = time();

if ($_SERVER["REQUEST_METHOD"] == "POST") {
   if (empty($_POST["first-name"])) {
     $firstNameErr = "First name is required";
   } else {
     $first_name = test_input($_POST["first-name"]);
   }
    if (empty($_POST["last-name"])) {
     $lastNameErr = "Last name is required";
   } else {
     $last_name = test_input($_POST["last-name"]);
   }
   if (empty($_POST["email"])) {
     $emailErr = "Email is required";
   } else {
     $email = test_input($_POST["email"]);
   }
   if (empty($_POST["message"])) {
     $messageErr = "Message is required";
   } else {
     $message = test_input($_POST["message"]);
   }

   if(isset($first_name) && isset($last_name) && isset($email) && isset($message) && isset($token))
   {
     $_SESSION['first_name'] = $first_name;
     $_SESSION['last_name'] = $last_name;
     $_SESSION['email'] = $email;
     $_SESSION['message'] = $message;
     $_SESSION['token'] = $token;
     header("Location: SessionsCRSF.php");
   }
}
function test_input($data) {
   $data = trim($data);
   $data = stripslashes($data);
   $data = htmlspecialchars($data);
   return $data;
}
?>

在同一页上是表单(放置一个片段,但你明白了):

<form class="ui form"  method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
  <input type="hidden" name="token" value="<?php echo $token; ?>" />
  <div class="field">
    <label>First Name</label>
    <input name="first-name" id="first-name" placeholder="First Name" type="text" value="<?php if(isset($first_name)) { echo $first_name; }?>">
     <?php if(isset($firstNameErr)) print ('<span class="error">* ' . $firstNameErr . '</span>'); ?>
  </div>
(...)
</form>

但我不知道如何通过标题中列出的SessionsCRSF.php表单进行测试。。。

以下是SessionsCRSF.php表单的片段,因为其余的只是PHPMailer的东西:

<?php
session_start();
    if ($_POST['token'] == $_SESSION['token'])
    {
      die('working, yay?'); 
    }

$first_name = $_SESSION['first-name'];
$last_name = $_SESSION['last-name'];
$email = $_SESSION['email'];
$message = nl2br($_SESSION['message']);
require 'PHPMailerAutoload.php';
$mail = new PHPMailer;

已编辑

好吧,那么。。到目前为止,这就是我所拥有的:

<?php
session_start(); //allows use of session variables
if ($_SERVER["REQUEST_METHOD"] == "POST" && $_SESSION['token'] == $_POST['token'] && !empty($_SESSION['token'])) {
   if (empty($_POST["first-name"])) {
     $firstNameErr = "First name is required";
   } else {
     $first_name = test_input($_POST["first-name"]);
   }
    if (empty($_POST["last-name"])) {
     $lastNameErr = "Last name is required";
   } else {
     $last_name = test_input($_POST["last-name"]);
   }
   if (empty($_POST["email"])) {
     $emailErr = "Email is required";
   } else {
     $email = test_input($_POST["email"]);
   }
   if (empty($_POST["message"])) {
     $messageErr = "Message is required";
   } else {
     $message = test_input($_POST["message"]);
   }
   if(isset($first_name) && isset($last_name) && isset($email) && isset($message))
   {
     $_SESSION['first_name'] = $first_name;
     $_SESSION['last_name'] = $last_name;
     $_SESSION['email'] = $email;
     $_SESSION['message'] = $message;
     header("Location: contact9SessionsCRSF2.php");
     exit; 
   }
}
else {
  $token = md5(uniqid(rand(), TRUE));
  $_SESSION['token'] = $token;
  $_SESSION['token_time'] = time();
}
function test_input($data) {
   $data = trim($data);
   $data = stripslashes($data);
   $data = htmlspecialchars($data);
   return $data;
}
?>

您有一个页面,其中包含$_SESSION值,并且在HTML表单的隐藏字段中有INPUT值,它们是相同的值。

Page 1:
[SESSION] ... [INPUT]

然后,您将表单提交到目标地址,此处为第2页,然后检查您提交的隐藏字段值是否等于$_SESSION值,该值不会由输入表单字段传递。

这个输入值是从$_POST数组中找到的。

Page 2: 
[SESSION] ... [POST]

特别是针对您的问题,您将表单提交到PHP_SELF页面(通常PHP_SELF是一个不好使用的变量,不鼓励使用),这是同一页面。但是,您对数据的检查是在sessionCRSF.php页面上完成的,所以您应该更新您的表格以放入:

<form action='sessionCRSF.php' ... > 

然后在该页面的顶部,您对令牌值的检查应该会成功。


您似乎对SERVER请求方法表现出了一些困惑,有GET/POST/PUT和其他方法,如果您有

我有一个内部$_SERVER["REQUEST_METHOD"]表单

这到底是什么?如果它是一个表单,那么它就是一个GET/POST方法,据我所知,这不是内部的——即使它在同一台服务器上,它也是从一个页面到另一个页面的,所以上面的概念适用。

解决方案

通过讨论和澄清,我现在可以看到解决方案:

在处理表单提交的If语句中,您没有对令牌发布值$_POST['token']进行任何处理。因此,您需要添加一个语句来处理它,以其最简单的形式,在为$_SESSION表单数据设置值的代码点,添加一行:

$_SESSION['posted_token'] = $_POST['token'];

这就是你所需要的,然后在sessionCRSF.php中比较这两个值,这样你就有了:

 if ($_SESSION['posted_token'] == $_SESSION['token'])
    {
      die('working, yay?'); 
    }

(你让我早些时候混淆了"内部"表格等!)

编辑:

当生成表单时,您需要设置原始$_SESSION['token'],当提交表单时,NOT,您的代码当前在每次迭代中都会创建$_SESSION['token']值,并且由于页面本身引用,这意味着令牌值(随机)将永远不会相同。您需要设置

if (form submitted){
   save submitted POSTED token
}
else{
    /// form not submitted
    generate a token and save to session
} 

不要在同一次迭代中同时做这两件事!!

代码

因为有时只看它更容易…

代码已经更新,可以在POST提交时比较令牌,从而绕过将数据发送到另一个要比较的页面的整个需要

<?php
session_start(); //allows use of session variables

if ($_SERVER["REQUEST_METHOD"] == "POST" && 
$_SESSION['token'] == $_POST['token'] && !empty($_SESSION['token'])) {
   if (empty($_POST["first-name"])) {
     $firstNameErr = "First name is required";
   } else {
     $first_name = test_input($_POST["first-name"]);
   }
    ...etc...
   if(isset($first_name) && isset($last_name) && isset($email) && isset($message))
   {
     $_SESSION['first_name'] = $first_name;
     $_SESSION['last_name'] = $last_name;
     $_SESSION['email'] = $email;
     $_SESSION['message'] = $message;
     header("Location: SessionsCRSF.php");
     exit; //ALWAYS end execution after sending location headers. 
   }
}
else {
  $token = md5(uniqid(rand(), TRUE));
  $_SESSION['token'] = $token;
  $_SESSION['token_time'] = time();
}