【问题标题】:Preventing duplicate form submissions防止重复提交表单
【发布时间】:2011-06-04 16:30:54
【问题描述】:

我想出了一种通过返回/前进或刷新页面来防止重复提交表单的技术。我想在这里讨论一下,我已经测试了一个不在生产环境中的样本,你能识别出哪些缺陷?

请注意,我很清楚使用表单令牌,它可以保护您免受 CSRF 攻击,并且未在以下步骤中添加。

-为每个表单生成表单ID,并将其用作表单中的隐藏字段:

$formid = microtime(true)*10000;

-在表单提交时

  • 从数据验证

  • 计算表单字段数据的哈希

    $allvals = '';
    foreach($_POST as $k=>$v){
        $allvals .= $v;
    }
    $formHash = sha1($allvals);
    
  • 通过与之前保存的哈希值进行比较来验证表单哈希值。会话值通过 $formid 变量绑定到每个表单。

    $allowAction = true;
    if(isset($_SESSION['formHash'][$_POST['formid']]) && ($_SESSION['formHash'][$_POST['formid']] == $formHash)){
         $allowAction = false;
    }
    
  • 如果未找到表单哈希,则表示这是第一次提交表单或更改表单数据。
  • 如果数据保存(例如到数据库),将表单哈希保存到会话:

    $_SESSION['formHash'][$_POST['formid']] = $formHash;
    

完整版代码: http://thebusy.me/2011/01/06/preventing-duplicate-form-submissions/

【问题讨论】:

标签: php forms submit


【解决方案1】:

实现您想要的更简单的方法是在提交时使用重定向。处理 POST 请求后,您重定向,甚至可能重定向到同一页面。这是一种称为“POST 后重定向”或POST/Redirect/GET 的常见模式。

例如:

<?php
if($_POST) {
    // do something

    // now redirect
    header("Location: " . $_SERVER["REQUEST_URI"]);
    exit;
}
?>

<html> ...
<form method="post" action=""> ... </form>

通过将操作设置为“”,然后它将提交给自己,此时 if($_POST) 代码块将验证为 true 并处理表单,然后重定向回自己。

当然,您可能希望重定向到显示“您的表单已提交”响应的不同页面,或者将表单放在不同的页面上并让该页面的 HTML 作为响应。

这种方法的好处是,当您点击返回按钮时,它会发出 GET 请求,因此不会重新提交表单。

在 Firefox 上,它实际上会将提交给自己的内容从浏览器历史记录中取出,因此当用户浏览网页然后回击时,他们会看到表单页面,而不是看到“谢谢”页面。

【讨论】:

  • 是的,我读到了这个,我将在未来的项目中使用它。问题出在我当前的网站上,我正在使用表单令牌,并且我不想通过重定向到其他页面来更改代码的结构。谢谢@newz2000
  • 您甚至可以更进一步来启用感谢信息。设置 2 个 div,一个带有表单,一个带有感谢信息。然后将谢谢 div 设置为在 css 中显示:无。让重定向回到 formpage.php?thankyou=1 if ( $_GET['thankyou'] ==1){css 使表单 div 不显示,并将感谢设置为 display:block}
  • 这需要与表单标记结合才能万无一失。如果用户在 POST 请求进行时刷新或重新提交,您最终会收到重复请求。
  • 尽管每个程序员都会进行重定向,但您的答案并不是解决方案
【解决方案2】:

看起来您对此感到过于复杂。我最喜欢的方式,因为它还可以同时防止一些会话劫持黑客,在这里描述:

http://www.spotlesswebdesign.com/blog.php?id=11

在任何形式上都简单易行。它使用随机生成的页面实例 ID 来验证收到的表单提交是否与提供给该特定用户的最后一个页面相同。

【讨论】:

  • 感谢您的回答,但是,帖子指出:“在设置新的会话页面实例 ID 之前,让您的表单处理逻辑发生很重要,尽管有一些方法可以解决这个问题这。”尝试同时打开两个页面,提交第一个是不行的,因为第二个页面打开时会生成新的页面id,这是该技术的弱点。但是您可以使用生成的页面 id 数组,并检查它们,然后从数组中删除匹配的那个。
  • @isogashii 我已经修改了文章以反映基于数组的方法。
  • 很遗憾链接失效了
【解决方案3】:

上述两种解决方案都不错,但有点短。

如何在接下来的几分钟内停止来自同一用户的进一步插入,数据可能有细微的变化?

这可以通过将 md5 哈希值放入用户机器上的 cookie 中并将副本存储在数据库中来完成 - 这样,在指定时间内来自同一台机器的任何进一步尝试都可以被忽略并停止插入到数据库。

也许有人可以评论我的建议的有效性和有效性,还是我在找错树???

【讨论】:

    猜你喜欢
    • 2016-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 2011-02-19
    • 2012-04-05
    相关资源
    最近更新 更多