vulnerability seccion race ejemplo critica condicion carrera php concurrency

seccion - El método más confiable y seguro para prevenir condiciones de carrera en PHP



race condition vulnerability (5)

De hecho, creo que no hay necesidad de un complejo mutex / semáforo cualquiera que sea la solución.

Después de leer la respuesta de dqhendricks e investigar un poco, descubrí que las claves de formulario a través de $_SESSION son todo lo que necesita. En PHP, las sesiones se bloquean y session_start() espera hasta que se libere la sesión del usuario. Solo tienes que unset() la clave de formulario en la primera solicitud válida. La segunda solicitud tiene que esperar hasta que la primera libere la sesión.

Sin embargo, cuando se ejecuta en un escenario de equilibrio de carga (no basado en ip de origen o de sesión), las cosas se vuelven más complicadas. Para tal escenario, estoy seguro de que encontrará una solución valiosa en este gran documento: thwartedefforts.org/2006/11/11/…

He reproducido su caso de uso con la siguiente demostración. simplemente lance este archivo a su servidor web y pruébelo:

<?php session_start(); if (isset($_REQUEST[''do_stuff''])) { // do stuff if ($_REQUEST[''uniquehash''] == $_SESSION[''uniquehash'']) { echo "valid, doing stuff now ... "; flush(); // delete formkey from session unset($_SESSION[''uniquehash'']); // release session early - after committing the session data is read-only session_write_close(); sleep(20); echo "stuff done!"; } else { echo "nope, {$_REQUEST[''uniquehash'']} is invalid."; } } else { // show form with formkey $_SESSION[''uniquehash''] = md5("foo".microtime().rand(1,999999)); ?> <html> <head><title>session race condition example</title></head> <body> <form method="POST"> <input type="hidden" name="PHPSESSID" value="<?=session_id()?>"> <input type="text" name="uniquehash" value="<?= $_SESSION[''uniquehash''] ?>"> <input type="submit" name="do_stuff" value="Do stuff!"> </form> </body> </html> <?php } ?>

Necesito usar mutexes o semáforos en PHP, y eso me asusta. Para aclarar, no tengo miedo de escribir código libre de interbloqueos que se sincronice correctamente o tenga miedo de los peligros de la programación concurrente, sino de lo bien que PHP maneja los casos marginales.

Fondo rápido: escribiendo una interfaz de controlador de tarjeta de crédito que se encuentra entre los usuarios y la puerta de enlace de la tarjeta de crédito de terceros. Necesito evitar solicitudes duplicadas, y ya tengo un sistema que funciona, pero si el usuario hace clic en enviar (sin JS habilitado, así que no puedo deshabilitar el botón para ellos) milisegundos, se produce una condición de carrera donde mi script PHP No se da cuenta de que se ha realizado una solicitud duplicada. Necesito un semáforo / mutex para poder garantizar que solo se envíe una solicitud exitosa por cada transacción única.

Estoy ejecutando PHP detrás de nginx a través de PHP-FPM con múltiples procesos en una máquina Linux multi-core. Quiero estar seguro de que

  1. Los semáforos se comparten entre todos los procesos php-fpm y entre todos los núcleos (kernel i686).
  2. php-fpm maneja un bloqueo del proceso PHP mientras mantiene un mutex / semáforo y lo libera en consecuencia.
  3. php-fpm maneja un aborto de sesión mientras mantiene un mutex / semáforo y lo libera en consecuencia.

Sí, lo sé. Preguntas muy básicas, y sería una tontería pensar que no existe una solución adecuada para cualquier otra pieza de software. Pero esto es PHP, y ciertamente no se creó teniendo en cuenta la concurrencia, se bloquea con frecuencia (según las extensiones que haya cargado) y se encuentra en un entorno volátil (PHP-FPM y en la web).

Con respecto a (1), supongo que si PHP está usando las funciones POSIX que ambas condiciones se cumplen en una máquina SMP i686. En cuanto a (2), veo por un breve vistazo a la documentación que hay un parámetro que decide este comportamiento (aunque no entiendo por qué querría que PHP nunca lanzara un mutex si la sesión se cancela). Pero (3) es mi principal preocupación y no sé si es seguro asumir que php-fpm maneja correctamente todos los casos marginales para mí. Yo (obviamente) nunca quiero un punto muerto, pero no estoy seguro de que pueda confiar en que PHP nunca deje mi código en un estado en el que no pueda obtener una exclusión mutua porque la sesión que lo tomó fue finalizada con gracia o sin gracia.

He considerado utilizar un enfoque de LOCK TABLES MySQL, pero aún hay más dudas porque mientras confío en el bloqueo de MySQL más que en el bloqueo de PHP, me temo que si PHP cancela una solicitud (con * out * crash) al mantener el bloqueo de sesión de MySQL, MySQL podría mantener la tabla bloqueada (especialmente porque puedo visualizar fácilmente el código que causaría esto).

Honestamente, me sentiría más cómodo con una extensión de C muy básica en la que puedo ver exactamente qué llamadas POSIX se están realizando y con qué parámetros para garantizar el comportamiento exacto que quiero ... pero no espero escribir ese código.

¿Alguien tiene alguna práctica recomendada relacionada con la concurrencia con respecto a PHP que le gustaría compartir?


Lo que hago para evitar la condición de carrera de la sesión en el código es después de la última operación que almacena datos en la sesión. Utilizo la función PHP session_write_close() Observe que si está utilizando PHP 7, debe desactivar el búfer de salida predeterminado en php.ini. Si tiene operaciones que consumen tiempo, sería mejor ejecutarlas después de invocar session_write_close ().

Espero que ayude a alguien, para mi me salvó la vida :)


Si el problema solo surge cuando se presiona un botón con milisegundos de diferencia, ¿no funcionaría un debouncer de software? ¿Cómo guardar el tiempo de un botón al presionar una variable de sesión y no permitir más por, digamos, un segundo? Sólo una idea antes de mi café de la mañana. Aclamaciones.


Una pregunta interesante que tienes, pero no tienes ningún dato o código para mostrar.

Para el 80% de los casos, las posibilidades de que ocurra algo desagradable debido a PHP en sí son prácticamente nulas si sigue los procedimientos y prácticas estándar relacionados con evitar que los usuarios envíen formularios varias veces, lo que se aplica a casi todas las demás configuraciones, no solo a PHP.

Si tiene el 20% y su entorno lo exige, entonces una opción es usar colas de mensajes con las que estoy seguro de que está familiarizado. Una vez más, esta idea es un lenguaje agnóstico. Nada que ver con los idiomas. Se trata de cómo se mueven los datos.


puede almacenar un hash aleatorio en una matriz dentro de los datos de su sesión, así como imprimir ese hash como un valor de entrada de formulario oculto. cuando llega una solicitud, si el valor de hash oculto existe en la matriz de su sesión, puede eliminar el hash de la sesión y procesar el formulario; de lo contrario, no lo haga.

esto debería evitar los envíos de formularios duplicados, así como ayudar a prevenir los ataques csrf.