如何防止用户重新发送表单数据?

时间:2011-10-25 23:50:04

标签: php forms

我有一个POST表单,它确实将数据发送到同一个文件,如果使用点击浏览器中的后退按钮,他可以轻松地重新发送数据,它将是还在读。

有没有办法避免这种行为?

6 个答案:

答案 0 :(得分:4)

有两种一般方法。

POST-REDIRECT-GET

首先是post-redirect-get模式,其中用户填写signup.php上的表单POST submit.php REDIRECT成功完成后发送回thanks.phpthanks.php。浏览器会看到重定向响应,并为submit.php发出GET。这样做是因为没有重定向,点击刷新会重新加载POST,导致重新发布表单数据警告并且signup.php ----------- ... <input type="text" name="email"> ... submit.php ---------- ... if ($_POST) { // process data header('Location: thanks.php'); } ... thanks.php ---------- ... Thanks ... 请求被双重发布。重定向解决了这个问题。

nonce

NONCE

第二种方法是在表单中嵌入<input type="nonce" value="<?= uniqid(); ?>"> 键。这种方法还具有防止CSRF攻击的额外好处。这里的方法是在你的表单中注入一个隐藏的guid:

{{1}}

服务器需要跟踪所有nonce密钥问题(在数据库或其他东西中),当它收到表单时,它将处理表单并从数据库中删除nonce密钥。如果用户第二次重新提交表单,则nonce密钥将不存在,服务器可以通过忽略或发出警告来处理。

答案 1 :(得分:3)

Levi发送的链接将为您回答。但是如果你想要一个替代方案,我就是这样做的......

用户发布课程,与您的一样。同一个文件。在课程开始时我做后期处理。对于这个例子,我会很简单......

<?php
session_start();

//set form vars ahead of time so you can pre-populate the value attr on post
$form = array(
    'name' => '',
    'email' => ''
);

if(!empty($_POST))
{
    //do some kind of validation...
    $errors = array();
    if(trim($_POST['name']) == '')
        $errors[] = 'Please enter your name';

    if(empty($errors))
    {
        $_SESSION['message'] = 'Thank you for participating';
        header('location: /form.php'); // same file
        exit;
    }
    else
    {
        // set the form vars to the post vars so you don't lose the user's input
        $form['name'] = $_POST['name'];
        $form['email'] = $_POST['email'];

        $message = '<span style="color:red">';
        foreach($errors AS $error)
        {
            $message .= $error."<br />";
        }
        $message .= '</span>';
        $_SESSION['message'] = $message;
    }
}

if(isset($_SESSION['message']))
{
    echo $_SESSION['message'];
    unset($_SESSION['message']);
}
?>
<form id="some_form" action="" method="post">
    <fieldset>
        <label for="name">Name</label> <input type="text" name="name" value="<?php echo $form['name']; ?>" />
        <br /><br />
        <label for="email">Email</label> <input type="text" name="email" value="<?php echo $form['email']; ?>" />
        <br /><br />
        <input type="submit" name="submit" value="Submit" />
    </fieldset>
</form>

现在您可以反复刷新,而不是两次提交表单。

编辑以显示更多示例。显然,您的验证和错误处理应该比这更复杂,但这应该会让您在正确的方向。

答案 2 :(得分:2)

如果用户想要这样做,你不能100%阻止这种情况,但为了避免这种情况发生,请查看Post/Redirect/Get

答案 3 :(得分:1)

发送表单时,将随机数推送到$_SESSION['stopdupe']值,然后输入隐藏字段。

收到处理表格时:

  1. 检查$_SESSION['stopdupe']是否存在,并匹配隐藏字段的值。 (如果没有,请忽略帖子)
  2. 取消设置$_SESSION['stopdupe']
  3. 正常处理表单。
  4. 这样,如果用户按下提交按钮两次,则不会处理第二个请求。

    另一个有用的技巧是使用表单上的onSubmit javascript事件来禁用该按钮,因此用户无法轻松提交多次。

答案 4 :(得分:1)

这对我有用。我使用smarty,但它也可以毫不聪明地完成。这只是一个想法。根据需要修改它。

HTML表单:

<form action="submit_file.php" method="POST">
<input type="hidden" name="submit_edit" value="1">
<input type="text" name="data"/>
</form>

globals.php中的php

if (count($_POST) > 0) {
    if (isset($_POST['submit_edit']) && $_POST['submit_edit'] == '1'
            && time() - $_SESSION['last_request_time'] < 100
            && count($_SESSION['last_request']) > 0
            && serialize($_SESSION['last_request']) == serialize($_POST)) {

        $oSmarty->assign('display_time_alert', '1');
        unset($_POST['submit_edit']);
        unset($_REQUEST['submit_edit']);
    } else {
        $_SESSION['last_request_time'] = time();
        $_SESSION['last_request'] = $_POST;
    }
}

这是在head.tpl

{literal}
    <script>
function showTimeAlert(){
        alert('{/literal}{tr var="Cannot send the same request twice"}{literal}');
    }
    {/literal}
    {if isset($display_time_alert) AND $display_time_alert eq '1'}
    showTimeAlert();
    {/if}
    {literal}



    </script>
{/literal}

最后是submit_file.php

require_once("globals.php");

if(isset($_POST['submit_edit']) && $_POST['submit_edit'] == '1')){
//record data
}

$oSmarty->dysplay(some.tpl);//in some.tpl is included head.tpl

答案 5 :(得分:0)

在First Page中使用一个会话变量并将值赋值为1.示例:

<form name="frm1" method="post">
    <?php $_SESSION['resend_chk']=1; ?>
    <input type="text" name="a" />
    <input type="submit" name="submit">
</form>

在第二页:

if(isset($_REQUEST['submit'])
{
    if($_SESSION['resend_chk']==1)
    {
        insert to db OR and any transaction
        $_SESSION['resend_chk']=0;
    }
}

它将插入一次或者事务将在db中发生一次并且会话变量的值将改变,下次当我们尝试重新发送数据时它不会执行任何事务,因为会话变量的值已经更改。 / p>