2011-01-06 9 views
12

Inventé una técnica para evitar el envío de formularios duplicados retrocediendo o retrocediendo o actualizando la página. Y pensé en molerlo aquí, ya probé una muestra que no está en el entorno de producción, ¿cuáles son los defectos que puedes identificar?Prevención de envíos de formularios duplicados

Tenga en cuenta que conozco muy bien el uso de tokens de formulario, que lo defenderán de los ataques CSRF, y no se agregaron en los pasos a continuación.

-Generar la forma de identificación para cada forma, y ​​utilizarlo como campo oculto en la forma:

$formid = microtime(true)*10000; 

- El envío de formulario:

  • Validar a partir de datos

  • Calcular el hash de los campos de formulario de datos

    $allvals = ''; 
    foreach($_POST as $k=>$v){ 
        $allvals .= $v; 
    } 
    $formHash = sha1($allvals); 
    
  • Valide el hash de forma comparando con hashes previamente guardados. el valor de la sesión se asocia a cada formulario mediante la variable $ formid.

    $allowAction = true; 
    if(isset($_SESSION['formHash'][$_POST['formid']]) && ($_SESSION['formHash'][$_POST['formid']] == $formHash)){ 
        $allowAction = false; 
    } 
    
  • si no se ha encontrado la forma de hash, significa que esta es la primera vez forma presentada o se cambia los datos del formulario.
  • Si los datos guardados (a base de datos, por ejemplo), guardar la forma de hash a la sesión:

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

versión completa del código: http://thebusy.me/2011/01/06/preventing-duplicate-form-submissions/

+0

Considere el benchmarking de su fragmento de hash contra '$ formHash = sha1 (serialize ($ _ POST));'. También tendrá claves de hash, lo que puede o no ser beneficioso. – Dan

+0

Posiblemente relacionado/dup, http://stackoverflow.com/questions/4614052/how-to-prevent-multiple-form-submission-on-multiple-clicks-in-php/4614310#4614310 – user562374

+0

Posible duplicado de http://stackoverflow.com/questions/218907/how-to-handle-multiple-submissions-server-side –

Respuesta

7

Una forma más sencilla de lograr lo que querer es usar redirigir en enviar. Después de procesar una solicitud POST, redirige, posiblemente incluso a la misma página. Este es un patrón común llamado "Redirect after POST" o POST/Redirect/GET.

Por ejemplo:

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

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

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

cambie la acción a "" entonces se presentará a sí mismo, en cuyo punto el if ($ _ POST) bloque de código validará en true y procesar el formulario, a continuación, redirigir de vuelta a sí mismo.

Por supuesto, es posible que desee redireccionar a una página diferente que muestre una respuesta "su formulario ha sido enviado" o coloque el formulario en otra página y el HTML de esta página sea la respuesta.

El beneficio de este método es que cuando presiona el botón Atrás hace una solicitud GET para que el formulario no se vuelva a enviar.

En Firefox, en realidad quitará el envío del historial del navegador para que cuando los usuarios naveguen por la web y luego respondan, en lugar de ver la página de "agradecimiento", vean la página del formulario.

+0

Sí, he leído sobre esto y lo usaré para futuros proyectos. el problema es para mis sitios web actuales, estoy usando tokens de formulario, y no quiero cambiar la estructura del código redireccionando a otras páginas. Gracias @ newz2000 – isogashii

+1

incluso podría dar un paso más para habilitar un mensaje de agradecimiento. Setup 2 divs, uno con el formulario y el otro con el mensaje de agradecimiento. Luego configure el agradecimiento div para mostrar: none en css. Haga que la redirección regrese a formpage.php? Thankyou = 1 if ($ _GET ['thank you'] == 1) {css para que el formulario div no muestre ninguno, y configure las gracias para mostrar: bloque} – MSD

+5

This necesita combinarse con tokens de forma para ser infalible. Si el usuario actualiza o vuelve a enviar mientras la solicitud POST está en progreso, usted termina con una solicitud duplicada. – bcoughlan

1

Parece que te estás volviendo demasiado complicado con esto.Mi forma favorita, ya que también impide que alguna sesión de pajas hacks, al mismo tiempo, se describe aquí:

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

Es muy sencillo y fácil de impliment en cualquier forma. Utiliza un ID de instancia de página generada aleatoriamente para verificar que el envío del formulario recibido sea idéntico a la última página servida a ese usuario en particular.

+2

gracias por la respuesta, pero, la publicación dice: "Es importante tener su lógica de procesamiento de formularios antes de establecer la nueva id de instancia de página de sesión para que esto funcione , aunque hay formas de evitar esto ". Intente abrir dos páginas al mismo tiempo, enviar el primero no funcionará, porque se genera una nueva identificación de página cuando se abre la segunda página, esa es la debilidad de esta técnica. Pero puede usar un conjunto de identificadores de página generados, y compararlos, y eliminar el que coincida con el conjunto. – isogashii

+0

Buen punto. La matriz es una gran idea. – dqhendricks

+0

@isogashii He modificado el artículo para que refleje el método basado en matrices. – dqhendricks

-1

Ambas soluciones son buenas pero un poco cortas.

¿Qué tal detener nuevas inserciones en los próximos minutos del mismo usuario con cambios quizás menores en los datos?

esto se puede hacer poniendo un hash md5 en una cookie en la máquina de los usuarios y almacenando una copia en la base de datos; de esta manera, cualquier intento posterior de la misma máquina durante un tiempo específico puede ignorarse y detenerse la base de datos.

¿alguien podría comentar sobre la validez y la eficacia de mi sugerencia o estoy ladrando el árbol equivocado?

Cuestiones relacionadas