php - texto - "Mantenerme conectado"-el mejor enfoque
string strip_tags (12)
Aviso de seguridad : Basar la cookie en un hash MD5 de datos deterministas es una mala idea; es mejor usar un token aleatorio derivado de un CSPRNG. Vea la respuesta de ircmaxell a esta pregunta para un enfoque más seguro.
Normalmente hago algo como esto:
- El usuario inicia sesión con ''mantenerme conectado''
- Crear sesion
- Cree una cookie llamada SOMETHING que contenga: md5 (salt + nombre de usuario + ip + salt) y una cookie llamada somethingElse que contenga id.
- Almacenar cookie en base de datos
- El usuario hace cosas y se va ----
- El usuario devuelve, verifica si hay alguna cookie cookie. Si existe, obtenga el hash antiguo de la base de datos para ese usuario, verifique que el contenido de la cookie ALGO coincida con el hash de la base de datos, que también debe coincidir con un hash recién calculado (para el ip) por lo tanto: cookieHash == databaseHash == md5 (salt + nombre de usuario + ip + sal), si lo hacen, goto 2, si no lo hacen goto 1
Por supuesto, puede usar diferentes nombres de cookies, etc. También puede cambiar un poco el contenido de la cookie, solo asegúrese de que no se cree fácilmente. Por ejemplo, también puede crear un user_salt cuando se crea el usuario y también poner eso en la cookie.
También puedes usar sha1 en lugar de md5 (o casi cualquier algoritmo)
Mi aplicación web utiliza sesiones para almacenar información sobre el usuario una vez que ha iniciado sesión y para mantener esa información a medida que viajan de una página a otra dentro de la aplicación. En esta aplicación específica, estoy almacenando el user_id
, first_name
y last_name
de la persona.
Me gustaría ofrecer una opción "Mantener mi sesión iniciada" al iniciar sesión que pondrá una cookie en la máquina del usuario durante dos semanas, que reiniciará su sesión con los mismos detalles cuando regresen a la aplicación.
¿Cuál es el mejor enfoque para hacer esto? No quiero almacenar su user_id
en la cookie, ya que parece que eso facilitaría que un usuario intente y falsifique la identidad de otro usuario.
De acuerdo, permítame ponerlo sin rodeos: si está poniendo datos de usuario, o cualquier cosa derivada de los datos de usuario en una cookie para este propósito, está haciendo algo mal.
Ahí. Lo dije. Ahora podemos pasar a la respuesta real.
¿Qué hay de malo con los datos de usuario hash, preguntas? Bueno, todo se reduce a la superficie de exposición y la seguridad a través de la oscuridad.
Imagina por un segundo que eres un atacante. Verá un conjunto de cookies criptográficas para el recordatorio en su sesión. Tiene 32 caracteres de ancho. Caramba. Eso puede ser un MD5 ...
Imaginemos también por un segundo que conocen el algoritmo que usaste. Por ejemplo:
md5(salt+username+ip+salt)
Ahora, todo lo que un atacante debe hacer es forzar la "sal" (que no es realmente una sal, sino más adelante), ¡y ahora puede generar todos los tokens falsos que quiera con cualquier nombre de usuario para su dirección IP! Pero el brute forzar una sal es difícil, ¿verdad? Absolutamente. Pero las GPU de hoy en día son extremadamente buenas en eso. Y a menos que uses suficiente aleatoriedad (hazlo lo suficientemente grande), caerá rápidamente, y con él las llaves de tu castillo.
En resumen, lo único que te protege es la sal, que en realidad no te protege tanto como piensas.
¡Pero espera!
¡Todo eso fue predicado que el atacante conoce el algoritmo! Si es secreto y confuso, entonces estás a salvo, ¿verdad? INCORRECTO . Esa línea de pensamiento tiene un nombre: Seguridad a través de la oscuridad , en la que NUNCA debe confiarse.
La mejor manera
La mejor manera es nunca dejar que la información de un usuario salga del servidor, excepto la identificación.
Cuando el usuario inicie sesión, genere un token aleatorio grande (128 a 256 bits). Agregue eso a una tabla de base de datos que asigna el token al ID de usuario y luego envíelo al cliente en la cookie.
¿Qué pasa si el atacante adivina el token aleatorio de otro usuario?
Bueno, vamos a hacer algunas matemáticas aquí. Estamos generando un token aleatorio de 128 bits. Eso significa que hay:
possibilities = 2^128
possibilities = 3.4 * 10^38
Ahora, para mostrar cuán absurdamente es ese número, imaginemos cada servidor en Internet (digamos 50,000,000 en la actualidad) intentando forzar la fuerza bruta de ese número a una tasa de 1,000,000,000 por segundo cada uno. En realidad, sus servidores se derretirían bajo tal carga, pero vamos a resolver esto.
guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000
Entonces, 50 billones de adivinanzas por segundo. ¡Eso es rápido! ¿Derecha?
time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000
Entonces 6.8 sextillion segundos ...
Tratemos de reducir eso a números más amigables.
215,626,585,489,599 years
O mejor:
47917 times the age of the universe
Sí, eso es 47917 veces la edad del universo ...
Básicamente, no se va a romper.
Así que para resumir:
El mejor enfoque que recomiendo es almacenar la cookie en tres partes.
function onLogin($user) {
$token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
storeTokenForUser($user, $token);
$cookie = $user . '':'' . $token;
$mac = hash_hmac(''sha256'', $cookie, SECRET_KEY);
$cookie .= '':'' . $mac;
setcookie(''rememberme'', $cookie);
}
Luego, para validar:
function rememberMe() {
$cookie = isset($_COOKIE[''rememberme'']) ? $_COOKIE[''rememberme''] : '''';
if ($cookie) {
list ($user, $token, $mac) = explode('':'', $cookie);
if (!hash_equals(hash_hmac(''sha256'', $user . '':'' . $token, SECRET_KEY), $mac)) {
return false;
}
$usertoken = fetchTokenByUserName($user);
if (hash_equals($usertoken, $token)) {
logUserIn($user);
}
}
}
Nota: No utilice el token o la combinación de usuario y token para buscar un registro en su base de datos. Siempre asegúrese de obtener un registro basado en el usuario y use una función de comparación de tiempo seguro para comparar el token recuperado después. Más sobre los ataques de tiempo .
Ahora, es muy importante que SECRET_KEY
sea un secreto criptográfico (generado por algo como /dev/urandom
y / o derivado de una entrada de alta entropía). Además, GenerateRandomToken()
debe ser una fuente aleatoria fuerte ( mt_rand()
no es lo suficientemente fuerte. Use una biblioteca, como RandomLib o random_compat , o mcrypt_create_iv()
con DEV_URANDOM
) ...
El hash_equals()
es para prevenir ataques de tiempo . Si usa una versión de PHP por debajo de PHP 5.6, la función hash_equals()
no es compatible. En este caso, puede reemplazar hash_equals()
con la función timingSafeCompare:
/**
* A timing safe equals comparison
*
* To prevent leaking length information, it is important
* that user input is always used as the second parameter.
*
* @param string $safe The internal (safe) value to be checked
* @param string $user The user submitted (unsafe) value
*
* @return boolean True if the two strings are identical.
*/
function timingSafeCompare($safe, $user) {
if (function_exists(''hash_equals'')) {
return hash_equals($safe, $user); // PHP 5.6
}
// Prevent issues if string length is 0
$safe .= chr(0);
$user .= chr(0);
// mbstring.func_overload can make strlen() return invalid numbers
// when operating on raw binary strings; force an 8bit charset here:
if (function_exists(''mb_strlen'')) {
$safeLen = mb_strlen($safe, ''8bit'');
$userLen = mb_strlen($user, ''8bit'');
} else {
$safeLen = strlen($safe);
$userLen = strlen($user);
}
// Set the result to the difference between the lengths
$result = $safeLen - $userLen;
// Note that we ALWAYS iterate over the user-supplied length
// This is to prevent leaking length information
for ($i = 0; $i < $userLen; $i++) {
// Using % here is a trick to prevent notices
// It''s safe, since if the lengths are different
// $result is already non-0
$result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
}
// They are only identical strings if $result is exactly 0...
return $result === 0;
}
Genere un hash, tal vez con un secreto que solo usted conozca, luego guárdelo en su base de datos para poder asociarlo con el usuario. Debería funcionar bastante bien.
Hay dos artículos muy interesantes que encontré mientras buscaba una solución perfecta para el problema "recuérdame":
Hice un ángulo de esta pregunta here , y las respuestas lo llevarán a todos los enlaces de cookies de tiempo de espera basados en token que necesita.
Básicamente, no almacena el ID de usuario en la cookie. Almacena un token de una sola vez (cadena enorme) que el usuario utiliza para recoger su sesión de inicio de sesión anterior. Luego, para que sea realmente seguro, solicita una contraseña para operaciones pesadas (como cambiar la contraseña en sí).
Hilo viejo, pero sigue siendo una preocupación válida. Noté algunas buenas respuestas acerca de la seguridad, y evité el uso de "seguridad en la oscuridad", pero los métodos técnicos reales dados no eran suficientes a mis ojos. Cosas que debo decir antes de aportar mi método:
- NUNCA almacene una contraseña en texto claro ... ¡NUNCA!
- NUNCA almacene la contraseña con hash de un usuario en más de una ubicación en su base de datos. El servidor backend siempre es capaz de extraer la contraseña de hash de la tabla de usuarios. No es más eficiente almacenar datos redundantes en lugar de transacciones adicionales de la base de datos, lo contrario es cierto.
- Su ID de sesión debe ser única, por lo que dos usuarios nunca podrían compartir una ID, por lo tanto, el propósito de una ID (¿podría su número de identificación de licencia de conducir coincidir con otras personas? No.) Esto genera una combinación única de dos piezas, basada en 2 cuerdas únicas. Su tabla de Sesiones debe usar el ID como el PK. Para permitir que varios dispositivos sean confiables para el inicio de sesión automático, use otra tabla para dispositivos confiables que contenga la lista de todos los dispositivos validados (vea mi ejemplo a continuación), y se asigne usando el nombre de usuario.
- No sirve para hacer hash de datos conocidos en una cookie, la cookie puede copiarse. Lo que estamos buscando es un dispositivo de usuario que cumpla con los requisitos para proporcionar información auténtica que no pueda obtenerse sin que un atacante ponga en peligro la máquina del usuario (nuevamente, vea mi ejemplo). Esto significaría, sin embargo, que un usuario legítimo que prohíba que la información estática de su máquina (es decir, la dirección MAC, el nombre del host del dispositivo, el agente del usuario si está restringido por el navegador, etc.) permanezca coherente (o la falsifique en primer lugar) no podrá usa esta característica Pero si esto es una preocupación, considere el hecho de que está ofreciendo el inicio de sesión automático a los usuarios que se identifican de forma única , por lo que si se niegan a ser conocidos falsificando su MAC, falsificando su agente de usuario, falsificando / cambiando su nombre de host, ocultándose detrás de proxies, etc., entonces no son identificables y nunca deben autenticarse para un servicio automático. Si desea esto, debe buscar en el acceso de tarjeta inteligente incluido con el software del lado del cliente que establece la identidad del dispositivo que se está utilizando.
Dicho todo esto, hay dos formas excelentes de autofirmar en su sistema.
Primero, la forma barata y fácil que lo pone todo en otra persona. Si hace que su sitio sea compatible con el inicio de sesión con, digamos, su cuenta de google +, probablemente tenga un botón optimizado de google + que registrará al usuario si ya ha iniciado sesión en google (lo hice aquí para responder a esta pregunta, ya que siempre estoy firmado en google). Si desea que el usuario inicie sesión automáticamente si ya ha iniciado sesión con un autenticador confiable y compatible, y marcó la casilla para hacerlo, haga que las secuencias de comandos del lado del cliente realicen el código detrás del botón ''iniciar sesión'' correspondiente antes de cargar , solo asegúrese de que el servidor almacene una ID única en una tabla de inicio de sesión automático que tenga el nombre de usuario, la ID de sesión y el autenticador utilizado para el usuario. Dado que estos métodos de inicio de sesión utilizan AJAX, está esperando una respuesta de todos modos, y esa respuesta es una respuesta validada o un rechazo. Si obtiene una respuesta validada, úsela normalmente y luego continúe cargando el usuario que ha iniciado sesión normalmente. De lo contrario, el inicio de sesión falló, pero no se lo diga al usuario, simplemente continúe sin iniciar sesión, lo notarán. Esto es para evitar que un atacante que robó cookies (o las falsificó en un intento de aumentar los privilegios) supiera que el usuario inicia sesión automáticamente en el sitio.
Esto es barato, y puede que algunos lo consideren sucio, ya que trata de validar tu autoestima potencialmente iniciada en lugares como Google y Facebook, sin siquiera avisarte. Sin embargo, no debe usarse en usuarios que no hayan solicitado el inicio de sesión automático en su sitio, y este método en particular es solo para autenticación externa, como con Google+ o FB.
Debido a que se usó un autenticador externo para indicar al servidor entre bambalinas si un usuario fue validado o no, un atacante no puede obtener nada más que un ID único, que es inútil por sí solo. Voy a elaborar
- El usuario ''joe'' visita el sitio por primera vez, el ID de la sesión se coloca en la ''sesión'' de cookies.
- Usuario ''joe'' inicia sesión, aumenta los privilegios, obtiene un nuevo ID de sesión y renueva la ''sesión'' de cookies.
- El usuario ''joe'' elige el inicio de sesión automático utilizando google +, obtiene una ID única colocada en la cookie ''keepmesignedin''.
- El usuario ''joe'' tiene google mantenerlos registrados, lo que permite que su sitio inicie sesión automáticamente cuando el usuario utiliza google en su servidor.
- El atacante intenta, de manera sistemática, identificadores únicos para "mantener la firma" (esto es de conocimiento público y se entrega a todos los usuarios), y no está registrado en ningún otro lugar; intenta una ID única dada a ''joe''.
- El servidor recibe una ID única para ''joe'', obtiene coincidencias en la base de datos para una cuenta de google +.
- El servidor envía a Attacker a la página de inicio de sesión que ejecuta una solicitud AJAX a Google para iniciar sesión.
- El servidor de Google recibe una solicitud, utiliza su API para ver que el atacante no está conectado actualmente.
- Google envía una respuesta indicando que no hay ningún usuario registrado actualmente a través de esta conexión.
- La página del atacante recibe una respuesta, el script redirige automáticamente a la página de inicio de sesión con un valor POST codificado en la url.
- La página de inicio de sesión obtiene el valor POST, envía la cookie para ''keepmesignedin'' a un valor vacío y una válida hasta la fecha de 1-1-1970 para impedir un intento automático, haciendo que el navegador del atacante simplemente elimine la cookie.
- Al atacante se le da la primera página de inicio de sesión normal.
Pase lo que pase, incluso si un atacante usa una ID que no existe, el intento debe fallar en todos los intentos, excepto cuando se recibe una respuesta validada.
Este método puede y debe usarse junto con su autenticador interno para aquellos que inician sesión en su sitio utilizando un autenticador externo.
=========
Ahora, para su propio sistema de autenticación que puede iniciar sesión automáticamente a los usuarios, así es como lo hago:
DB tiene algunas tablas:
TABLE users:
UID - auto increment, PK
username - varchar(255), unique, indexed, NOT NULL
password_hash - varchar(255), NOT NULL
...
Tenga en cuenta que el nombre de usuario tiene una longitud de 255 caracteres. Mi programa de servidor limita los nombres de usuario en mi sistema a 32 caracteres, pero los autenticadores externos pueden tener nombres de usuario con sus @ domain.tld más grandes que eso, así que solo admito la longitud máxima de una dirección de correo electrónico para una máxima compatibilidad.
TABLE sessions:
session_id - varchar(?), PK
session_token - varchar(?), NOT NULL
session_data - MediumText, NOT NULL
Tenga en cuenta que no hay ningún campo de usuario en esta tabla, porque el nombre de usuario, cuando está conectado, está en los datos de la sesión y el programa no permite datos nulos. El session_id y session_token se pueden generar usando hashes md5 aleatorios, sha1 / 128/256, sellos de fecha y hora con cadenas aleatorias agregadas y luego lo que quiera, pero la entropía de su salida debería ser tan alta como tolerable mitigue los ataques de fuerza bruta incluso despegando, y todos los hashes generados por su clase de sesión deben verificarse en la tabla de sesiones antes de intentar agregarlos.
TABLE autologin:
UID - auto increment, PK
username - varchar(255), NOT NULL, allow duplicates
hostname - varchar(255), NOT NULL, allow duplicates
mac_address - char(23), NOT NULL, unique
token - varchar(?), NOT NULL, allow duplicates
expires - datetime code
Las direcciones MAC por su naturaleza se supone que son ÚNICAS, por lo tanto, tiene sentido que cada entrada tenga un valor único. Los nombres de host, por otro lado, podrían duplicarse en redes separadas legítimamente. ¿Cuántas personas usan "Home-PC" como uno de sus nombres de computadora? El servidor backend toma el nombre de usuario de los datos de la sesión, por lo que es imposible manipularlo. En cuanto al token, se debe utilizar el mismo método para generar tokens de sesión para las páginas para generar tokens en cookies para el inicio de sesión automático del usuario. Por último, se agrega el código de fecha y hora para cuando el usuario deba volver a validar sus credenciales. Actualice esta fecha y hora en el inicio de sesión del usuario manteniéndolo dentro de unos pocos días, o obligue a que caduque, independientemente del último inicio de sesión, manteniéndolo solo durante aproximadamente un mes, lo que dicte su diseño.
Esto evita que alguien falsifique de forma sistemática el MAC y el nombre de host de un usuario que conozcan que inicie sesión automáticamente. NUNCA haga que el usuario guarde una cookie con su contraseña, texto simple u otro. Haga que el token se regenere en cada navegación de página, tal como lo haría con el token de sesión. Esto reduce enormemente la posibilidad de que un atacante pueda obtener una cookie de token válida y utilizarla para iniciar sesión. Algunas personas intentarán decir que un atacante podría robar las cookies de la víctima y hacer un ataque de repetición de sesión para iniciar sesión. Si un atacante pudiera robar las cookies (lo cual es posible), seguramente habrían comprometido todo el dispositivo, lo que significa que podrían usar el dispositivo para iniciar sesión de todos modos, lo que anula el propósito de robar las cookies por completo. Siempre que su sitio se ejecute a través de HTTPS (lo que debería ocurrir cuando se trata de contraseñas, números CC u otros sistemas de inicio de sesión), se le ofrece al usuario toda la protección que puede dentro de un navegador.
Una cosa a tener en cuenta: los datos de la sesión no deben caducar si utiliza el inicio de sesión automático. Puede caducar la capacidad de continuar la sesión de forma falsa, pero la validación en el sistema debe reanudar los datos de la sesión si se trata de datos persistentes que se espera que continúen entre las sesiones. Si desea datos de sesión persistentes Y no persistentes, use otra tabla para los datos de sesión persistentes con el nombre de usuario como PK, y haga que el servidor la recupere como lo haría con los datos de sesión normales, solo use otra variable.
Una vez que se ha logrado un inicio de sesión de esta manera, el servidor todavía debe validar la sesión. Aquí es donde puede codificar las expectativas de sistemas robados o comprometidos; Los patrones y otros resultados esperados de los inicios de sesión a los datos de sesión a menudo pueden llevar a conclusiones de que un sistema fue secuestrado o que las cookies fueron falsificadas para obtener acceso. Aquí es donde su ISS Tech puede establecer reglas que podrían activar el bloqueo de una cuenta o la eliminación automática de un usuario del sistema de inicio de sesión automático, manteniendo a los atacantes el tiempo suficiente para que el usuario determine cómo el atacante tuvo éxito y cómo eliminarlos.
Como nota final, asegúrese de que cualquier intento de recuperación, cambios de contraseña o fallos de inicio de sesión más allá del umbral resulten en que el inicio de sesión automático se deshabilite hasta que el usuario valide correctamente y reconozca que esto ha ocurrido.
Pido disculpas si alguien esperaba que el código fuera dado en mi respuesta, eso no va a suceder aquí. Diré que uso PHP, jQuery y AJAX para ejecutar mis sitios, y NUNCA utilizo Windows como servidor ... nunca.
Mi solución es así. No es 100% a prueba de balas, pero creo que te ahorrará la mayor parte de los casos.
Cuando el usuario haya iniciado sesión correctamente, cree una cadena con esta información:
$data = (SALT + ":" + hash(User Agent) + ":" + username
+ ":" + LoginTimestamp + ":"+ SALT)
Cifre $data
, establezca el tipo en HttpOnly y configure la cookie.
Cuando el usuario vuelva a su sitio, realice estos pasos:
- Obtener datos de cookies. Eliminar los caracteres peligrosos dentro de la cookie. Explotarlo con
:
personaje. - Comprobar validez. Si la cookie es anterior a X días, redirija al usuario a la página de inicio de sesión.
- Si la galleta no es vieja; Obtener la última hora de cambio de contraseña de la base de datos. Si la contraseña se cambia después del último inicio de sesión, el usuario redirigirá al usuario a la página de inicio de sesión.
- Si el pase no fue cambiado recientemente; Obtener el agente del navegador actual del usuario. Compruebe si (currentUserAgentHash == cookieUserAgentHash). Si los agentes son los mismos, vaya al siguiente paso, de lo contrario, redirija a la página de inicio de sesión.
- Si todos los pasos pasaron con éxito, autorice el nombre de usuario.
Si el usuario cierra la sesión, elimine esta cookie. Crear una nueva cookie si el usuario vuelve a iniciar sesión.
Recomendaría el enfoque mencionado por Stefan (es decir, siga las pautas de Mejores prácticas recomendadas de cookies de inicio de sesión persistentes ) y también le recomiendo que se asegure de que sus cookies sean cookies de HttpOnly para que no sean accesibles a JavaScript potencialmente malicioso.
Introducción
Su título "Mantenerme conectado": el mejor enfoque me dificulta saber por dónde empezar, porque si está considerando el mejor enfoque, tendrá que considerar lo siguiente:
- Identificación
- Seguridad
Galletas
Las cookies son vulnerables. Entre las vulnerabilidades comunes de robo de cookies del navegador y los ataques de scripts entre sitios, debemos aceptar que las cookies no son seguras. Para ayudar a mejorar la seguridad, debe tener en cuenta que php
setcookies
tiene una funcionalidad adicional, como
bool setcookie (string $ name [, string $ value [, int $ expire = 0 [, string $ path [, string $ domain [, bool $ secure = false [, bool $ httponly = false]]]]])
- seguro (utilizando la conexión HTTPS)
- httponly (Reducir el robo de identidad a través del ataque XSS)
Definiciones
- Token (cadena aleatoria impredecible de n longitud, por ejemplo, / dev / urandom)
- Referencia (cadena aleatoria impredecible de n longitud, por ejemplo, / dev / urandom)
- Firma (generar un valor de hash con clave utilizando el método HMAC)
Enfoque simple
Una solución simple sería:
- El usuario ha iniciado sesión con Recordarme
- Cookie de inicio de sesión con token y firma
- Cuando regresa, la firma está marcada.
- Si la firma es correcta ... se buscarán el nombre de usuario y el token en la base de datos
- si no es válido ... volver a la página de inicio de sesión
- Si es válido iniciar sesión automáticamente
El estudio de caso anterior resume todos los ejemplos dados en esta página, pero sus desventajas son que
- No hay forma de saber si las cookies fueron robadas.
- El atacante puede tener acceso a operaciones confidenciales, como el cambio de contraseña o datos como información personal y de horneado, etc.
- La cookie comprometida aún sería válida para la vida útil de la cookie.
Mejor solucion
Una mejor solución sería
- El usuario ha iniciado sesión y recuérdame está seleccionado
- Generar Token y firma y almacenar en cookie
- Los tokens son aleatorios y solo son validos para autenticacion individual
- Las fichas se reemplazan en cada visita al sitio.
- Cuando un usuario no registrado visita el sitio, se verifican la firma, el token y el nombre de usuario
- Recuerde que el inicio de sesión debe tener acceso limitado y no permitir la modificación de contraseñas, información personal, etc.
Código de ejemplo
// Set privateKey
// This should be saved securely
$key = ''fc4d57ed55a78de1a7b31e711866ef5a2848442349f52cd470008f6d30d47282'';
$key = pack("H*", $key); // They key is used in binary form
// Am Using Memecahe as Sample Database
$db = new Memcache();
$db->addserver("127.0.0.1");
try {
// Start Remember Me
$rememberMe = new RememberMe($key);
$rememberMe->setDB($db); // set example database
// Check if remember me is present
if ($data = $rememberMe->auth()) {
printf("Returning User %s/n", $data[''user'']);
// Limit Acces Level
// Disable Change of password and private information etc
} else {
// Sample user
$user = "baba";
// Do normal login
$rememberMe->remember($user);
printf("New Account %s/n", $user);
}
} catch (Exception $e) {
printf("#Error %s/n", $e->getMessage());
}
Clase utilizada
class RememberMe {
private $key = null;
private $db;
function __construct($privatekey) {
$this->key = $privatekey;
}
public function setDB($db) {
$this->db = $db;
}
public function auth() {
// Check if remeber me cookie is present
if (! isset($_COOKIE["auto"]) || empty($_COOKIE["auto"])) {
return false;
}
// Decode cookie value
if (! $cookie = @json_decode($_COOKIE["auto"], true)) {
return false;
}
// Check all parameters
if (! (isset($cookie[''user'']) || isset($cookie[''token'']) || isset($cookie[''signature'']))) {
return false;
}
$var = $cookie[''user''] . $cookie[''token''];
// Check Signature
if (! $this->verify($var, $cookie[''signature''])) {
throw new Exception("Cokies has been tampared with");
}
// Check Database
$info = $this->db->get($cookie[''user'']);
if (! $info) {
return false; // User must have deleted accout
}
// Check User Data
if (! $info = json_decode($info, true)) {
throw new Exception("User Data corrupted");
}
// Verify Token
if ($info[''token''] !== $cookie[''token'']) {
throw new Exception("System Hijacked or User use another browser");
}
/**
* Important
* To make sure the cookie is always change
* reset the Token information
*/
$this->remember($info[''user'']);
return $info;
}
public function remember($user) {
$cookie = [
"user" => $user,
"token" => $this->getRand(64),
"signature" => null
];
$cookie[''signature''] = $this->hash($cookie[''user''] . $cookie[''token'']);
$encoded = json_encode($cookie);
// Add User to database
$this->db->set($user, $encoded);
/**
* Set Cookies
* In production enviroment Use
* setcookie("auto", $encoded, time() + $expiration, "/~root/",
* "example.com", 1, 1);
*/
setcookie("auto", $encoded); // Sample
}
public function verify($data, $hash) {
$rand = substr($hash, 0, 4);
return $this->hash($data, $rand) === $hash;
}
private function hash($value, $rand = null) {
$rand = $rand === null ? $this->getRand(4) : $rand;
return $rand . bin2hex(hash_hmac(''sha256'', $value . $rand, $this->key, true));
}
private function getRand($length) {
switch (true) {
case function_exists("mcrypt_create_iv") :
$r = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
break;
case function_exists("openssl_random_pseudo_bytes") :
$r = openssl_random_pseudo_bytes($length);
break;
case is_readable(''/dev/urandom'') : // deceze
$r = file_get_contents(''/dev/urandom'', false, null, 0, $length);
break;
default :
$i = 0;
$r = "";
while($i ++ < $length) {
$r .= chr(mt_rand(0, 255));
}
break;
}
return substr(bin2hex($r), 0, $length);
}
}
Pruebas en Firefox & Chrome
Ventaja
- Mejor seguridad
- Acceso limitado para el atacante
- Cuando la cookie es robada solo es válida para un acceso único
- La próxima vez que el usuario original acceda al sitio, puede detectar y notificar automáticamente el robo al usuario.
Desventaja
- No admite la conexión persistente a través de múltiples navegadores (móvil y web)
- La cookie aún puede ser robada porque el usuario solo recibe la notificación después del siguiente inicio de sesión.
Arreglo rapido
- Introducción de sistema de aprobación para cada sistema que debe tener conexión persistente.
- Utilizar múltiples cookies para la autenticación.
Enfoque de cookies múltiples
Cuando un atacante está a punto de robar cocineros, solo se enfoca en un sitio web o dominio en particular, por ejemplo. example.com
Pero realmente puede autenticar a un usuario de 2 dominios diferentes ( example.com y fakeaddsite.com ) y hacer que se vea como "Advert Cookie"
- Usuario ha iniciado sesión en example.com con recordarme
- Tienda de nombre de usuario, token, referencia en cookie
- Almacenar nombre de usuario, token, referencia en la base de datos, por ejemplo. Memcache
- Enviar ID de acceso mediante get y iframe a fakeaddsite.com
- fakeaddsite.com usa la referencia para obtener usuario y token de la base de datos
- fakeaddsite.com almacena la firma
- Cuando un usuario está devolviendo la información de la firma de recuperación con iframe desde fakeaddsite.com
- Combina los datos y haz la validación.
- ..... sabes lo que queda
Algunas personas podrían preguntarse cómo puede usar 2 cookies diferentes. Bueno, es posible, imagine example.com = localhost
y fakeaddsite.com = 192.168.1.120
. Si inspeccionas las cookies se vería así.
De la imagen de arriba.
- El sitio actual visitado es localhost
- También contiene cookies establecidas desde 192.168.1.120.
192.168.1.120
- Solo acepta
HTTP_REFERER
definido - Sólo acepta la conexión de
REMOTE_ADDR
especificado - Sin JavaScript, sin contenido, pero no consiste más que en firmar información y agregarla o recuperarla de una cookie
Ventaja
- El 99% de las veces has engañado al atacante.
- Puede bloquear fácilmente la cuenta en el primer intento del atacante
- El ataque se puede prevenir incluso antes del siguiente inicio de sesión como con los otros métodos.
Desventaja
- Solicitud múltiple al servidor solo para un inicio de sesión único
Mejora
- Hecho use iframe use
ajax
Implementar una función "Mantener mi sesión iniciada" significa que debe definir exactamente lo que significará para el usuario. En el caso más simple, lo usaría para indicar que la sesión tiene un tiempo de espera mucho más largo: 2 días (por ejemplo) en lugar de 2 horas. Para hacerlo, necesitará su propio almacenamiento de sesión, probablemente en una base de datos, por lo que puede establecer tiempos de caducidad personalizados para los datos de la sesión. Luego, debe asegurarse de establecer una cookie que se mantendrá durante unos días (o más), en lugar de caducar cuando cierran el navegador.
Puedo escucharte preguntando "¿por qué 2 días? ¿Por qué no 2 semanas?". Esto se debe a que el uso de una sesión en PHP hará retroceder automáticamente el vencimiento. Esto se debe a que la expiración de una sesión en PHP es en realidad un tiempo de espera inactivo.
Ahora, dicho esto, probablemente implementaría un valor de tiempo de espera más difícil que almacené en la sesión misma, y en aproximadamente 2 semanas, y agregaría código para ver eso y para invalidar por la fuerza la sesión. O al menos para desconectarlos. Esto significará que se pedirá al usuario que inicie sesión periódicamente. Yahoo! Haz esto.
Leí todas las respuestas y todavía encontraba difícil extraer lo que debía hacer. Si una imagen vale 1k palabras, espero que esto ayude a otros a implementar un almacenamiento seguro y persistente basado en la Mejor Práctica de la Cookie de Inicio de Sesión Persistente de Barry Jaspan
Si tiene preguntas, comentarios o sugerencias, intentaré actualizar el diagrama para reflejar al novato que intenta implementar un inicio de sesión seguro y persistente.
No entiendo el concepto de almacenar cosas encriptadas en una cookie cuando es la versión encriptada de lo que necesitas para piratear. Si me falta algo, por favor comenta.
Estoy pensando en tomar este enfoque para ''Recordarme''. Si puedes ver algún problema, por favor comenta.
Cree una tabla para almacenar los datos de "Recordarme", de forma independiente a la tabla de usuarios, de modo que pueda iniciar sesión desde varios dispositivos.
Al iniciar sesión correctamente (con Recordarme marcado):
a) Genere una cadena aleatoria única para usarla como UserID en esta máquina: bigUserID
b) Generar una cadena aleatoria única: bigKey
c) Almacenar una cookie: bigUserID: bigKey
d) En la tabla "Recordarme", agregue un registro con: ID de usuario, dirección IP, bigUserID, bigKey
Si intentas acceder a algo que requiere inicio de sesión:
a) Verifique la cookie y busque bigUserID y bigKey con una dirección IP coincidente
b) Si lo encuentra, inicie sesión en la persona pero establezca una marca en la tabla de usuario "inicio de sesión suave" para que, ante cualquier operación peligrosa, pueda solicitar un inicio de sesión completo.
Al cerrar sesión, marque todos los registros de "Recordarme" para ese usuario como caducados.
Las únicas vulnerabilidades que puedo ver es;
- Usted podría obtener la computadora portátil de alguien y falsificar su dirección IP con la cookie.
- usted podría simular una dirección IP diferente cada vez y adivinar todo el asunto, pero con dos cadenas grandes que coincidan, eso sería ... hacer un cálculo similar al anterior ... No tengo idea ... ¿grandes probabilidades?