¿Existe alguna implementación de javascript SHA-256 que generalmente se considere confiable?

95

Estoy escribiendo un inicio de sesión para un foro y necesito codificar la contraseña del lado del cliente en javascript antes de enviarla al servidor. Tengo problemas para averiguar en qué implementación de SHA-256 realmente puedo confiar. Esperaba que hubiera algún tipo de script autorizado que todos usaran, pero estoy encontrando muchos proyectos diferentes, todos con sus propias implementaciones.

Me doy cuenta de que usar las criptomonedas de otras personas es siempre un acto de fe a menos que esté calificado para revisarlo usted mismo, y que no existe una definición universal de "confiable", pero esto parece algo común y lo suficientemente importante como para que haya algún tipo de de consenso sobre qué utilizar. ¿Soy simplemente ingenuo?

Edite ya que aparece mucho en los comentarios: Sí, volvemos a hacer un hash más estricto en el lado del servidor. El hash del lado del cliente no es el resultado final que guardamos en la base de datos. El hash del lado del cliente se debe a que el cliente humano lo solicita. No han dado una razón específica por la cual, probablemente solo les gusta la exageración.

jono
fuente
9
No quiero salirte del tema, pero ¿por qué estás usando la contraseña en el lado del cliente?
Steve
5
@ddyer Ni siquiera cerca. "Don't roll your own" se aplica a inventar su propio algoritmo, escribir su propia implementación de un algoritmo, desarrollar su propio protocolo sobre algoritmos criptográficos o casi cualquier cosa anterior utilizando una abstracción de alto nivel como esté disponible. Si cree que estará seguro si se apega a un núcleo seguro y solo escribe código de pegamento, lo pasará mal.
Stephen Touset
26
si usa una contraseña hash sin un protocolo de desafío / respuesta, entonces la contraseña hash ES la contraseña y es realmente lo mismo que transmitir la contraseña en texto sin cifrar.
ddyer
26
@ddyer Es valioso proteger la contraseña de texto sin formato del usuario para todos los otros sitios en los que podrían usarla, si no para nuestro sitio en particular. Es una solución fácil que tal vez no nos ayude, pero que potencialmente puede ayudar al usuario si cometemos errores en alguna parte. Y como dije, solicitud del cliente, no puedo hacer nada al respecto, incluso si quisiera.
jono
4
@Anorov Estoy más que abierto a que me cambien de opinión :) pero en este caso, realmente no entiendo cómo se aplica tu punto. Hicimos hash la contraseña dos veces: una en el lado del cliente con un simple SHA-256, y una vez en el lado del servidor con algo más exigente. El primero para proteger el texto plano en caso de MITM o similar, y el segundo para protección bruta. Incluso si obtuvo la base de datos y el hash de administrador, no podría usarlo directamente para validar el inicio de sesión.
jono

Respuestas:

111

La biblioteca de cifrado de Stanford JS contiene una implementación de SHA-256. Si bien la criptografía en JS no es un esfuerzo tan bien investigado como otras plataformas de implementación, esta está al menos parcialmente desarrollada y hasta cierto punto patrocinada por Dan Boneh , que es un nombre bien establecido y confiable en criptografía. y significa que el proyecto está supervisado por alguien que realmente sabe lo que está haciendo. El proyecto también cuenta con el apoyo de la NSF .

Sin embargo, vale la pena señalar ...
... que si aplica un hash a la contraseña del lado del cliente antes de enviarla, entonces el hash es la contraseña y la contraseña original se vuelve irrelevante. Un atacante solo necesita interceptar el hash para hacerse pasar por el usuario, y si ese hash se almacena sin modificar en el servidor, entonces el servidor almacena la contraseña verdadera (el hash) en texto sin formato .

Entonces, su seguridad ahora es peor porque decidió agregar sus propias mejoras a lo que antes era un esquema confiable.

Tylerl
fuente
31
Sin embargo, si vuelve a hacer hash en el servidor, esta práctica es perfectamente legítima.
Nick Brunt
13
@NickBrunt si aplica el hash en el servidor, entonces no está guardando la contraseña transmitida, pero no ha agregado ninguna seguridad más allá de transmitir la contraseña original en lugar de transmitir el hash de la contraseña, ya que en cualquier caso lo que está transmitiendo es el contraseña real .
tylerl
54
Es cierto, pero no es peor que enviar una contraseña que posiblemente se use en otros lugares de Internet en texto sin formato.
Nick Brunt
15
Estoy de acuerdo con @NickBrunt. Es mejor si el atacante lee una cadena de hash aleatoria que puede encajar solo con esta aplicación en particular en lugar de la contraseña original que puede usarse en varios lugares.
Aebsubis
12
Necesito usar hash del lado del cliente porque no quiero que el servidor vea nunca la contraseña de texto sin formato del usuario. No para mayor seguridad al servicio que estoy brindando, sino hacia el usuario. No solo estoy guardando el hash del usuario salado con una sal constante (constante por usuario, no globalmente), sino que también lo vuelvo a hacer con una sal de sesión aleatoria en cada inicio de sesión, lo que SÍ proporciona un poco de seguridad adicional en la red contra el rastreo. Si el servidor se ve comprometido, se termina el juego, pero eso es cierto para cualquier cosa que no sea realmente p2p de todos modos.
Hatagashira
50

En https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest encontré este fragmento que usa el módulo js interno:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder('utf-8').encode(message);                    

    // hash the message
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

    // convert ArrayBuffer to Array
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    // convert bytes to hex string                  
    const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
    return hashHex;
}

Tenga crypto.subtleen cuenta que solo está disponible en httpso localhost, por ejemplo, para su desarrollo local python3 -m http.server, debe agregar esta línea a su /etc/hosts: 0.0.0.0 localhost

Reinicie y podrá abrir localhost:8000con el trabajo crypto.subtle.

Vitaly Zdanevich
fuente
2
Esto es asombroso. Gracias. ¿Hay una API comparable disponible en Node.js?
Con Antonakos
2
La biblioteca 'crypto' incorporada debería funcionar bien: nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho
17

La implementación de SHA-256 de Forge es rápida y confiable.

Para ejecutar pruebas en varias implementaciones de JavaScript SHA-256, vaya a http://brillout.github.io/test-javascript-hash-implementations/ .

Los resultados en mi máquina sugieren que Forge es la implementación más rápida y también considerablemente más rápida que la Stanford Javascript Crypto Library (sjcl) mencionada en la respuesta aceptada.

Forge tiene un tamaño de 256 KB, pero la extracción del código relacionado con SHA-256 reduce el tamaño a 4,5 KB, consulte https://github.com/brillout/forge-sha256

brillante
fuente
Logré instalarlo con npm install node-forge, pero ahora, ¿cómo usar la biblioteca para crear un hash a partir de la cadena foo?
usuario
15

Para aquellos interesados, este es un código para crear hash SHA-256 usando sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
Danny Sullivan
fuente
4
Esto debe incorporarse en la respuesta aceptada para aquellos que quieran utilizar la solución allí. (Diablos, incluso sus propios documentos no tienen un fragmento como este)
MagicLegend
Esta es la mejor respuesta. Estaba probando la solución criptográfica pero no funcionó para mí.
Augusto González
11

No, no hay forma de utilizar JavaScript del navegador para mejorar la seguridad de las contraseñas. Le recomiendo que lea este artículo . En su caso, el mayor problema es el problema del huevo de gallina:

¿Cuál es el "problema del huevo de gallina" con la entrega de criptografía Javascript?

Si no confía en la red para entregar una contraseña o, peor aún, no confía en que el servidor no guarde secretos de usuario, no puede confiar en ellos para entregar el código de seguridad. El mismo atacante que estaba husmeando contraseñas o leyendo diarios antes de que introduzcas la criptografía simplemente está secuestrando el código criptográfico después de que tú lo hagas.

[...]

¿Por qué no puedo usar TLS / SSL para entregar el código criptográfico Javascript?

Usted puede. Es más difícil de lo que parece, pero transmite de forma segura criptografía Javascript a un navegador mediante SSL. El problema es que, después de haber establecido un canal seguro con SSL, ya no necesita la criptografía Javascript; tienes criptografía "real".

Lo que conduce a esto:

El problema con la ejecución de código criptográfico en Javascript es que prácticamente cualquier función de la que depende la criptografía podría ser anulada silenciosamente por cualquier parte del contenido utilizado para construir la página de alojamiento. La seguridad criptográfica se puede deshacer al principio del proceso (generando números aleatorios falsos o alterando las constantes y los parámetros utilizados por los algoritmos), o más tarde (enviando el material clave a un atacante), o --- en el escenario más probable --- pasando por alto la criptografía por completo.

No existe una forma confiable para que ningún fragmento de código Javascript verifique su entorno de ejecución. El código criptográfico de Javascript no puede preguntar: "¿Realmente estoy tratando con un generador de números aleatorios o con algún facsímil de uno proporcionado por un atacante?" Y ciertamente no puede afirmar que "nadie puede hacer nada con este secreto criptográfico, excepto en las formas que yo, el autor, apruebo". Estas son dos propiedades que a menudo se proporcionan en otros entornos que usan criptografía y son imposibles en Javascript.

Básicamente, el problema es este:

  • Sus clientes no confían en sus servidores, por lo que quieren agregar un código de seguridad adicional.
  • Ese código de seguridad lo entregan sus servidores (en los que no confían).

O alternativamente,

  • Sus clientes no confían en SSL, por lo que quieren que use un código de seguridad adicional.
  • Ese código de seguridad se envía a través de SSL.

Nota: Además, SHA-256 no es adecuado para esto, ya que es muy fácil forzar contraseñas no iteradas sin sal y sin forzar . Si decide hacer esto de todos modos, busque una implementación de bcrypt , scrypt o PBKDF2 .

Brendan Long
fuente
1
Esto no es correcto. La seguridad de la contraseña se puede mejorar mediante el hash del lado del cliente. También es incorrecto que SHA-256 no sea adecuado siempre que se use algo como PBKDF2 en el lado del servidor. En tercer lugar, si bien el artículo de matasano contiene información útil, el chiste es incorrecto ya que ahora tenemos aplicaciones de navegador que cambian fundamentalmente el problema del huevo y la gallina.
user239558
2
Tiene razón en que PBKDF2 debería usarse en el cliente. La furia contra la criptografía javascript del lado del cliente supone que la página inicial y las solicitudes posteriores tienen las mismas propiedades de seguridad. Obviamente, eso no es cierto para las aplicaciones de navegador donde la página inicial se instala por separado y es estática. Tampoco es cierto para ninguna página con semántica de caché permanente. En estos casos, TOFU es exactamente igual que cuando se instala un programa del lado del cliente. Esto también puede ser cierto en configuraciones más complejas donde el servicio de páginas estáticas y dinámicas están separadas en el servidor.
user239558
3
Es legítimo evitar que los administradores del sistema tengan acceso a las contraseñas de los usuarios, por lo que el hash en el lado del cliente evita que los DBA u otros profesionales de TI vean las contraseñas de los usuarios y traten de reutilizarlas para acceder a otros servicios / sistemas, ya que muchos usuarios repiten sus contraseñas. .
Yamada
1
@Xenland Todo lo que estás haciendo es crear una nueva contraseña que es "token + contraseña". Todos los problemas originales aún se aplican. Por ejemplo, un atacante puede reemplazar el JavaScript con código para enviarle el token + contraseña.
Brendan Long
2
@Xenland El problema del huevo y la gallina no tiene nada que ver con las solicitudes que envías. El problema es que su cliente está usando código JavaScript que descargó a través de una conexión insegura para realizar trabajos de seguridad. La única forma de enviar JavaScript de forma segura a un navegador web es servirlo a través de TLS, y una vez que lo hace, no necesita hacer nada más porque su conexión ya es segura. No importa cuál sea su protocolo si está utilizando código JavaScript que un atacante puede simplemente reemplazar.
Brendan Long
10

Encontré esta implementación muy fácil de usar. También tiene una generosa licencia estilo BSD:

jsSHA: https://github.com/Caligatio/jsSHA

Necesitaba una forma rápida de obtener la representación de cadena hexadecimal de un hash SHA-256. Solo tomó 3 líneas:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
cobbzilla
fuente
1

Además de la biblioteca de Stanford que mencionó tylerl. Encontré jsrsasign muy útil (repositorio de Github aquí: https://github.com/kjur/jsrsasign ). No sé exactamente qué tan confiable es, pero he usado su API de SHA256, Base64, RSA, x509, etc. y funciona bastante bien. De hecho, también incluye la biblioteca de Stanford.

Si todo lo que quiere hacer es SHA256, jsrsasign podría ser una exageración. Pero si tiene otras necesidades en el área relacionada, creo que es una buena opción.

Muy lejos
fuente
2
Es más seguro y rápido usar developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Vitaly Zdanevich
2
De acuerdo, pero para ser justos, respondí esta pregunta en 2015, mientras que la especificación de la API de cifrado web de mozilla se publicó en enero de 2017
lejano