php - remove - strip_tags wordpress
¿Qué tan seguro es mi sistema de inicio de sesión de PHP? (5)
Soy nuevo en PHP y este es también mi primer sistema de inicio de sesión, así que sería genial si ustedes pudieran revisar mi código y ver si pueden detectar algún agujero de seguridad:
Nota: Estoy desinfectando todas las entradas de los usuarios, aunque no se muestra aquí.
Regístrate:
Paso 1: tomo la contraseña que el usuario eligió y la ejecuto a través de esta función:
encrypt($user_chosen_password, $salt);
function encrypt($plain_text, $salt) {
if(!$salt) {
$salt = uniqid(rand(0, 1000000));
}
return array(
''hash'' => $salt.hash(''sha512'', $salt.$plain_text),
''salt'' => $salt
);
}
Paso 2: Guardo el hash y el salt ( $password[''hash'']
y $password[''salt'']
) en la tabla de usuarios en la base de datos:
id | username | password | salt | unrelated info...
-----------------------------------------------------------
1 | bobby | 809a28377 | 809a28377f | ...
fd131e5934
180dc24e15
bbe5f8be77
371623ce36
4d5b851e46
Iniciar sesión:
Paso 1: tomo el nombre de usuario que el usuario ingresó y busco en la base de datos para ver si se devolvieron las filas. En mi sitio, no hay 2 usuarios que puedan compartir el mismo nombre de usuario, por lo que el campo de nombre de usuario siempre tiene un valor único. Si recibo 1 fila devuelta, tomo la sal para ese usuario.
Paso 2: Luego ejecuto la contraseña ingresada por el usuario a través de la función de cifrado (como se publicó anteriormente) pero esta vez también proporciono la sal recuperada de la base de datos:
encrypt($user_entered_password, $salt);
Paso 3: ahora tengo la contraseña adecuada para comparar en esta variable: $password[''hash'']
. Así que hice una segunda búsqueda en la base de datos para ver si el nombre de usuario ingresado y la contraseña hash juntas devuelven una sola fila. Si es así, las credenciales del usuario son correctas.
Paso 4: para registrar al usuario después de que se hayan pasado sus credenciales, genero una cadena aleatoria única y la hash:
$random_string = uniqid(rand(0, 1000000));
$session_key = hash(''sha512'', $random_string);
Luego inserto $session_key
en la tabla active_sessions
en la base de datos:
user_id | key
------------------------------------------------------------
1 | 431b5f80879068b304db1880d8b1fa7805c63dde5d3dd05a5b
Paso 5:
Tomo la cadena única no cifrada generada en el último paso ( $random_string
) y la configuro como el valor de una cookie a la que llamo active_session
:
setcookie(''active_session'', $random_string, time()+3600*48, ''/'');
Paso 6:
En la parte superior de mi header.php
incluye esta verificación:
if(isset($_COOKIE[''active_session'']) && !isset($_SESSION[''userinfo''])) {
get_userinfo();
}
La función get_userinfo()
realiza una búsqueda en la tabla de users
en la base de datos y devuelve una matriz asociativa que se almacena en una sesión llamada userinfo
:
// primero esta función toma el valor de la cookie active_session y la mezcla para obtener la session_key:
hash(''sha512'', $random_string);
// luego realiza una búsqueda en la tabla active_sessions
para ver si existe un registro con esta key
, si es así tomará el user_id
asociado con ese registro y lo usará para hacer una segunda búsqueda en la tabla de users
para obtener la información de userinfo
:
$_SESSION[''userinfo''] = array(
''user_id'' => $row->user_id,
''username'' => $row->username,
''dob'' => $row->dob,
''country'' => $row->country,
''city'' => $row->city,
''zip'' => $row->zip,
''email'' => $row->email,
''avatar'' => $row->avatar,
''account_status'' => $row->account_status,
''timestamp'' => $row->timestamp,
);
Si existe la sesión de userinfo
, sé que el usuario está autenticado. Si no existe, pero existe la cookie active_session
, esa comprobación en la parte superior del archivo header.php
creará esa sesión.
La razón por la que estoy usando una cookie y no solo por las sesiones es persistir en el inicio de sesión. Entonces, si el usuario cierra el navegador, la sesión puede haber desaparecido, pero la cookie seguirá existiendo. Y dado que existe esa verificación en la parte superior de header.php
, la sesión se volverá a crear y el usuario puede funcionar como un usuario conectado como siempre.
Cerrar sesión:
Paso 1: tanto la sesión userinfo
como la cookie active_session
están desarmadas.
Paso 2: se elimina el registro asociado de la tabla active_sessions
en la base de datos.
Notas: El único problema que puedo ver (y tal vez hay muchos otros), es si el usuario falsifica esa cookie active_session
al crearla ellos mismos en su navegador. Por supuesto, deben establecer como valor de esa cookie una cadena que, una vez encriptada, debe coincidir con un registro en la tabla active_sessions
desde donde recuperaré el user_id
para crear esa sesión. No estoy seguro de cuáles son las posibilidades de esto de manera realista, para un usuario (tal vez usando un programa automatizado) adivinar correctamente una cadena que no sabe que será sha512 encriptada y emparejada con la cadena en la tabla active_sessions
en la base de datos para obtener la identificación de usuario para construir esa sesión.
Perdón por el gran ensayo, pero como esta es una parte tan crítica de mi sitio y debido a mi inexperiencia solo quería ejecutarlo por desarrolladores más experimentados para asegurarme de que en realidad sea seguro.
¿Ves algún agujero de seguridad en esta ruta y cómo se puede mejorar?
... ejecuta la contraseña ingresada por el usuario a través de la función de cifrado ...
Entonces, ¿cómo se transfiere la contraseña del navegador al servidor? No has mencionado proteger contra los ataques de hombre en el medio.
Debe incluir algún tipo de tiempo de espera o failover para prevenir ataques de fuerza bruta. Hay varias formas de hacerlo, incluido el bloqueo basado en IP, los tiempos de espera incrementales, etc. Ninguno de estos detendrá alguna vez a un pirata informático, pero pueden hacerlo mucho más difícil.
Otro punto (que no has mencionado, por lo que no conozco tu plan) son los mensajes de falla. Haga que los mensajes de falla sean lo más imprecisos posible. Proporcionar un mensaje de error como ''Ese nombre de usuario existe, pero las contraseñas no coinciden'' podría ser útil para el usuario final, pero mata la funcionalidad de inicio de sesión. Acaba de convertir un ataque de fuerza bruta que debería tomar O(n^2)
tiempo a O(n)
+ O(n)
. En lugar de necesitar probar cada permutación en una tabla de arcoiris (por ejemplo), el pirata informático primero prueba todos los valores de nombre de usuario (con una contraseña establecida) primero, hasta que el mensaje de error cambie. Entonces, conoce a un usuario válido, y solo tiene que usar la fuerza bruta de la contraseña.
En ese sentido, también debe asegurarse de que transcurra la misma cantidad de tiempo cuando existe un nombre de usuario y no existe. Está ejecutando procesos adicionales cuando realmente existe un nombre de usuario. Como tal, el tiempo de respuesta sería más largo cuando existe un nombre de usuario vs cuando no lo hace. Un pirata informático increíblemente hábil podría cronometrar las solicitudes de páginas para encontrar un nombre de usuario válido.
Del mismo modo, debe asegurarse de que, además de las cookies que caducan, también expire la tabla de sesiones.
Por último, en la llamada get_user_info()
, debe finalizar todas las sesiones abiertas si hay múltiples inicios de sesión activos y simultáneos. Asegúrese de finalizar las sesiones después de una cantidad establecida de inactividad (como 30 minutos).
En la línea de lo que mencionó @Greg Hewgill, no ha incluido ninguno de los siguientes:
- Conexión SSL / encriptada entre el Servidor-Cliente
- Otros protocolos de transporte que utilizará para procesar la autenticación (como OAuth)
Su servidor es seguro, pero no importa qué tan increíblemente seguro sea su algoritmo si alguien puede leer los datos intercambiados (MITM). Debes asegurarte de que solo estás comunicándote a través de un protocolo encriptado.
Deberías usar mt_rand () en lugar de rand ()
Este es el por qué:
- http://php.net/manual/en/function.rand.php#73730
- https://.com/a/28760905/2056484
Este código ...
function encrypt($plain_text, $salt) {
if(!$salt) {
$salt = uniqid(rand(0, 1000000));
}
return array(
''hash'' => $salt.hash(''sha512'', $salt.$plain_text),
''salt'' => $salt
);
}
...es malo. Use la nueva API de contraseña y termine con ella. A menos que sea un experto, no debe intentar diseñar su propio sistema de autenticación. Son extremadamente difíciles de entender .
Para registrar al usuario después de que se hayan pasado sus credenciales, genero una cadena aleatoria única y la hash:
Simplemente deja que PHP maneje la administración de la sesión . rand()
y mt_rand()
son generadores de números aleatorios muy inseguros.
Parece que el código que creó no se puede probar a través de unidades unitarias y pruebas de integración. Esto hace que sea difícil detectar cualquier error que pueda estar incluido en su implementación a lo largo del tiempo y mientras se ejecuta en un entorno de producción.
Esto normalmente conduce a problemas de seguridad, porque la seguridad de una ejecución estricta y correcta y el manejo de datos en buen estado no se prueban ni verifican.
(Es solo otro punto de la lista, vea también la respuesta sobre cómo proteger la capa de transporte, y tampoco ha especificado cómo proteger los datos de su sesión para que no se alteren).