【问题标题】:CSRF for internal $_SERVER["REQUEST_METHOD"] form内部 $_SERVER["REQUEST_METHOD"] 表单的 CSRF
【发布时间】:2015-11-11 08:27:25
【问题描述】:

有史以来第一次尝试实现 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;
}
?>

在同一页面上是表单(放置一个 sn-p,但你明白了):

<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 表单的 sn-p,因为其余的只是 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;
}
?>

【问题讨论】:

  • 你没有发布到 SessionsCRSF.php 所以if ($_POST['token'] == $_SESSION['token']) 不可能工作,因为$_POST['token'] 变量不存在。至少,这会让你的测试失败。
  • @JoeRose,我认为它是通过test_input 上面的函数发布的......然后标题将它带到SessionCRSF.php?我不知道......我该如何让它工作?我的意思是,这不是$_POST,对吧?但更多的是$_SESSION?那么,我应该写这个而不是if ($_SESSION['token'] == $_SESSION['token'])?如果您能解释一下我是如何完成这项工作的,我将不胜感激! =)
  • 我认为更好的方法是创建一个处理 CRSF 的类,然后将发布的令牌和会话令牌传递给该类,然后返回它是否有效,并且然后您的当前页面可以根据此响应执行一些代码(例如再次显示该页面并为用户显示错误消息)。如果你不想写一个类,你可以在 link 找到一个很好的类,你可以将它添加到你的应用程序中(我在搜索 CSRF 时看到了一堆结果)。
  • @JoeRose,好吧,我认为根据这篇文章实现一个 CSRF 令牌相对简单:shiflett.org/articles/cross-site-request-forgeries ... 如果我能那样做,我愿意。我真的不想写一个类,也不想依赖外部脚本,因为这是出于商业目的,我希望维护它,所以如果可能的话,我想了解我在这里做什么。耸耸肩。
  • 在这种情况下,为什么不直接发布到当前页面,验证当前页面上的令牌(可以访问$token$_POST['token'] 变量,检查匹配,然后执行基于此,而不是使用外部页面。如果令牌不正确并且需要重新显示页面,您可以将会话中的表单数据回显到表单字段中(确保使用标题重定向到同一页面以消除重新提交表单的可能性),或者如果令牌正确,您可以执行 PHPMailer 代码,然后重定向到成功页面。

标签: php forms csrf csrf-protection


【解决方案1】:

您有您的页面,其中包含您的 $_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();
}

【讨论】:

  • 嘿,感谢您的回复和解释。我得到你解释的第 1 页和第 2 页的东西。我无法更新&lt;form action = 'sessionCRSF.php ... &gt; 的唯一原因是因为我自行提交它以进行验证,然后使用标头将变量传递到sessionsCRSF.php 页面。因此,我在试图弄清楚如何将隐藏的 CSRF 令牌转移到 sessionCRSF.php 页面以便对其进行检查时感到困惑。如果我解释得很糟糕,请告诉我。这就是我发布代码的顶部和第二部分的原因。
  • @user27307254534534534543675765 我不明白你的流程逻辑,你在一个页面上有一个表单,这个表单提交到另一个页面,然后转移到sessionCRSF.php页面?这听起来很困惑。表单可以提交到另一个页面,然后该页面执行然后转发到最终的休息页面,这可能吗?
  • 如果您的表单提交和激活是“内部”的,那么您无需担心 CSRF,因为其他人将无法访问和提交数据以进行伪造。
  • 不,表单提交给自己,因此这一行:action="&lt;?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?&gt;"&gt;。代码的第一部分和代码的第二部分在同一页面上。然后我尝试将它带到sessionCRSF.php 页面。我以为即使我使用action="&lt;?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?&gt;"&gt;,我仍然需要担心CSRF?
  • @Martin,请查看代码的第一部分,然后向下滚动。查看test_function 上方的代码,您会发现它依赖于标头将其传递到sessionCRSF.php 页面(假设已设置)。
猜你喜欢
  • 2022-06-21
  • 2010-09-29
  • 2012-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多