PHP Sesión Fijación / Secuestro

145

Estoy tratando de entender más sobre la fijación de sesiones PHP y el secuestro y cómo prevenir estos problemas. He estado leyendo los siguientes dos artículos en el sitio web de Chris Shiflett:

Sin embargo, no estoy seguro de entender las cosas correctamente.

Para ayudar a prevenir la fijación de la sesión, es suficiente llamar a session_regenerate_id (true); después de iniciar sesión con éxito en alguien? Creo que lo entiendo correctamente.

También habla sobre el uso de tokens pasados ​​en URL a través de $ _GET para evitar el secuestro de sesión. ¿Cómo se haría esto exactamente? Supongo que cuando alguien inicia sesión, usted genera su token y lo almacena en una variable de sesión, ¿en cada página compararía esa variable de sesión con el valor de la variable $ _GET?

¿Debería cambiarse este token solo una vez por sesión o en cada carga de página?

¿También es una buena manera de prevenir el secuestro sin tener que pasar un valor en las URL? Esto sería mucho más fácil.

me2
fuente
Tal vez podría agregar enlaces a las páginas donde ha encontrado estas recomendaciones.
Gumbo

Respuestas:

220

Ok, hay dos problemas separados pero relacionados, y cada uno se maneja de manera diferente.

Fijación de sesión

Aquí es donde un atacante establece explícitamente el identificador de sesión de una sesión para un usuario. Por lo general, en PHP se realiza dándoles una URL como http://www.example.com/index...?session_name=sessionid. Una vez que el atacante le da la URL al cliente, el ataque es lo mismo que un ataque de secuestro de sesión.

Hay algunas formas de evitar la fijación de la sesión (hacer todas):

  • Establecer session.use_trans_sid = 0en su php.iniarchivo. Esto le indicará a PHP que no incluya el identificador en la URL y que no lea la URL de los identificadores.

  • Establecer session.use_only_cookies = 1en su php.iniarchivo. Esto le indicará a PHP que nunca use URL con identificadores de sesión.

  • Vuelva a generar la ID de sesión cada vez que cambie el estado de la sesión. Eso significa cualquiera de los siguientes:

    • Autenticacion de usuario
    • Almacenar información confidencial en la sesión
    • Cambiar cualquier cosa sobre la sesión
    • etc ...

Secuestro de sesión

Aquí es donde un atacante obtiene un identificador de sesión y puede enviar solicitudes como si fuera ese usuario. Eso significa que, dado que el atacante tiene el identificador, no se puede distinguir del usuario válido con respecto al servidor.

No puede evitar directamente el secuestro de sesión. Sin embargo, puede poner pasos para que sea muy difícil y difícil de usar.

  • Use un identificador hash de sesión fuerte: session.hash_functionen php.ini. Si PHP <5.3, session.hash_function = 1configúrelo para SHA1. Si PHP> = 5.3, configúrelo en session.hash_function = sha256o session.hash_function = sha512.

  • Enviar un hash fuerte: session.hash_bits_per_characteren php.ini. Establece esto en session.hash_bits_per_character = 5. Si bien esto no hace que sea más difícil de descifrar, sí hace una diferencia cuando el atacante intenta adivinar el identificador de sesión. La identificación será más corta, pero usa más caracteres.

  • Establezca una entropía adicional con session.entropy_filey session.entropy_lengthen su php.iniarchivo. Establezca el primero en session.entropy_file = /dev/urandomy el último en el número de bytes que se leerán del archivo de entropía, por ejemplo session.entropy_length = 256.

  • Cambie el nombre de la sesión del PHPSESSID predeterminado. Esto se logra llamando session_name()con su propio nombre de identificador como primer parámetro antes de llamar session_start.

  • Si eres realmente paranoico, también puedes rotar el nombre de la sesión, pero ten en cuenta que todas las sesiones se invalidarán automáticamente si cambias esto (por ejemplo, si lo haces dependerá del tiempo). Pero dependiendo de su caso de uso, puede ser una opción ...

  • Gire su identificador de sesión a menudo. No haría esto todas las solicitudes (a menos que realmente necesite ese nivel de seguridad), pero en un intervalo aleatorio. Desea cambiar esto a menudo, ya que si un atacante secuestra una sesión, no desea que pueda usarla durante demasiado tiempo.

  • Incluya el agente de usuario de$_SERVER['HTTP_USER_AGENT'] en la sesión. Básicamente, cuando comienza la sesión, guárdela en algo así $_SESSION['user_agent']. Luego, en cada solicitud posterior verifique que coincida. Tenga en cuenta que esto se puede falsificar, por lo que no es 100% confiable, pero es mejor que no.

  • Incluya la dirección IP del usuario de$_SERVER['REMOTE_ADDR'] en la sesión. Básicamente, cuando comienza la sesión, guárdela en algo así $_SESSION['remote_ip']. Esto puede ser problemático para algunos ISP que usan múltiples direcciones IP para sus usuarios (como solía hacer AOL). Pero si lo usa, será mucho más seguro. La única forma en que un atacante puede falsificar la dirección IP es comprometer la red en algún momento entre el usuario real y usted. Y si comprometen la red, pueden hacer mucho peor que un secuestro (como ataques MITM, etc.).

  • Incluya un token en la sesión y en el lado de los navegadores que incremente y compare a menudo. Básicamente, para cada solicitud hacer $_SESSION['counter']++en el lado del servidor. También haga algo en JS en el lado de los navegadores para hacer lo mismo (usando un almacenamiento local). Luego, cuando envíe una solicitud, simplemente tome un nonce de un token y verifique que el nonce sea el mismo en el servidor. Al hacer esto, debería poder detectar una sesión secuestrada ya que el atacante no tendrá el contador exacto, o si lo tiene, tendrá 2 sistemas que transmiten el mismo conteo y puede decir que uno está falsificado. Esto no funcionará para todas las aplicaciones, pero es una forma de combatir el problema.

Una nota sobre los dos

La diferencia entre la fijación de sesión y el secuestro es solo sobre cómo se ve comprometido el identificador de sesión. En la fijación, el identificador se establece en un valor que el atacante conoce de antemano. En Hijacking es adivinado o robado por el usuario. De lo contrario, los efectos de los dos son los mismos una vez que se compromete el identificador.

Regeneración de ID de sesión

Siempre que regenere el identificador de sesión con session_regenerate_idla sesión anterior, debe eliminarse. Esto sucede de forma transparente con el controlador de sesión principal. Sin embargo, algunos controladores de sesión personalizados que usansession_set_save_handler() no lo hacen y están abiertos a atacar identificadores de sesión antiguos. Asegúrese de que si está utilizando un controlador de sesión personalizado, que realiza un seguimiento del identificador que abre, y si no es el mismo que guarda, elimine (o cambie) explícitamente el identificador en el anterior.

Usando el controlador de sesión predeterminado, está bien con solo llamar session_regenerate_id(true). Eso eliminará la información de la sesión anterior para usted. La identificación anterior ya no es válida y hará que se cree una nueva sesión si el atacante (o cualquier otra persona) trata de usarla. Sin embargo, tenga cuidado con los controladores de sesión personalizados ...

Destruyendo una sesión

Si va a destruir una sesión (por ejemplo, al cerrar sesión), asegúrese de destruirla completamente. Esto incluye desarmar la cookie. Utilizando session_destroy:

function destroySession() {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
    session_destroy();
}
ircmaxell
fuente
44
El uso de 5 en lugar de 4 bits por carácter no cambia la "fuerza" de ninguna manera (lo que sea que significa "fuerza" en este caso). Pero aunque sus puntos son recomendables en general, carecen de algunos detalles importantes. Por ejemplo, qué sucede con la sesión asociada a la ID de sesión anterior o cómo se debe manejar una sesión con una ID de sesión anterior después de que no sea válida.
Gumbo
2
@battal: No, ese es el punto. session_regenerate_idno invalida la sesión que todavía está asociada a la ID anterior; solo si el parámetro delete_old_session se establece en verdadero, la sesión se destruirá. ¿Pero qué pasa si un atacante inicia esta regeneración de ID?
Gumbo
66
No estoy de acuerdo con la regeneración de sesión cada vez que cambia una variable de sesión, solo debe hacerse al iniciar sesión / cerrar sesión. Además, verificar el agente de usuario no tiene sentido y verificar REMOTE_ADDR es problemático. Una cosa que me gustaría agregar es session.entropy_file = /dev/urandom. Se ha demostrado que la generación de entropía interna de PHP es extremadamente débil y el conjunto de entropía proporcionado por / dev / random o / dev / uranom es lo mejor que puede obtener en un servidor web sin un hardware rng.
torre
44
También debe agregar session.cookie_httponlyy session.cookie_secure. El primero ayuda a frustrar xss (pero no es perfecto). La segunda es la mejor manera de detener OWASP A9 ...
rook
44
No entiendo una respuesta tan buena pero falta la pieza más importante: use SSL / HTTPS. El incremento del contador es una fuente de problemas con solicitudes múltiples rápidamente una tras otra, un usuario actualiza una página dos veces o presiona dos veces los botones de enviar. La solución de la dirección IP es un problema hoy en día con todos los usuarios móviles y los IP que cambian constantemente. Podrías mirar el primer conjunto de IP, pero aún así está pidiendo problemas. Lo mejor es evitar el descubrimiento de la identificación de la sesión en primer lugar y eso es usar SSL / HTTPS.
Sanne
37

Ambos ataques de sesión tienen el mismo objetivo: obtener acceso a una sesión legítima de otro usuario. Pero los vectores de ataque son diferentes:

En ambos ataques, la ID de la sesión son los datos sensibles en los que se centran estos ataques. Por lo tanto, es la ID de sesión la que debe protegerse tanto para un acceso de lectura (Secuestro de sesión) como para un acceso de escritura (Fijación de sesión).

La regla general de proteger datos confidenciales mediante HTTPS también se aplica en este caso. Además, debe hacer lo siguiente:

Para evitar ataques de fijación de sesión , asegúrese de que:

Para evitar ataques de secuestro de sesión , asegúrese de que:

Para evitar ambos ataques de sesión, asegúrese de que:

  • para aceptar solo sesiones que su aplicación haya iniciado. Puede hacerlo mediante la toma de huellas digitales de una sesión al inicio con información específica del cliente. Puede usar la ID de agente de usuario, pero no use la dirección IP remota ni ninguna otra información que pueda cambiar entre las solicitudes.
  • para cambiar la ID de sesión usando session_regenerate_id(true)después de un intento de autenticación ( truesolo en caso de éxito) o un cambio de privilegios y destruir la sesión anterior. (Asegúrese de almacenar cualquier cambio de $_SESSIONuso session_write_close antes volver a generar la ID si desea conservar la sesión asociada a la ID anterior; de lo contrario, solo la sesión con la nueva ID se verá afectada por esos cambios).
  • usar una implementación de caducidad de sesión adecuada (consulte ¿Cómo caduco una sesión de PHP después de 30 minutos? ).
Gumbo
fuente
Impresionante publicación, especialmente la última sección.
Mattis
6

Los tokens que mencionas son un "nonce" - número usado una vez. No necesariamente tienen que usarse solo una vez, pero cuanto más tiempo se usen, mayores serán las probabilidades de que el nonce se pueda capturar y usar para secuestrar la sesión.

Otro inconveniente de nonces es que es muy difícil construir un sistema que los use y permita múltiples ventanas paralelas en la misma forma. Por ejemplo, el usuario abre dos ventanas en un foro y comienza a trabajar en dos publicaciones:

window 'A' loads first and gets nonce 'P'
window 'B' loads second and gets nonce 'Q'

Si no tiene forma de rastrear varias ventanas, solo habrá almacenado un nonce, el de la ventana B / Q. Cuando el usuario luego envía su publicación desde la ventana A y la pasa en una 'P', este sistema rechazará la publicación como P != Q.

Marc B
fuente
Entonces, ¿qué tiene esto que ver con la fijación de la sesión?
torre
2
Tiene un punto válido, especialmente en el ámbito del uso simultáneo de muchas solicitudes AJAX.
DanielG
2

No leí el artículo de Shiflett, pero creo que has entendido mal algo.

Por defecto, PHP pasa el token de sesión en la URL cada vez que el cliente no acepta cookies. De lo contrario, en el caso más común, el token de sesión se almacena como una cookie.

Esto significa que si coloca un token de sesión en la URL, PHP lo reconocerá e intentará usarlo posteriormente. La fijación de la sesión ocurre cuando alguien crea una sesión y luego engaña a otro usuario para que comparta la misma sesión abriendo una URL que contiene el token de la sesión. Si el usuario se autentica de alguna manera, el usuario malintencionado conoce el token de sesión de uno autenticado, que podría tener diferentes privilegios.

Como estoy seguro de que Shiflett explica, lo habitual es regenerar un token diferente cada vez que cambian los privilegios de un usuario.

Andrea
fuente
Para agregar a esto, asegúrese de destruir las sesiones abiertas anteriormente, ya que seguirán siendo válidas con los permisos de usuario existentes.
corrodedmonkee
0

Sí, podría evitar la fijación de la sesión regenerando la identificación de la sesión una vez al iniciar sesión. De esta manera, si el atacante no sabrá el valor de la cookie de la sesión recién autenticada. Otro enfoque que detiene totalmente el problema se establece session.use_only_cookies=Trueen su configuración de tiempo de ejecución. Un atacante no puede establecer el valor de una cookie en el contexto de otro dominio. La fijación de la sesión se basa en enviar el valor de la cookie como GET o POST.

torre
fuente