Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid leaking user existence info #180

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions etebase_server/fastapi/routers/authentication.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import random
import typing as t
from typing_extensions import Literal
from datetime import datetime
Expand Down Expand Up @@ -122,16 +123,36 @@ def get_login_user(request: Request, challenge: LoginChallengeIn) -> UserType:
raise AuthenticationFailed(code="user_not_init", detail="User not properly init")
return user
except User.DoesNotExist:
raise AuthenticationFailed(code="user_not_found", detail="User not found")
return fake_user(username)


def get_encryption_key(salt: bytes):
FAKE_USER_COUNT = 1000


def fake_user(username: str) -> UserType:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good idea, but this is unfortunately not effective.

The returned user id is random every time, so just hit this API twice to check for existence. Additionally, I haven't had the time to analyze the using of static pubkeys/salts but I'm concerned that may have issues too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The returned user id is random every time, so just hit this API twice to check for existence.

Oh, you are absolutely right, didn't think of that

username_bytes = bytes(username, encoding="utf-8")
login_pubkey = get_encryption_key(b"", b"loginPubkey", username_bytes)[:32]
salt = get_encryption_key(b"", b"salt", username_bytes)[:16]

user = User()
user.username = username
user.id = random.Random(settings.SECRET_KEY + username).randint(0, FAKE_USER_COUNT)

userinfo = UserInfo()
userinfo.loginPubkey = login_pubkey
userinfo.salt = salt

user.userinfo = userinfo
return user


def get_encryption_key(salt: bytes, person=b"etebase-auth", data: bytes = b""):
key = nacl.hash.blake2b(settings.SECRET_KEY.encode(), encoder=nacl.encoding.RawEncoder)
return nacl.hash.blake2b(
b"",
data=data,
key=key,
salt=salt[: nacl.hash.BLAKE2B_SALTBYTES],
person=b"etebase-auth",
person=person,
encoder=nacl.encoding.RawEncoder,
)

Expand Down