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 password reset process #7538

Merged
merged 2 commits into from
May 20, 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/7538.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hash passwords as early as possible during password reset.
5 changes: 1 addition & 4 deletions synapse/handlers/set_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,13 @@ def __init__(self, hs):
async def set_password(
self,
user_id: str,
new_password: str,
password_hash: str,
logout_devices: bool,
requester: Optional[Requester] = None,
):
if not self.hs.config.password_localdb_enabled:
raise SynapseError(403, "Password change disabled", errcode=Codes.FORBIDDEN)

self._password_policy_handler.validate_password(new_password)
password_hash = await self._auth_handler.hash(new_password)

try:
await self.store.user_set_password_hash(user_id, password_hash)
except StoreError as e:
Expand Down
13 changes: 11 additions & 2 deletions synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,14 @@ async def on_PUT(self, request, user_id):
else:
new_password = body["password"]
logout_devices = True

new_password_hash = await self.auth_handler.hash(new_password)

await self.set_password_handler.set_password(
target_user.to_string(), new_password, logout_devices, requester
target_user.to_string(),
new_password_hash,
logout_devices,
requester,
)

if "deactivated" in body:
Expand Down Expand Up @@ -523,6 +529,7 @@ def __init__(self, hs):
self.store = hs.get_datastore()
self.hs = hs
self.auth = hs.get_auth()
self.auth_handler = hs.get_auth_handler()
self._set_password_handler = hs.get_set_password_handler()

async def on_POST(self, request, target_user_id):
Expand All @@ -539,8 +546,10 @@ async def on_POST(self, request, target_user_id):
new_password = params["new_password"]
logout_devices = params.get("logout_devices", True)

new_password_hash = await self.auth_handler.hash(new_password)

await self._set_password_handler.set_password(
target_user_id, new_password, logout_devices, requester
target_user_id, new_password_hash, logout_devices, requester
)
return 200, {}

Expand Down
21 changes: 18 additions & 3 deletions synapse/rest/client/v2_alpha/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,27 @@ def __init__(self, hs):
self.auth = hs.get_auth()
self.auth_handler = hs.get_auth_handler()
self.datastore = self.hs.get_datastore()
self.password_policy_handler = hs.get_password_policy_handler()
self._set_password_handler = hs.get_set_password_handler()

@interactive_auth_handler
async def on_POST(self, request):
body = parse_json_object_from_request(request)

# we do basic sanity checks here because the auth layer will store these
# in sessions. Pull out the username/password provided to us.
clokep marked this conversation as resolved.
Show resolved Hide resolved
if "new_password" in body:
new_password = body.pop("new_password")
if not isinstance(new_password, str) or len(new_password) > 512:
raise SynapseError(400, "Invalid password")
self.password_policy_handler.validate_password(new_password)

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

# there are two possibilities here. Either the user does not have an
# access token, and needs to do a password reset; or they have one and
# need to validate their identity.
Expand Down Expand Up @@ -276,12 +291,12 @@ async def on_POST(self, request):
logger.error("Auth succeeded but no known type! %r", result.keys())
raise SynapseError(500, "", Codes.UNKNOWN)

assert_params_in_dict(params, ["new_password"])
new_password = params["new_password"]
assert_params_in_dict(params, ["new_password_hash"])
new_password_hash = params["new_password_hash"]
logout_devices = params.get("logout_devices", True)

await self._set_password_handler.set_password(
user_id, new_password, logout_devices, requester
user_id, new_password_hash, logout_devices, requester
)

return 200, {}
Expand Down
4 changes: 2 additions & 2 deletions synapse/rest/client/v2_alpha/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,8 @@ async def on_POST(self, request):
raise SynapseError(400, "Invalid 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 the password is valid, hash it and store it back on the body.
# This ensures that only 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)
Expand Down