Skip to content

Hash an API token that comes in an header #2217

@ZordoC

Description

@ZordoC

Hello everyone! Starting off I love this framework :-)!

I'm currently facing this issue regarding security and my API keys.

My approach to design my API was to have the API access keys stored in a database. (hashed) And then once the key would be sent to the header I would hash it again and see if it's present on the db.

I've managed to do almost everything except the fact that I cannot hash the API key once it' submitted ... perhaps easier with an example ...

def check_all_hashed_keys(db: Session = SessionLocal()):
    quer = db.query(models.ApiKeys).with_entities(models.ApiKeys.value).all()
    clean = [ str(path)[3:-3] for path in quer]
    return clean


API_KEY = check_all_hashed_keys()
API_KEY_NAME = "access_token"
COOKIE_DOMAIN = "localtest.me"


api_key_query = APIKeyQuery(name=API_KEY_NAME, auto_error=False)
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
api_key_cookie = APIKeyCookie(name=API_KEY_NAME, auto_error=False)

print(Security(api_key_query))

async def get_api_key(
    api_key_query: str = Security(api_key_query),
    api_key_header: str = Security(api_key_header),
    api_key_cookie: str = Security(api_key_cookie),
):

    if api_key_query in API_KEY:
        return api_key_query
    elif api_key_header in API_KEY:
        return api_key_header
    elif api_key_cookie in API_KEY:
        return api_key_cookie
    else:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )

So API_KEY is a list containing all the Access tokens ( hashed by bcrypt ), now I want to compare both keys however I have a problem! The key submitted is not hashed and the one in the database is.

After checking this code https://github.com/tiangolo/fastapi/blob/master/fastapi/security/api_key.py

I realize that I cannot extract the token via code in order to hash it, which then makes me unable to verify if the Key matches any of the ones present on the database.

Does anyone know a better way of doing this? I thought about changing the source code to my needs ans basically hash it there

class APIKeyQuery(APIKeyBase):
    def __init__(
        self, *, name: str, scheme_name: Optional[str] = None, auto_error: bool = True
    ):
        self.model: APIKey = APIKey(**{"in": APIKeyIn.query}, name=name)
        self.scheme_name = scheme_name or self.__class__.__name__
        self.auto_error = auto_error

    async def __call__(self, request: Request) -> Optional[str]:
        api_key: str = request.query_params.get(self.model.name) 
        if not api_key:
            if self.auto_error:
                raise HTTPException(
                    status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
                )
            else:
                return None
        return api_key 

Something like this

api_key: str = request.query_params.get(bcrypt.hashpw(self.model.name.encode('utf-8'), bcrypt.gensalt()))  

This looks like a terrible way and I'd like to avoid it at all possible costs!

Anymore information that you may require I can post it here

Best, Jose

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions