Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Hash passwords earlier in the registration process #7523

Merged
merged 2 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions changelog.d/7523.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hash passwords as early as possible during registration.
9 changes: 2 additions & 7 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def check_username(self, localpart, guest_access_token=None, assigned_user_id=No
def register_user(
self,
localpart=None,
password=None,
password_hash=None,
guest_access_token=None,
make_guest=False,
admin=False,
Expand All @@ -147,7 +147,7 @@ def register_user(
Args:
localpart: The local part of the user ID to register. If None,
one will be generated.
password (unicode): The password to assign to this user so they can
password_hash (unicode): The hashed password to assign to this user so they can
clokep marked this conversation as resolved.
Show resolved Hide resolved
login again. This can be None which means they cannot login again
via a password (e.g. the user is an application service user).
user_type (str|None): type of user. One of the values from
Expand All @@ -164,11 +164,6 @@ def register_user(
yield self.check_registration_ratelimit(address)

yield self.auth.check_auth_blocking(threepid=threepid)
password_hash = None
if password:
password_hash = yield defer.ensureDeferred(
self._auth_handler.hash(password)
)

if localpart is not None:
yield self.check_username(localpart, guest_access_token=guest_access_token)
Expand Down
30 changes: 15 additions & 15 deletions synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,11 @@ async def on_PUT(self, request, user_id):

else: # create user
password = body.get("password")
if password is not None and (
not isinstance(body["password"], text_type)
or len(body["password"]) > 512
):
raise SynapseError(400, "Invalid password")
password_hash = None
if password is not None:
if not isinstance(password, text_type) or len(password) > 512:
raise SynapseError(400, "Invalid password")
password_hash = await self.auth_handler.hash(password)

admin = body.get("admin", None)
user_type = body.get("user_type", None)
Expand All @@ -259,7 +259,7 @@ async def on_PUT(self, request, user_id):

user_id = await self.registration_handler.register_user(
localpart=target_user.localpart,
password=password,
password_hash=password_hash,
admin=bool(admin),
default_display_name=displayname,
user_type=user_type,
Expand Down Expand Up @@ -298,7 +298,7 @@ class UserRegisterServlet(RestServlet):
NONCE_TIMEOUT = 60

def __init__(self, hs):
self.handlers = hs.get_handlers()
self.auth_handler = hs.get_auth_handler()
self.reactor = hs.get_reactor()
self.nonces = {}
self.hs = hs
Expand Down Expand Up @@ -362,16 +362,16 @@ async def on_POST(self, request):
400, "password must be specified", errcode=Codes.BAD_JSON
)
else:
if (
not isinstance(body["password"], text_type)
or len(body["password"]) > 512
):
password = body["password"]
if not isinstance(password, text_type) or len(password) > 512:
raise SynapseError(400, "Invalid password")

password = body["password"].encode("utf-8")
if b"\x00" in password:
password_bytes = password.encode("utf-8")
if b"\x00" in password_bytes:
raise SynapseError(400, "Invalid password")

password_hash = await self.auth_handler.hash(password)

admin = body.get("admin", None)
user_type = body.get("user_type", None)

Expand All @@ -388,7 +388,7 @@ async def on_POST(self, request):
want_mac_builder.update(b"\x00")
want_mac_builder.update(username)
want_mac_builder.update(b"\x00")
want_mac_builder.update(password)
want_mac_builder.update(password_bytes)
want_mac_builder.update(b"\x00")
want_mac_builder.update(b"admin" if admin else b"notadmin")
if user_type:
Expand All @@ -407,7 +407,7 @@ async def on_POST(self, request):

user_id = await register.registration_handler.register_user(
localpart=body["username"].lower(),
password=body["password"],
password_hash=password_hash,
admin=bool(admin),
user_type=user_type,
)
Expand Down
22 changes: 13 additions & 9 deletions synapse/rest/client/v2_alpha/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,12 +426,16 @@ async def on_POST(self, request):
# we do basic sanity checks here because the auth layer will store these
# in sessions. Pull out the username/password provided to us.
if "password" in body:
if (
not isinstance(body["password"], string_types)
or len(body["password"]) > 512
):
password = body.pop("password")
if not isinstance(password, string_types) or len(password) > 512:
raise SynapseError(400, "Invalid password")
self.password_policy_handler.validate_password(body["password"])
self.password_policy_handler.validate_password(password)

# If the password is valid, hash it and store it back on the request.
# This ensures the hashed password is handled everywhere.
if "password_hash" in body:
raise SynapseError(400, "Unexpected property: password_hash")
body["password_hash"] = await self.auth_handler.hash(password)

desired_username = None
if "username" in body:
Expand Down Expand Up @@ -484,7 +488,7 @@ async def on_POST(self, request):

guest_access_token = body.get("guest_access_token", None)

if "initial_device_display_name" in body and "password" not in body:
if "initial_device_display_name" in body and "password_hash" not in body:
# ignore 'initial_device_display_name' if sent without
# a password to work around a client bug where it sent
# the 'initial_device_display_name' param alone, wiping out
Expand Down Expand Up @@ -546,11 +550,11 @@ async def on_POST(self, request):
registered = False
else:
# NB: This may be from the auth handler and NOT from the POST
assert_params_in_dict(params, ["password"])
assert_params_in_dict(params, ["password_hash"])

desired_username = params.get("username", None)
guest_access_token = params.get("guest_access_token", None)
new_password = params.get("password", None)
new_password_hash = params.get("password_hash", None)

if desired_username is not None:
desired_username = desired_username.lower()
Expand Down Expand Up @@ -583,7 +587,7 @@ async def on_POST(self, request):

registered_user_id = await self.registration_handler.register_user(
localpart=desired_username,
password=new_password,
password_hash=new_password_hash,
guest_access_token=guest_access_token,
threepid=threepid,
address=client_addr,
Expand Down