¿Este token CSRF generado se consideraría criptográficamente sólido?

2

Estoy trabajando para mejorar la seguridad de una aplicación web existente que actualmente no ha implementado el uso de tokens anti-CSRF, por lo que depende de mí generar una, agregar los campos ocultos y las comprobaciones, etc. Un derecho de limitación ahora es que ni los módulos openssl o mcrypt están configurados actualmente en el servidor.

Aquí está mi código de generación de tokens:

$csrfToken = base64_encode(uniqid(mt_rand(100, 999) . microtime(), true));

Tengo la sensación de que esto proporcionaría una ficha adecuada, pero no particularmente fuerte, pero ese es mi instinto personal. ¿Alguien tiene alguna sugerencia sobre cómo puedo reforzar la seguridad de este token sin el uso de las funciones mcrypt o openssl? ¡Gracias!

EDITAR: esta es actualmente la forma en que estoy verificando el token después de enviar el formulario, donde $ token es el token pasado junto con la solicitud:

function validate_token($token) {
    return isset($_SESSION['csrfToken']) && $token === $_SESSION['csrfToken'];
}
    
pregunta hRdCoder 24.12.2014 - 22:56
fuente

4 respuestas

3

Yo diría que no. Aquí es un artículo interesante sobre un tema similar.

sugeriría:

  1. Usar una función hash (esto no agrega virtualmente ninguna seguridad por sí solo, pero hace que las mejoras mencionadas a continuación sean mucho más eficientes), por ejemplo. %código%
  2. Agregar una constante secreta larga y aleatoria (esto agrega seguridad mientras que la constante secreta permanece secreta , este valor puede ser muy fácil de leer en un servidor de alojamiento compartido), por ejemplo. %código%
  3. Leyendo algunos bytes de $csrfToken = base64_encode(hash("sha256", uniqid(mt_rand(100, 999) . microtime(), true), true)); si está disponible y agregándolos al valor que se oculta ( esto agrega seguridad real sin el uso de las extensiones de PHP mencionadas en la publicación de la pregunta), por ejemplo. %código%
  4. Agregar el puerto y la IP del cliente (esto mejora la seguridad un poco, especialmente si el atacante no conoce la dirección IP del cliente), por ejemplo. %código%
  5. Esto no mejora la seguridad, pero hace que el algoritmo de generación sea más comprensible: elimine el material uniqid () y reemplácelo con $csrfToken = base64_encode(hash("sha256", "nS7W7@IEPJ~6&&vp#r>ziW-SGOC?s>!,.(s" . uniqid(mt_rand(100, 999) . microtime(), true), true)); puro, por ejemplo. %código%
  6. (Agregado más tarde) Agregue el encabezado de solicitud HTTP /dev/urandom a la entrada de la función hash. Esto tiene un doble efecto en la seguridad: una buena parte es que el token CSRF también depende de la cookie de sesión (por lo que el atacante debe conocer / adivinar la cookie de sesión de la víctima para calcular el token CSRF: en la mayoría de los casos, la cookie de sesión es más crítica); Lo malo es que el atacante puede intentar "adivinar" las cookies de sesión basadas en cualquier token de CSRF guardado en el historial del navegador. Para hacer que esta suposición sea más complicada computacionalmente (a expensas de los recursos del servidor), el encabezado $handle = fopen("/dev/urandom", "rb"); $urandom_data = fread($handle, 16); if(strlen($urandom_data)!==16) exit("Unable to read /dev/urandom"); fclose($handle); $csrfToken = base64_encode(hash("sha256", $urandom_data . "nS7W7@IEPJ~6&&vp#r>ziW-SGOC?s>!,.(s" . uniqid(mt_rand(100, 999) . microtime(), true), true)); se puede procesar solo antes de agregarlo, por ejemplo. %código%

P.S. Observe que las cadenas de dígitos de longitud variable se concatenan utilizando separadores sin dígitos (para evitar que algunos conjuntos de cadenas no equivalentes den como resultado valores concatenados equivalentes).

P.S.2 Parece que falta un corchete (" $handle = fopen("/dev/urandom", "rb"); $urandom_data = fread($handle, 16); if(strlen($urandom_data)!==16) exit("Unable to read /dev/urandom"); fclose($handle); $csrfToken = base64_encode(hash("sha256", $urandom_data . $_SERVER["REMOTE_ADDR"] . ":" . $_SERVER["REMOTE_PORT"] . "nS7W7@IEPJ~6&&vp#r>ziW-SGOC?s>!,.(s" . uniqid(mt_rand(100, 999) . microtime(), true), true)); ") en el código de la pregunta.

P.S.3 Más sobre el significado de la función hash criptográfica aquí. La función de hash hace lo siguiente:

  1. Agrega NO entropía : las funciones son deterministas (la función hash siempre producirá la misma salida de cada entrada particular). Es importante comprender que no se agrega entropía (incluso si se aumenta la longitud de la cuerda). SHA-256 de la cadena "A" tendrá una longitud de 32 bytes, pero seguirá siendo un valor bien conocido (puedes simplemente buscar en Google este hash en hexadecimal para descubrir qué tan popular es).
  2. Mezcla y confunde (esta vez ofuscado no significa algo evitable e inseguro) la entrada: la única forma en que el atacante puede determinar la entrada es adivinándola. Básicamente, la función hash introduce un principio de todo o nada: o el atacante conoce la entrada completa (y puede verificarla calculando el hash) o el atacante no sabe qué partes de la entrada que predijo son correctas. Como resultado, la función hash permite aumentar la seguridad agregando una constante secreta larga y aleatoria (paso 2 mencionado anteriormente), sin el uso de la función hash, el atacante vería la constante en todos los tokens CSRF observados (así sabría cuál es la constante y que hay que añadir).
  3. (Muy relacionado con 2.) Hace al atacante gastar algunos recursos computacionales con cada "conjetura" de la entrada de la función hash. En el artículo mencionado anteriormente, el atacante usa GPU (tarjeta de video), ya que debe hacer un gran número de "suposiciones" y eso llevaría mucho tiempo en la CPU. Cuanto más compleja (menos predecible) sea la entrada de la función hash, más recursos computacionales deberá usar el atacante para adivinar la entrada. Si el atacante tiene suficientes recursos computacionales para "adivinar" la entrada de la función hash utilizada para generar el token CSRF (como se dijo antes, la cantidad de recursos computacionales necesarios depende de la complejidad / previsibilidad de la entrada), puede muestrear la entrada y comenzar a analice cómo se compone, identifique partes constantes, etc.

Para obtener más información sobre la aplicación de funciones hash criptográficas, los siguientes artículos de Wikipedia son relevantes: Función hash criptográfica , código de autenticación de mensaje basado en hash .

P.S.4 Parcial fuera de tema: como se mencionó anteriormente, puede ser fácil para un atacante extraer el código de la constante y la generación al atacar el entorno de alojamiento compartido.

    
respondido por el DavisNT 24.12.2014 - 23:45
fuente
1

No. No soy un desarrollador de PHP, pero encontré esto en la función uniqID que estás usando:

página de manual de UniqID

Advertencia

Esta función no crea cadenas aleatorias ni impredecibles. Esta función no debe usarse por razones de seguridad. Use una función / generador aleatorio criptográficamente seguro y funciones hash criptográficamente seguras. para crear identificaciones seguras impredecibles.

Consulte este hilo para generar números aleatorios seguros en PHP, Como está fuera de mi alcance y en gran medida esto es a lo que se reduce tu pregunta. Si está obteniendo 128 bits de entropía, la codificación o la puesta a través de otras funciones de hash no es en gran medida importante.

    
respondido por el Steve Sether 26.12.2014 - 22:10
fuente
0

No intente reinventar la rueda y no intente reinventar una solución para números aleatorios únicos. Si su lenguaje o marco proporciona una función para generar un UUID al azar, vaya con eso. Es posible que sepas lo que estás haciendo, pero acabas de depurar y dar soporte. El lenguaje o el marco tiene una compañía u organización detrás de él para asegurarse de que lo hagan bien.

    
respondido por el DTK 28.12.2014 - 02:52
fuente
0

La respuesta aceptada parece ser proporcionar aceite de serpiente junto con un buen consejo.

Para la generación de tokens CSRF específicamente, solo hay unos pocos requisitos:

  • impredecible
  • alguna resistencia a la fuerza bruta
  • utiliza caracteres imprimibles que son compatibles con la tecnología web

Todo lo que necesitas hacer para lograr esto es generar suficiente aleatoriedad criptográficamente segura para evitar un intento de fuerza bruta modesta (en comparación con, digamos, descifrado de contraseñas fuera de línea) y luego codificarlo por base64.

Puede ver qué utiliza ampliamente la comunidad de Clojure enlace como parte del middleware anti-falsificación (evita el CSRF).

(defn- new-token [] (random/base64 60))

La clave es que la función random/base64 que se llama aquí utiliza la clase criptográficamente segura SecureRandom bajo el capó. El 60 se refiere a 60 bytes de datos aleatorios para codificar en base64.

Eso es todo.

Puede obtener el mismo resultado si obtiene datos aleatorios de /dev/urandom .

Aquí hay problemas con las otras sugerencias para el algoritmo de generación de tokens:

  • Uso de mt_rand : no use esto en absoluto, no es adecuado para este propósito y es exactamente contra lo que advierte el artículo vinculado en la respuesta aceptada (pero luego la respuesta aceptada va) para utilizar mt_rand en ejemplos de código ...)
  • El uso de uniq_id : no crea cadenas impredecibles o aleatorias, según los documentos PHP
  • Usar una función hash: reduce el conjunto de caracteres, pero no agrega ninguna seguridad. La codificación Base64 es más clara sobre el propósito y no implica una funcionalidad de seguridad que simplemente no existe
  • Uso de una clave secreta: en otros contextos, las claves secretas son críticas; en este contexto, simplemente le proporciona algo que debe mantener en secreto (no es algo fácil de hacer), agregando complejidad y dificultad sin beneficios adicionales de seguridad
  • añadiendo IP / puerto / etc del cliente. - No hace que el token sea más seguro que los datos cripto-aleatorios, pero sí hace que el algoritmo sea más complicado y menos portátil
  • Uso de microtime: no agrega ninguna seguridad adicional, la hora actual es lo suficientemente predecible como para ser forzada. Si consideramos que mt_rand es inseguro, seguramente una marca de tiempo también lo es.

Esencialmente, cualquier cosa que haga que agregue complejidad sin cumplir los requisitos fundamentales simplemente hace que el algoritmo:

  • Menos mantenible
  • Más difícil de entender
  • Es más probable que tenga un error crítico
  • Menos seguro
respondido por el David Meister 31.05.2017 - 12:23
fuente

Lea otras preguntas en las etiquetas