Python library or technique to defend against timing side channel attack

397 Views Asked by At

Consider a login API entry point that gets a username and a password.

The password is securely hashed and salted, and following a proper practice we return an identical response either if the username doesn't exists or the password is incorrect.

def verify_user(session, username, password):
    user = session.query(Users.username == 'username').one()
    stored_hash = user.password_hash if user is not None else 'some-dommy-hash'
    return verify_password(password=password, hash=stored_hash)

Note that we also try to make sure that both control flow will do about the same amount of work, so they should take about the same amount of time.

But the about and should is what got me worried.

Is it safe to assume that finding and not finding a user in the database take the same amount of time? Can I assume that hashing the same dummy hash will not be cached?

What I really want is a way to have something like:

def constant_time_verify_user(time, session, username, password):
    with constant_time(time):
        ans = verify_user(session, username, password)
    return ans

where constant_time, is something like timeout, in the sense that it will take exactly a given amount of time.

So my question: Is there a well established practice, or a library that will help to obscure the amount of time an operation might take, in order to defend against time based attacks?


Edit: @Staw's answer suggested that the motivation for this was unclear, and indeed, I am trying to prevent information leak about the existence of users in the database.

1

There are 1 best solutions below

0
On

I understand what are you really worrying about. You don't want others to know if a user exists or not.

Under single thread structure, it doesn't seem easy to implement. But if you can accept using multiple threads, that could be possible.

You can wrap your function into a thread and set a reasonable timeout for it. If the thread returns before timeout exceeded, then sleep a little bit more.

As I usually build asynchronous web server, that is a little bit easier for me to implement this.

async def constant_time_verify_user(time, session, username, password):
    start_time = _time.time()
    try:
        ans = await asyncio.wait_for(verify_user(session, username, password), timeout=time)
        await asyncio.sleep(time + start_time - _time.time())
        return ans
    except (asyncio.TimeoutError, asyncio.CancelledError):
        return "Exceed maximum execute time!"