# Crypto
Como se especifica en la sección de Settings se recomienda cambiar los valores de SECRET_KEY y CSRF_SESSION_KEY al igual que el valor de la variable hash_key que se encuentra en el archivo app/ext/security/crypto.py
hash_key = '-super-key-secret-for-project-please-change-this'
Este módulo hace uso de algunas de las funciones definidas en werkzeug.security
generate_password_hash
check_password_hash
gen_salt
# Métodos de Cifrado
El BackEndBase utiliza pbkdf2:sha512 (opens new window) (Función de derivación de clave basada en contraseña 2) para generar las contraseñas con un costo (opens new window) por defecto de 10, son funciones de derivación clave, con un costo computacional variable, y es utilizado con el fin de generar una contraseña más segura para las cuentas de usuario.
# Funciones disponibles
El módulo crypto contiene las siguientes funciones:
# generate_random_hash
Esta función implementa un generador de números pseudoaleatorios para diversas distribuciones.
from app.ext.security import generate_random_hash
...
result = generate_random_hash(128)
print(result)
>>> '155360093060481481006247830930724753347'
# generate_uuid
Esta función genera un UUID aleatorio.
from app.ext.security import generate_uuid
...
result = generate_uuid()
print(result)
>>> '79b1103ddae6452eaaf26625d9b41f1d'
# base64_encode
Esta función codifica los objetos de tipo bytes usando Base64 (opens new window) y devuelva los bytes codificados.
from app.ext.security import base64_encode
...
text = "Lorem ipsum dolor sit amet"
result = base64_encode(text)
print(result)
>>> b'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ='
# base64_decode
Esta función decodifica el objeto similar a los bytes codificados en Base64 ó las cadenas ASCII y devuelve los bytes decodificados.
from app.ext.security import base64_decode
...
decoded_string = base64_decode(result)
print(decoded_string)
>>> b'Lorem ipsum dolor sit amet'
# generate_salt
Esta función genera una cadena aleatoria de caracteres con una longitud especificada.
from app.ext.security import generate_salt
...
result = generate_salt(30)
print(result)
>>> 'wSpMDL1S80JhRDdEuHln65bAkfstCp'
# generate_digest
Esta función genera un hash seguro diferente. utilizando SHA512 (opens new window). Los términos "hash seguro" y "resumen de mensaje" son intercambiables. Los algoritmos más antiguos se llamaban resúmenes de mensajes. El término moderno es hash seguro, para más información consulte el módulo hashlib (opens new window).
from app.ext.security import generate_digest
...
result = generate_digest(text)
print(result)
>>> 'b1f4aaa6b51c19ffbe4b1b6fa107be09c8acafd7c768106a3faf475b1e27a940d3c075fda671eadf46c68f93d7eabcf604bcbf7055da0dc4eae6743607a2fc3f'
# generate_key_hash
Esta función se vale de otras funciones como generate_digest
y base64_encode,
además de las variables CSRF_SESSION_KEY,
SECRET_KEY
y hash_key
para generar un hash inicial codificado en Base64, que será la base para generar una contraseña utilizando la función generate_password
.
from app.ext.security import generate_key_hash
...
result = generate_key_hash(text)
print(result)
>>> b'HBzCpmjCn3bCgsOPw4l2FMOFwpPDlXvCvAwxOXHDlcKrKQlEw49Aw7PCpMKMHxDCnGIxZjRhYWE2YjUxYzE5ZmZiZTRiMWI2ZmExMDdiZTA5YzhhY2FmZDdjNzY4MTA2YTNmYWY0NzViMWUyN2E5NDBkM2MwNzVmZGE2NzFlYWRmNDZjNjhmOTNkN2VhYmNmNjA0YmNiZjcwNTVkYTBkYzRlYWU2NzQzNjA3YTJmYzNmYzE2ZTZmZDQyYmM3ZDU4ZTcyYjNhOGVhMTc4YTUzYzBhMWZlNjExYzc2YzQ3YTYzMmM0NDM0Yzc0MzhmNTMzOGRjN2MxNWExOTQ3ZWVhOTYwMDkzYmM3ZTYxODc2ZDQ3YzFkOWQ3ZWE4ZTc5ZjIxMzBhMGI0ZDgwNjZlMzViZDY0ZTYxMmNkZWJkYzcxNmE2OGNhNzcyNTdhM2ZkODA4MzYyODBlNDM0NDM4MjRlODdlNzE5OTk3MjhkMjk1NjU4NDgyMTFmZDFkZDE5N2RmOGNjYjI5NjAwZDlhM2RlMDkzNzExYmI1OGEyZWZjMjFkMTkwMTUzNzg3MzgyZDZlZA=='
# cipher_password
Esta función se encarga de sanear los datos que envía el usuario (caracteres especiales y acentos) y generar un hash codificado en Base64, utilizando la función anterior generate_key_hash
y la función sanity_check
(del modulo Commons
) internamente.
Tenga en cuenta que el resultado puede ser diferente a generate_key_hash
ya que cipher_password elimina caracteres especiales y acentos, haciendo que el hash cambie.
from app.ext.security import cipher_password
...
result = cipher_password(text)
print(result)
>>> b'HBzCpmjCn3bCgsOPw4l2FMOFwpPDlXvCvAwxOXHDlcKrKQlEw49Aw7PCpMKMHxDCnE5vbmVjMTZlNmZkNDJiYzdkNThlNzJiM2E4ZWExNzhhNTNjMGExZmU2MTFjNzZjNDdhNjMyYzQ0MzRjNzQzOGY1MzM4ZGM3YzE1YTE5NDdlZWE5NjAwOTNiYzdlNjE4NzZkNDdjMWQ5ZDdlYThlNzlmMjEzMGEwYjRkODA2NmUzNWJkNjRlNjEyY2RlYmRjNzE2YTY4Y2E3NzI1N2EzZmQ4MDgzNjI4MGU0MzQ0MzgyNGU4N2U3MTk5OTcyOGQyOTU2NTg0ODIxMWZkMWRkMTk3ZGY4Y2NiMjk2MDBkOWEzZGUwOTM3MTFiYjU4YTJlZmMyMWQxOTAxNTM3ODczODJkNmVk'
# generate_password
Esta función se vale de métodos de hash existentes como pbkdf2:sha512 para crear un hash que finalmente es nuevamente cifrado y firmado utilizando werkzeug.security
generate_password utiliza internamente a las funciones cipher_password
(descrita arriba) y generate_password_hash
de werkzeug.security, generando un hash de contraseña de 159 caracteres.
Tenga en cuenta, de que esta función al utilizar pbkdf2:sha512 (opens new window) y las funciones anteriormente descritas, para generar las contraseñas, es practicamente imposible determinar cual fue la cadena de caracteres utilizada por el usuario en primera instancia, Se recomienda crear un sistema de recuperacion de contraseñas que normalmente lo que hará, es cambiar la contraseña actual, basado en los criterios que considere necesario.
Ejemplo:
from flask import request
from app.ext.security import Auth, generate_password
from app.ext.rest import Rest, HttpStatus
...
@Auth.validate_request("username", "password")
def post(self):
content = request.get_json()
username = content.get('username')
password = content.get('password')
pass_hash = "{0}{1}".format(username, password)
new_password = generate_password(pass_hash)
return Rest.response(200, HttpStatus.OK, new_password)
La salida del ejemplo anterior es:
{
"status": 200,
"message": "success",
"data": "pbkdf2:sha512:50000$qscXW2pDuU$f9989800f3836a2c44bced7c56868ea32721a9de1939a1ad97f80eae0761a0d17c46e4ed75dc9e81e4b3a2d1ba1534174aa5885e7b9bec76d98811a99ed41fcd"
}
# check_password
Esta función utiliza el método anterior para generar una contraseña y después verificar su antenticidad utilizando werkzeug.security
Como se comentó arriba, no es posible determinar cual fué la cadena de caracteres utilizada por el usuario en primera instancia, check_password realiza una comparación estricta de una contraseña ingresada por el usuario y el hash almacenado con anterioridad.
Ejemplo:
from app.ext.security import AuthStatus, check_password
...
password_to_check = "{0}{1}".format(username, password)
resp = check_password(new_password, password_to_check)
if resp is True:
print "success!, the password is valid"
else:
print AuthStatus.INVALID_PASSWORD
Recuerda
Como se comenta más arriba, las constantes SECRET_KEY y CSRF_SESSION_KEY, y la variable hash_key, son utilizadas en conjunto con la información proporcionada por el usuario, para generar un hash que finalmente es nuevamente cifrado y firmado utilizando el módulo werkzeug.security.
Por lo que, si se han generado contraseñas anteriormente y se cambia el valor de alguna de las constantes citadas, la verificación de las contraseñas (usado por el método check_password) fallará.
# Más información
Para más información consulte:
- La sección de issues (opens new window) del BackEndBase