2013-03-20 69 views
0

对于使用PHP编写的应用程序中的“确认订单”页面,我需要阻止多个表单提交,因此我们不会获得重复的订单。我试图通过两种方式来处理这个问题:即使使用CSRF令牌,表单提交中的PHP重复订单

  1. 对于用户JavaScript支持提交按钮上点击
  2. 令牌与形式产生的禁用和存储在会话。在第一次提交时,他们被比较,会话令牌被删除。因此,没有匹配标记的后续提交应该被拒绝。这应该有防止CSRF攻击的额外好处。

我明白,两者都是相当标准的做法,但问题似乎仍然存在?如果我点击提交按钮几次,js被禁用,我将得到X个重复订单。

这让我觉得可能是问题是配置相关。它位于lighthttpd上,php是用cgi-fcgi编译的。我并不完全确定这是否有意义,但我对此感到困惑。

服务器代码如下(上剪下来的简洁):

<?php 
    $_SESSION['token'] = uniqid('', true); 
?> 
<form name="myform" action="confirm" method="POST"> 
    <!--.... --> 
<input type="hidden" name="csrftoken" value="<?php echo $_SESSION['token']; ?>" /> 
<input type="submit" name="submit" /> 

然后在提交令牌验证:

<?php 
    if ($_POST['csrftoken'] == $_SESSION['token']) { 
     //proceed and process order 
     unset($_SESSION['token']); 
    } 

?> 

会话开始和令牌正确生成并随后取消设置。

我以前用过这种方法没有问题,但这一次它似乎仍然通过。希望有任何见解。

+1

保持会话尽可能短并强制使用'session_write_close()'进行保存。使用'$ _SESSION'输出html是一种糟糕的做法。在本地var然后释放会话中复制需要的会话数据。 – Ghigo 2013-03-20 08:52:41

+0

谢谢。该应用程序是一个遗留应用程序,我正在修复一个错误。我通常不会在HTML输出中引用$ _SESSION。 缺少session_write_close确实是问题的一部分。问题在于原始表单提交发生在锁定会话的iframe中,因此它不会被取消设置。使用session_write_close确实解决了这个问题,但是引入了后续会话更改未被正确更新的进一步问题,即使随后使用了session_start。 – 2013-03-21 08:40:54

+0

应对代码进行重构,以便在session_start()和session_write_close()之间进行所有$ _SESSION访问。我知道遗留代码必须工作,所以你可以考虑一个相当丑陋的黑客攻击:在'session_write_close()'之后搜索所有$ _SESSION访问权限,重新打开会话,修改会话数据并再次关闭它。当然不好的表现,但它应该做的伎俩。 – Ghigo 2013-03-21 09:46:55

回答

0

继Ghigo的评论后,我重构了代码来完成这项工作。

这个问题最终是从iframe和表单提交会话锁定的问题。确保会议尽早结束是解决这个问题的关键。