Password Strength Checker

Password Strength Checker

Dependencies:

- Python 3

In today's interconnected digital landscape, data security stands as an unyielding fortress guarding our most sensitive information. At its core lies the crucial factor of password strength, the first line of defense against unauthorized access and breaches. In this blog post, we embark on an enlightening journey into the realm of cybersecurity, exploring a potent alliance of two cutting-edge technologies: FastAPI and GraphQL, fueled by the Strawberry library. This dynamic combination promises not only to evaluate password robustness but also to deliver a developer's dream - a seamless and agile development experience.

Our exploration commences with the establishment of stringent password rules. These rules, including checks for minimum length, uppercase and lowercase character requirements, mandatory digits, essential special characters, and the prevention of sequential letter repetition, serve as the bedrock of password fortification.

We'll delve into the details of this powerful combination and show you how it can elevate the security of your applications to a whole new level.

Install requirements:

CMD / Terminal
pip3 install fastapi==0.87.0 uvicorn==0.20.0 graphene==3.1.1 strawberry-graphql==0.142.2

1. Let's start by creating some rules for passwords.

min_size: This rule verifies the minimum password length.
min_upper_case: It ensures the presence of a minimum number of uppercase letters.
min_lower_case: This rule checks for the inclusion of a minimum number of lowercase letters.
min_digit: It verifies if the password contains the required minimum digits.
min_special_chars: This rule confirms the presence of the minimum required special characters.
no_repeted: Lastly, it checks for the absence of repeated sequential letters in the password.

rules_password.py
import re

def min_size(string, value) -> bool:
    if len(string) < value:
        return False
    return True

def min_upper_case(string, value) -> bool:
    countUpperCaseChars = sum(1 for c in string if c.isupper())
    
    if countUpperCaseChars < value:
        return False
    return True

def min_lower_case(string, value) -> bool:
    countLowerCaseChars = sum(1 for c in string if c.islower())
    
    if countLowerCaseChars < value:
        return False
    return True

def min_digit(string, value) -> bool:
    countDigits = sum(1 for c in string if c.isdigit())
    
    if countDigits < value:
        return False
    return True

def min_special_chars(string, value) -> bool:
    specialChars = r'!@#$%^&*()-+\/{}[]'    
    countSpecialChars = sum(string.count(c) for c in list(specialChars))
    
    if countSpecialChars < value:
        return False
    return True

def no_repeted(string, value) -> bool:
    # https://stackoverflow.com/a/28007115    
    if re.search(r'(.)\1', string):
        return False
    return True

to_do

main.py
import strawberry
from typing import List
from fastapi import FastAPI
from strawberry.asgi import GraphQL
# Local import
from rules_password import *

@strawberry.type
class Response:
    verify: bool
    noMatch: List[str]

@strawberry.input
class Rules:
    rule: str
    value: int

@strawberry.type
class Query:
    @strawberry.field
    def verify(self, password,  rules) -> Response:
        
        # Relates the rules available in GraphQL with the name of the functions in rules_password.py
        dictFunctions = {
            "minSize": min_size,
            "minUppercase": min_upper_case,
            "minLowercase": min_lower_case,
            "minDigit": min_digit,
            "minSpecialChars": min_special_chars,
            "noRepeted": no_repeted,
        }
        
        # For each rule input in GraphQL, call the function and relate the name of the rule with the return to its function
        dictResponse = {}
        for ruleInput in rules:
            dictResponse[ruleInput.rule] = dictFunctions[ruleInput.rule](password, ruleInput.value)
            # Example: dictResponse["minSize"] = False (False it's return of min_size(string, value))
        
        if all(list(dictResponse.values())):
            # Returns empty "noMatch" attribute if all values in dictResponse are True
            return Response(verify=True, noMatch=[""])
        else:
            # Return the rules that did not pass in the "noMatch" attribute
            wrongChecks = [response for response in dictResponse if dictResponse[response] == False]
            return Response(verify=False, noMatch=wrongChecks)

schema = strawberry.Schema(query=Query)
graphql_app = GraphQL(schema)

app = FastAPI()
app.add_route("/graphql", graphql_app)
app.add_websocket_route("/graphql", graphql_app)

This is all required code.


Example of use:

Go go http://localhost:8000/graphql and you can insert queries like this:

Query Input
query {
    verify(password: "TestStrengthPassword!123&", rules: [
          {rule: "minSize", value: 10},
          {rule: "minUppercase", value: 5},
          {rule: "minLowercase", value: 3},
          {rule: "minDigit", value: 2}
          {rule: "minSpecialChars", value: 2}
          {rule: "noRepeted", value: 0}
    ]) {
        verify
        noMatch
    }
}
Output
{
    "data": {
        "verify": {
        "verify": false,
        "noMatch": [
            "minUppercase",
            "minDigit",
            "noRepeted"
        ]
        }
    }
}

For use like a API, make a POST to http://localhost:8000/graphql

example_use.py
import json
import requests

url = "http://localhost:8000/graphql"
    
body = """
query {
    verify(password: "TestStrengthPassword!12&", rules: [
        {rule: "minSize", value: 10},
        {rule: "minUppercase", value: 5},
        {rule: "minLowercase", value: 3},
        {rule: "minDigit", value: 3},
        {rule: "minSpecialChars", value: 2},
        {rule: "noRepeted", value: 0},
    ]) {
        verify
        noMatch
    }
}
"""

print(body)
response = requests.post(url=url, json={"query": body})
print("response status code: ", response.status_code)
if response.status_code == 200:
    parsed = json.loads(response.content)
    print("response : ", json.dumps(parsed, indent=4))

# response status code:  200
# response :  {
#     "data": {
#         "verify": {
#             "verify": false,
#             "noMatch": [
#                 "minUppercase",
#                 "minDigit",
#                 "noRepeted"
#             ]
#         }
#     }
# }

and its done!


All the code is available here too: Github - Password-Strength-Checker

CMD / Terminal
git clone https://github.com/AlfredoFilho/Password-Strength-Checker.git