password - Usar Python para autenticar contra nombre de usuario, hash, sal en DB creado por roles/membresÃa de ASP.NET
net core hash password (2)
Tenemos una aplicación actual donde las credenciales de inicio de sesión del usuario se almacenan en un DB de SQL Server. Estos son, básicamente, almacenados como un nombre de usuario de texto sin formato, un hash de contraseña y una sal asociada para este hash.
Todos fueron creados por funciones integradas en el sistema de membresía / rol de ASP.NET. Aquí hay una fila para un usuario llamado ''joe'' y una contraseña de ''contraseña'':
joe, kDP0Py2QwEdJYtUX9cJABg ==, OJF6H4KdxFLgLu + oTDNFodCEfMA =
He descargado estas cosas en un archivo CSV y estoy tratando de ponerlo en un formato utilizable para Django que almacena sus contraseñas en este formato:
[algo] $ [salt] $ [hash]
Donde la sal es una cadena simple y el hash es el resumen hexadecimal de un hash SHA1.
Hasta ahora he podido determinar que ASP está almacenando estos hashes y sales en un formato base64. Esos valores anteriores se decodifican en cadenas binarias.
Hemos utilizado el reflector para ver cómo ASP se autentica frente a estos valores:
internal string EncodePassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0)
{
return pass;
}
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(salt);
byte[] dst = new byte[src.Length + bytes.Length];
byte[] inArray = null;
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
if (passwordFormat == 1)
{
HashAlgorithm algorithm = HashAlgorithm.Create(Membership.HashAlgorithmType);
if ((algorithm == null) && Membership.IsHashAlgorithmFromMembershipConfig)
{
RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException();
}
inArray = algorithm.ComputeHash(dst);
}
else
{
inArray = this.EncryptPassword(dst);
}
return Convert.ToBase64String(inArray);
}
Esencialmente, extrae la sal del DB y b64 la decodifica en una representación binaria. Hace un "GetBytes" en la contraseña sin procesar y luego los concatena, sal primero.
A continuación, ejecuta el algoritmo SHA1 en esta nueva cadena, base64 la codifica y la compara con el valor almacenado en la base de datos.
Intenté escribir un código para intentar reproducir estos hash en Python y estoy fallando. No podré usarlos en Django hasta que pueda entender cómo se traduce esto. Así es como estoy probando:
import hashlib
from base64 import b64decode, b64encode
b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = ''password''
m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt)
# Pass in password
m1.update(password_string)
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
print "Logged in!"
else:
print "Didn''t match"
print b64hash
print b64encode(m1.digest())
Me pregunto si alguien puede ver algún defecto en mi enfoque o puede sugerirme un método alternativo. ¿Quizás pueda tomar los algoritmos anteriores y la contraseña y sal conocidas anteriormente y producir el hash en su sistema?
Dos pensamientos sobre lo que podría estar yendo mal.
Primero, el código de la reflexión tiene tres caminos:
- Si passwordFormat es 0, devuelve la contraseña tal como está.
- Si passwordFormat es 1, crea el hash como lo hace su código python.
- Si passwordFormat es diferente de 0 o 1, llama this.EncryptPassword ()
¿Cómo sabes que has hashing la contraseña y no cifrado la contraseña con this.EncryptPassword ()? Es posible que necesite revertir la función de miembro EncryptPassword () y replicar eso. Eso es a menos que tenga alguna información que garantice que está utilizando la contraseña y no la encriptará.
En segundo lugar, si de hecho se trata de la contraseña, es posible que desee ver qué devuelve la función Encoding.Unicode.GetBytes () para la cadena "contraseña", ya que puede obtener algo como:
0x00 0x70 0x00 0x61 0x00 0x73 0x00 0x73 0x00 0x77 0x00 0x6F 0x00 0x72 0x00 0x64
en lugar de:
0x70 0x61 0x73 0x73 0x77 0x6F 0x72 0x64
Espero que esto ayude.
Parece que python está insertando un marcador de orden de bytes cuando convierte una cadena UTF16 a binaria. La matriz de bytes .NET no contiene BOM, así que hice algo de ghetto python que convierte el UTF16 en hexadecimal, elimina los primeros 4 caracteres y luego los decodifica en binario.
Puede haber una mejor manera de arrancar el BOM, ¡pero esto funciona para mí!
Aquí hay uno que pasa:
import hashlib
from base64 import b64decode, b64encode
def utf16tobin(s):
return s.encode(''hex'')[4:].decode(''hex'')
b64salt = "kDP0Py2QwEdJYtUX9cJABg=="
b64hash = "OJF6H4KdxFLgLu+oTDNFodCEfMA="
binsalt = b64decode(b64salt)
password_string = ''password''.encode("utf16")
password_string = utf16tobin(password_string)
m1 = hashlib.sha1()
# Pass in salt
m1.update(binsalt + password_string)
# Pass in password
# B64 encode the binary digest
if b64encode(m1.digest()) == b64hash:
print "Logged in!"
else:
print "Didn''t match"
print b64hash
print b64encode(m1.digest())