La "mejor práctica" es, en primer lugar, dejar de usar el término "cifrado" porque SHA-256 no es cifrado . Esto se llama hashing . Si sus desarrolladores senior y los auditores externos hablan de SHA-256 como "cifrado", pueden estar seguros de que ninguno de ellos sabe realmente de lo que están hablando.
En su sistema, lo que sea que el cliente envíe, ya sea "cifrado" (con hash) o no en el cliente, es un valor que otorga acceso; ese valor es, por lo tanto, contraseña equivalente y revelarlo es tan mortal para la seguridad de su servidor como la contraseña de texto simple. Ese tipo de hashing del lado del cliente no trae la mejora de seguridad que sus auditores parecen creer que haga. Lo que complica la situación es el hecho de que cuando el cliente realiza un hash con código Javascript, ese código Javascript fue enviado por el mismo servidor , por lo que un servidor comprometido podría enviar perfectamente un Javascript que simplemente reciba la contraseña del usuario como lo escribe y envía esa contraseña a un servidor en Mongolia.
En cualquier caso, las mentes humanas son lo que son, las contraseñas recordadas por el usuario son débiles contra la fuerza bruta (llamadas "ataques de diccionario"). Una simple invocación de una función hash criptográfica como SHA-256, independientemente de la seguridad criptográfica de esa función, es demasiado rápida para brindar mucha protección aquí; una PC de escritorio lista para usar puede contener contraseñas potenciales a tasas calculadas en miles de millones por segundo. El buen hashing de la contraseña requiere funciones dedicadas con un costo computacional configurable (pero inherentemente alto). No podrá hacerlo correctamente en Javascript porque Javascript es débil para las tareas de computación intensivas.
La mejor práctica es usar SSL (siempre), transmitir la contraseña tal como está en el túnel SSL y dejar que el servidor aplique una función de hashing de contraseña adecuada, es decir, bcrypt (con un número de iteraciones lo suficientemente alto, tan alto como se puede tolerar dado el hardware disponible y la carga máxima esperada).
Ocasionalmente, uno se encuentra con auditores que necesitan algo que decir, y es posible que tenga que realizar algunos bailes rituales para apaciguarlos. Una de esas danzas es la criptografía por aspersión en varios lugares, como la torta de hielo. Esto es idiota pero inofensivo, siempre y cuando no elimine las partes importantes. En su caso, puede hacer hash SHA-256 en el lado del cliente, pero no debe eliminar el hashing de contraseña correcto en el lado del servidor, es decir, considerar el resultado del hash calculado por el cliente como la "contraseña" y usarlo como entrada a bcrypt. (Tenga en cuenta que las salidas de hash son binarias , no de texto, por lo que se necesita algo de codificación, por ejemplo, hexadecimal o Base64 .)
El SHA-256 adicional sería entonces inútil, pero no debilitará tu sistema, al contrario de lo que pretendes hacer:
Por lo tanto, para nuestra futura versión, hemos pasado a utilizar cryptojs para cifrar las contraseñas (sin sal) del lado del cliente, y estos valores se almacenan directamente en la base de datos sin más manipulación.
Si lo hace, entonces el contenido de la base de datos se puede usar para otorgar acceso inmediato al servidor. Un simple vistazo de solo lectura a la base de datos (por ejemplo, un disco duro roto descartado, una cinta de copia de seguridad perdida, un ataque de inyección de SQL ...), y su seguridad se ha ido por el desagüe.