Skip to content

Commit 56db0b1

Browse files
authored
Hash passwords earlier in the registration process (matrix-org#7523)
1 parent 75fbc1a commit 56db0b1

File tree

4 files changed

+31
-31
lines changed

4 files changed

+31
-31
lines changed

changelog.d/7523.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hash passwords as early as possible during registration.

synapse/handlers/register.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def check_username(self, localpart, guest_access_token=None, assigned_user_id=No
132132
def register_user(
133133
self,
134134
localpart=None,
135-
password=None,
135+
password_hash=None,
136136
guest_access_token=None,
137137
make_guest=False,
138138
admin=False,
@@ -147,7 +147,7 @@ def register_user(
147147
Args:
148148
localpart: The local part of the user ID to register. If None,
149149
one will be generated.
150-
password (unicode): The password to assign to this user so they can
150+
password_hash (str|None): The hashed password to assign to this user so they can
151151
login again. This can be None which means they cannot login again
152152
via a password (e.g. the user is an application service user).
153153
user_type (str|None): type of user. One of the values from
@@ -164,11 +164,6 @@ def register_user(
164164
yield self.check_registration_ratelimit(address)
165165

166166
yield self.auth.check_auth_blocking(threepid=threepid)
167-
password_hash = None
168-
if password:
169-
password_hash = yield defer.ensureDeferred(
170-
self._auth_handler.hash(password)
171-
)
172167

173168
if localpart is not None:
174169
yield self.check_username(localpart, guest_access_token=guest_access_token)

synapse/rest/admin/users.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,11 @@ async def on_PUT(self, request, user_id):
243243

244244
else: # create user
245245
password = body.get("password")
246-
if password is not None and (
247-
not isinstance(body["password"], text_type)
248-
or len(body["password"]) > 512
249-
):
250-
raise SynapseError(400, "Invalid password")
246+
password_hash = None
247+
if password is not None:
248+
if not isinstance(password, text_type) or len(password) > 512:
249+
raise SynapseError(400, "Invalid password")
250+
password_hash = await self.auth_handler.hash(password)
251251

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

260260
user_id = await self.registration_handler.register_user(
261261
localpart=target_user.localpart,
262-
password=password,
262+
password_hash=password_hash,
263263
admin=bool(admin),
264264
default_display_name=displayname,
265265
user_type=user_type,
@@ -298,7 +298,7 @@ class UserRegisterServlet(RestServlet):
298298
NONCE_TIMEOUT = 60
299299

300300
def __init__(self, hs):
301-
self.handlers = hs.get_handlers()
301+
self.auth_handler = hs.get_auth_handler()
302302
self.reactor = hs.get_reactor()
303303
self.nonces = {}
304304
self.hs = hs
@@ -362,16 +362,16 @@ async def on_POST(self, request):
362362
400, "password must be specified", errcode=Codes.BAD_JSON
363363
)
364364
else:
365-
if (
366-
not isinstance(body["password"], text_type)
367-
or len(body["password"]) > 512
368-
):
365+
password = body["password"]
366+
if not isinstance(password, text_type) or len(password) > 512:
369367
raise SynapseError(400, "Invalid password")
370368

371-
password = body["password"].encode("utf-8")
372-
if b"\x00" in password:
369+
password_bytes = password.encode("utf-8")
370+
if b"\x00" in password_bytes:
373371
raise SynapseError(400, "Invalid password")
374372

373+
password_hash = await self.auth_handler.hash(password)
374+
375375
admin = body.get("admin", None)
376376
user_type = body.get("user_type", None)
377377

@@ -388,7 +388,7 @@ async def on_POST(self, request):
388388
want_mac_builder.update(b"\x00")
389389
want_mac_builder.update(username)
390390
want_mac_builder.update(b"\x00")
391-
want_mac_builder.update(password)
391+
want_mac_builder.update(password_bytes)
392392
want_mac_builder.update(b"\x00")
393393
want_mac_builder.update(b"admin" if admin else b"notadmin")
394394
if user_type:
@@ -407,7 +407,7 @@ async def on_POST(self, request):
407407

408408
user_id = await register.registration_handler.register_user(
409409
localpart=body["username"].lower(),
410-
password=body["password"],
410+
password_hash=password_hash,
411411
admin=bool(admin),
412412
user_type=user_type,
413413
)

synapse/rest/client/v2_alpha/register.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,16 @@ async def on_POST(self, request):
426426
# we do basic sanity checks here because the auth layer will store these
427427
# in sessions. Pull out the username/password provided to us.
428428
if "password" in body:
429-
if (
430-
not isinstance(body["password"], string_types)
431-
or len(body["password"]) > 512
432-
):
429+
password = body.pop("password")
430+
if not isinstance(password, string_types) or len(password) > 512:
433431
raise SynapseError(400, "Invalid password")
434-
self.password_policy_handler.validate_password(body["password"])
432+
self.password_policy_handler.validate_password(password)
433+
434+
# If the password is valid, hash it and store it back on the request.
435+
# This ensures the hashed password is handled everywhere.
436+
if "password_hash" in body:
437+
raise SynapseError(400, "Unexpected property: password_hash")
438+
body["password_hash"] = await self.auth_handler.hash(password)
435439

436440
desired_username = None
437441
if "username" in body:
@@ -484,7 +488,7 @@ async def on_POST(self, request):
484488

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

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

551555
desired_username = params.get("username", None)
552556
guest_access_token = params.get("guest_access_token", None)
553-
new_password = params.get("password", None)
557+
new_password_hash = params.get("password_hash", None)
554558

555559
if desired_username is not None:
556560
desired_username = desired_username.lower()
@@ -583,7 +587,7 @@ async def on_POST(self, request):
583587

584588
registered_user_id = await self.registration_handler.register_user(
585589
localpart=desired_username,
586-
password=new_password,
590+
password_hash=new_password_hash,
587591
guest_access_token=guest_access_token,
588592
threepid=threepid,
589593
address=client_addr,

0 commit comments

Comments
 (0)