如何使用 PHP 会话来防止重复提交表单?
Posted
技术标签:
【中文标题】如何使用 PHP 会话来防止重复提交表单?【英文标题】:How do I use a PHP session to prevent duplicate form submissions? 【发布时间】:2017-06-03 11:39:48 【问题描述】:我想停止向我的网页提交重复的表单。我到处寻求帮助,我看到使用 $_SESSION
变量;但我看不到会话和表单如何在多个用户帐户的上下文之外相互连接。
谁能告诉我这个过程是如何工作的?
编辑:我不想停止多次提交表单;只是为了防止重新提交具有相同值的表单(例如在页面刷新时)。
处理完表单后,用户应该会看到成功/错误消息,然后再次看到表单。
【问题讨论】:
出于其他原因(XSRF)需要使用会话令牌,它间接解决了双重提交。虽然万无一失且更安全,但在某些情况下,Post-Redirect-Get pattern 可以实现更用户友好的方式。两者都应该使用,但后者可以防止多次点击(虽然不确定旧浏览器)和浏览器的后退按钮重新提交 - 所以它涵盖了 99% 的情况(您只能通过停止正在进行的请求或使用错误的 ajax 请求实现)。 @shudder 所以如果我使用会话令牌检查重复提交,如果发生重新提交是否有问题? 取决于你所说的问题。您肯定会阻止重新提交,并且无论如何您都需要处理这些情况,但在广泛的背景下,它们会更频繁地出现,并且通常没有用户的故意行为。您的回复应该是什么并不明显 - “无效令牌”(准确,但当诚实的用户不知道刚刚发生的事情时,您可能不关心那些滥用应用程序的人),“表格已经发送”(我点击了两次还是什么?),“编辑类型的表单页面”(有时),“空表单页面”(我想添加另一个)?很难保持一致。 ^ 我的意思是防止多次点击有什么好处? 使用会话与 PRG 防止多次点击的优势之一是:每次都有效。您需要两者,它们是互补的,例如输入验证和异常处理。 【参考方案1】:基本上您需要使用唯一的 SESSION 值两次。一旦您尝试提交表单中的某个位置并存储在 $_SESSION 变量中。当表单被 POST 并且两个值都匹配时,我们提交成功,当表单被 POST 但值不同时,不会发生提交。
即:
php 页面顶部的某处:
<?php
session_start(); // first line of your PHP page
$_SESSION['secretFormValue'] = isset($_SESSION["secretFormValue"]) ? $_SESSION["secretFormValue"] : md5(microtime()); // generate some unique session value
// assign submitted **secretFormValue** from your form to a local variable
$secretFormValue = isset($_POST["secretFormValue"])? filter_var($_POST["secretFormValue"], FILTER_SANITIZE_STRING) : '';
// check if the value is present in the **secretFormValue** variable
if($secretFormValue != '')
// check if both values are the same
if($_SESSION["secretFormValue"] == $secretFormValue)
// Process form values & submission ...
// add your own code...
unset($_SESSION["secretFormValue"]);
else
echo "Duplicate submission";
else
// do some other thing
表单下方某处:
<input type="hidden" name="secretFormValue" value"<?php echo $_SESSION['secretFormValue']; ?>">
***我没有对此进行测试,所以请在有错误的情况下发表评论,谢谢。*
编辑: 如果您需要阻止在页面刷新时提交,您可以包括在成功提交时清除所有 POST 值,以便刷新会因为 POST 为空而失败,即:
unset($_POST); // place it right before unset($_SESSION["secretFormValue"]);
或
在提交后包含重定向到不同页面(即感谢页面),即:
header("Location:ThankYouPage.php"); // place it right after unset($_SESSION["secretFormValue"]);
或者只是在其他 SO 帖子中找到了这个:
https://en.wikipedia.org/wiki/Post/Redirect/Get
【讨论】:
我还没有尝试过(网站其他地方的问题);但是在用户点击“提交”并重新加载页面后,会话变量是否会更改(第 3 行)并使它们永远不会匹配? 是的,这个想法是......“停止向我的网页提交重复的表单......”正如你最初要求的那样。这意味着同一个表格永远不能提交两次。我有一种感觉,您还需要在第一次提交完成后停止显示表单(改为显示感谢消息等)。我认为您可能需要澄清您试图阻止的具体情况。您是否尝试将表单提交限制为每位访问者 1 个? 刚刚编辑了我的问题。让我知道我是否应该提供任何其他信息。 所以两个“secretFormValues”在第一次提交时是不同的;因此它作为副本返回。将$_SESSION['secretFormValue'] = md5(microtime());
移动到代码的末尾使其看起来正常工作。
是的,有不止一种方法可以解决这个问题,您可能还想考虑一个场景,有人使用自动脚本针对您的表单页面发布 1000 次提交,因此 SESSION 位于顶部...谢谢。【参考方案2】:
一旦该访问者提交了表单,您就可以简单地设置一个$_SESSION['submittedForm'] = true;
变量。然后,您可以在他们下次访问表单页面时检查该会话数据,并且要么不显示表单,要么在他们尝试再次提交时抛出错误消息。
【讨论】:
【参考方案3】:我建议另一种方法。您要解决的问题有两种变体:
-
当用户在提交后刷新页面时,如何防止/检测表单被多次提交(通常是意外)?
如何防止表单数据不是来自我页面上显示的实际表单的提交(或至少使其更难提交)?
Milan 的解决方案解决了这两个问题,但它本身就产生了一个问题:如果用户在多个选项卡/窗口中打开了相同的页面(请记住,它们共享同一个会话),这将中断。
如果您不关心问题 #2,则通过在会话数据中保留已提交的表单 ID 列表来执行“被动”方法会更容易:
$formID = (isset($_POST["__form_id"]) ? $_POST["__form_id"] : false);
$submittedforms = (isset($_SESSION["submittedforms"]) ? $_SESSION["submittedforms"] : array());
// Check whether this form ID has been submitted before
if (in_array($formID, $submittedforms))
printf("Duplicate submission.");
exit;
// Store the ID of this form submission
$submittedforms[] = $formID;
$_SESSION["submittedforms"] = $submittedforms;
// Continue form processing...
如果您还需要防止未经授权的(自动)表单提交,则需要一种积极的方法。我会扩展 Milan 的解决方案,然后将 多个 表单 ID 存储在您的会话数据中(在一个数组中),每个生成的表单一个;然后在提交时一一删除。
【讨论】:
以上是关于如何使用 PHP 会话来防止重复提交表单?的主要内容,如果未能解决你的问题,请参考以下文章