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 = 0
en su php.ini
archivo. 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 = 1
en su php.ini
archivo. 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_function
en php.ini
. Si PHP <5.3, session.hash_function = 1
configúrelo para SHA1. Si PHP> = 5.3, configúrelo en session.hash_function = sha256
o session.hash_function = sha512
.
Enviar un hash fuerte: session.hash_bits_per_character
en 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_file
y session.entropy_length
en su php.ini
archivo. Establezca el primero en session.entropy_file = /dev/urandom
y 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_id
la 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();
}