lunes, 23 de octubre de 2023

Validador de contraseñas seguras con Python

Validador de contraseñas seguras con Python

Cuando se crean cuentas de usuario, es común que al momento de registrar una contraseña esta deba cumplir con varios parámetros de seguridad, como por ejemplo la longitud o contener caracteres especiales.

En esta ocasión vamos a ver un ejemplo de cómo realizar un validador de contraseñas en Python, para comprobar que cumplen con los siguientes parámetros específicos de seguridad:

  • Contener al menos una letra minúscula
  • Contener al menos una letra mayúscula
  • Contener al menos un número
  • Contener al menos un caracter especial
  • Tener una longitud mínima de 6 caracteres
  • Tener una longitud máxima de 25 caracteres

Para realizar varias estas validaciones vamos a utilizar expresiones regulares, por lo que deberemos importar este módulo. Adicional para imprimir por consola el mensaje de validación, vamos a usar el módulo pprint de Python.

Importar los módulos requeridos

Los módulos que vamos a importar son los siguientes:

from pprint import pprint
from re import match

Definición de constantes

Como se ha indicado, se va a validar que la longitud de la contraseña esté en un rango de 6 a 25 caracteres, por lo que vamos a definir estos valores como una constante, para luego utilizarlos en las validaciones.

MIN_LENGTH = 6
MAX_LENGTH = 25

Funciones de validación de reglas

Para hacerlo más visual, vamos a definir una función para validar cada uno de los parámetros que debe cumplir una contraseña para considerarla válida.

Función de validación de letra mayúscula

Como se mencionó antes, varias de las validaciones se van a realizar mediante expresiones regulares. Para esta validación se va a usar la expresión regular que compruebe si existe al menos una letra mayúscula dentro de la cadena de texto recibida como contraseña.

def validateUpper(text):
    return bool(match(r'\w*[A-Z]\w*', text))

Como se puede apreciar, se utiliza la función match que importamos al inicio, en esta se proporciona la expresión regular r’\w*[A-Z]\w*’ encargada de buscar alguna coincidencia de las letras mayúsculas en el texto (text) recibido. Al resultado de esta validación se le hace un cast con la función bool() que nos devolverá True si se encuentra alguna letra mayúscula o False si no existe.

Función de validación de letra minúscula

De manera similar, para validar la existencia de letras minúsculas dentro de la cadena de texto, usamos una expresión regular.

def validateLower(text):
    return bool(match(r'\w*[a-z]\w*', text))
Función de validación de números

Así mismo, vamos a realizar la validación de existencia de números en la cadena de texto.

def validateNumber(text):
    return bool(match(r'\w*[0-9]\w*', text))
Función de validación de caracteres especiales

Para comprobar la existencia de caracteres especiales, vamos a buscar dentro de la cadena de texto algún caracter que no sea alfanumérico. Es decir que al recorrer todos los caracteres de la cadena de texto, si alguno es diferente de una letra o un número, es porque debe ser un caracter especial. Para comprobar eso, hacemos uno de la función any(), la cual recibe un objeto iterable y verifica si existe al menos un elemento dentro del objeto iterable es True para devolver True como resultado, caso contrario retorna False indicando que el objeto iterable está vació o que no existe ningún elemento que sea True.

El objeto iterable lo obtenemos mediante la creación de una lista de valores True y False mediante el uso del método isalnum() que verifica si un caracter es alfanumérico.

def validateSpecialChar(text):
    return any(not c.isalnum() for c in text)
Función de validación de la longitud mínima

Para comprobar si la cadena recibida cumple con la longitud mínima requerida, usamos la función len() para obtener la longitud de la cadena y compararlo con el valor MIN_LENGTH que definimos al inicio.

def validateMinLength(text):
    return len(text) >= MIN_LENGTH
Función de validación de la longitud máxima

De manera similar, usamos la función len() para obtener la longitud de la cadena y compararlo con el valor MAX_LENGTH que definimos al inicio.

def validateMinLength(text):
    return len(text) <= MAX_LENGTH

Una vez definidas estas funciones, podemos crear una función adicional en la cual integraremos todas las funciones de validación para crear nuestro validador.

El validador consistirá en un diccionario, en el que se agregará el listado de las validaciones, cada una de las cuales tendrá los siguientes campos:

  • name: para definir el nombre de la validación a la que se hace referencia
  • value: para asignar un valor booleano, True si cumple con el requerimiento o False si no lo cumple
  • message: para indicar un mensaje de aprobación si se cumple el requerimiento o de error si no se cumple.

En el diccionario también se agrega una validación general que indica si la contraseña ingresada es correcta (cumple con todos los parámetros) o no, asignando el valor de True o False respectivamente. Para esto se usa la función all() de Python la cual se encarga de validar que todos los elementos en un objeto iterable son True para retornar un valor de True, caso contrario, si al menos un elemento del objeto iterable es diferente de True retorna False.

La definición de la función es la siguiente:

def validatePassword(text):
    validation = {}
    validations = [
        {
            "name": "uppercase",
            "value": validateUpper(text),
            "message": "Password contains at least one uppercase character" if validateUpper(
                text) else "Must contain at least one capital letter"
  },
        {
            "name": "lowercase",
            "value": validateLower(text),
            "message": "Password contains at least one lowercase character" if validateLower(
                text) else "Must contain at least one lowercase letter"
  },
        {
            "name": "number",
            "value": validateNumber(text),
            "message": "Password contains at least one number" if validateNumber(
                text) else "Must contain at least one number"
  },
        {
            "name": "specialChar",
            "value": validateSpecialChar(text),
            "message": "Password contains at least one special character" if validateSpecialChar(
                text) else "Must contain at least one special character"
  },
        {
            "name": "minLength",
            "value": validateMinLength(text),
            "message": "Password meets the minimum required length" if validateMinLength(
                text) else f"Password length must be greater than {MIN_LENGTH}"
  },
        {
            "name": "maxLength",
            "value": validateMaxLength(text),
            "message": "Password meets the maximum required length" if validateMaxLength(
                text) else f"Password length must be lower than {MAX_LENGTH}"
  }]
    validation["isValid"] = all([k.get("value") for k in validations])
    validation["validations"] = validations
    pprint(validation)

Para compobar el funcionamiento de nuestro validador, vamos a ejecutarlo con una cadena de texto de prueba que sí cumpla los parámetros y otra que no los cumpla.

Prueba con cadena que cumple los parámetros

Se va a usar la cadena de ejemplo:

V3ryS3cur3Pa$$word

El resultado de validar esta cadena de texto será el siguiente:

{
  'isValid': True,
  'validations': [
    {
      'message': 'Password contains at least one uppercase'
  'character',
      'name': 'uppercase',
      'value': True
    },
    {
      'message': 'Password contains at least one lowercase'
  'character',
      'name': 'lowercase',
      'value': True
    },
    {
      'message': 'Password contains at least one number',
      'name': 'number',
      'value': True
    },
    {
      'message': 'Password contains at least one special character',
      'name': 'specialChar',
      'value': True
    },
    {
      'message': 'Password meets the minimum required length',
      'name': 'minLength',
      'value': True
    },
    {
      'message': 'Password meets the maximum required length',
      'name': 'maxLength',
      'value': True
    }
  ]
}

Como podemos observar, se cumplen todos los parámetros, por lo tanto, la validación de esta contraseña es correcta.

Prueba con cadena que no cumple los parámetros

Para comprobar que las validaciones se realizan de manera correcta, ahora probaremos una cadena de texto más simple y observaremos el resultado. La cadena a usar es la siguiente:

12345

Y el resultado obtenido es este:

{
  'isValid': False,
  'validations': [
    {
      'message': 'Must contain at least one capital letter',
      'name': 'uppercase',
      'value': False
    },
    {
      'message': 'Must contain at least one lowercase letter',
      'name': 'lowercase',
      'value': False
    },
    {
      'message': 'Password contains at least one number',
      'name': 'number',
      'value': True
    },
    {
      'message': 'Must contain at least one special character',
      'name': 'specialChar',
      'value': False
    },
    {
      'message': 'Password length must be greater than 6',
      'name': 'minLength',
      'value': False
    },
    {
      'message': 'Password meets the maximum required length',
      'name': 'maxLength',
      'value': True
    }
  ]
}

En este caso vemos que la contraseña ingresada no es válida, esto debido a que los únicos parámetros que cumple (True) son el de contener al menos un número y el de ser de longitud menor a 25 caracteres.

No hay comentarios.:

Publicar un comentario