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

Validate client_secret parameter #6767

Merged
merged 8 commits into from
Jan 24, 2020
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions changelog.d/6767.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Validate `client_secret` parameter using the regex provided by the Client-Server API, temporarily allowing `:` characters for older clients. The `:` character will be removed in a future release.
4 changes: 3 additions & 1 deletion synapse/handlers/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from synapse.config.emailconfig import ThreepidBehaviour
from synapse.http.client import SimpleHttpClient
from synapse.util.hash import sha256_and_url_safe_base64
from synapse.util.stringutils import random_string
from synapse.util.stringutils import assert_valid_client_secret, random_string

from ._base import BaseHandler

Expand Down Expand Up @@ -84,6 +84,8 @@ def threepid_from_creds(self, id_server, creds):
raise SynapseError(
400, "Missing param client_secret in creds", errcode=Codes.MISSING_PARAM
)
assert_valid_client_secret(client_secret)

session_id = creds.get("sid")
if not session_id:
raise SynapseError(
Expand Down
23 changes: 18 additions & 5 deletions synapse/rest/client/v2_alpha/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
)
from synapse.push.mailer import Mailer, load_jinja2_templates
from synapse.util.msisdn import phone_number_to_msisdn
from synapse.util.stringutils import assert_valid_client_secret
from synapse.util.threepids import check_3pid_allowed

from ._base import client_patterns, interactive_auth_handler
Expand Down Expand Up @@ -81,6 +82,8 @@ async def on_POST(self, request):

# Extract params from body
client_secret = body["client_secret"]
assert_valid_client_secret(client_secret)

email = body["email"]
send_attempt = body["send_attempt"]
next_link = body.get("next_link") # Optional param
Expand Down Expand Up @@ -166,8 +169,9 @@ async def on_GET(self, request, medium):
)

sid = parse_string(request, "sid", required=True)
client_secret = parse_string(request, "client_secret", required=True)
token = parse_string(request, "token", required=True)
client_secret = parse_string(request, "client_secret", required=True)
assert_valid_client_secret(client_secret)

# Attempt to validate a 3PID session
try:
Expand Down Expand Up @@ -353,6 +357,8 @@ async def on_POST(self, request):
body = parse_json_object_from_request(request)
assert_params_in_dict(body, ["client_secret", "email", "send_attempt"])
client_secret = body["client_secret"]
assert_valid_client_secret(client_secret)

email = body["email"]
send_attempt = body["send_attempt"]
next_link = body.get("next_link") # Optional param
Expand Down Expand Up @@ -413,6 +419,8 @@ async def on_POST(self, request):
body, ["client_secret", "country", "phone_number", "send_attempt"]
)
client_secret = body["client_secret"]
assert_valid_client_secret(client_secret)

country = body["country"]
phone_number = body["phone_number"]
send_attempt = body["send_attempt"]
Expand Down Expand Up @@ -493,8 +501,9 @@ async def on_GET(self, request):
)

sid = parse_string(request, "sid", required=True)
client_secret = parse_string(request, "client_secret", required=True)
token = parse_string(request, "token", required=True)
client_secret = parse_string(request, "client_secret", required=True)
assert_valid_client_secret(client_secret)

# Attempt to validate a 3PID session
try:
Expand Down Expand Up @@ -559,6 +568,7 @@ async def on_POST(self, request):

body = parse_json_object_from_request(request)
assert_params_in_dict(body, ["client_secret", "sid", "token"])
assert_valid_client_secret(body["client_secret"])

# Proxy submit_token request to msisdn threepid delegate
response = await self.identity_handler.proxy_msisdn_submit_token(
Expand Down Expand Up @@ -600,8 +610,9 @@ async def on_POST(self, request):
)
assert_params_in_dict(threepid_creds, ["client_secret", "sid"])

client_secret = threepid_creds["client_secret"]
sid = threepid_creds["sid"]
client_secret = threepid_creds["client_secret"]
assert_valid_client_secret(client_secret)

validation_session = await self.identity_handler.validate_threepid_session(
client_secret, sid
Expand Down Expand Up @@ -637,8 +648,9 @@ async def on_POST(self, request):
body = parse_json_object_from_request(request)

assert_params_in_dict(body, ["client_secret", "sid"])
client_secret = body["client_secret"]
sid = body["sid"]
client_secret = body["client_secret"]
assert_valid_client_secret(client_secret)

await self.auth_handler.validate_user_via_ui_auth(
requester, body, self.hs.get_ip_from_request(request)
Expand Down Expand Up @@ -676,8 +688,9 @@ async def on_POST(self, request):
assert_params_in_dict(body, ["id_server", "sid", "client_secret"])
id_server = body["id_server"]
sid = body["sid"]
client_secret = body["client_secret"]
id_access_token = body.get("id_access_token") # optional
client_secret = body["client_secret"]
assert_valid_client_secret(client_secret)

requester = await self.auth.get_user_by_req(request)
user_id = requester.user.to_string()
Expand Down
3 changes: 3 additions & 0 deletions synapse/rest/client/v2_alpha/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from synapse.push.mailer import load_jinja2_templates
from synapse.util.msisdn import phone_number_to_msisdn
from synapse.util.ratelimitutils import FederationRateLimiter
from synapse.util.stringutils import assert_valid_client_secret
from synapse.util.threepids import check_3pid_allowed

from ._base import client_patterns, interactive_auth_handler
Expand Down Expand Up @@ -116,6 +117,8 @@ async def on_POST(self, request):

# Extract params from body
client_secret = body["client_secret"]
assert_valid_client_secret(client_secret)

email = body["email"]
send_attempt = body["send_attempt"]
next_link = body.get("next_link") # Optional param
Expand Down
17 changes: 17 additions & 0 deletions synapse/util/stringutils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd
# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -14,14 +15,22 @@
# limitations under the License.

import random
import re
import string

import six
from six import PY2, PY3
from six.moves import range

from synapse.api.errors import Codes, SynapseError

_string_with_symbols = string.digits + string.ascii_letters + ".,;:^&*-_+=#~@"

# https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-register-email-requesttoken
# Note: The : character is allowed here for older clients, but will be removed in a
# future release. Context: https://github.com/matrix-org/synapse/issues/6766
client_secret_regex = re.compile(r"^[0-9a-zA-Z\.\=\_\-\:]+$")

# random_string and random_string_with_symbols are used for a range of things,
# some cryptographically important, some less so. We use SystemRandom to make sure
# we get cryptographically-secure randoms.
Expand Down Expand Up @@ -109,3 +118,11 @@ def exception_to_unicode(e):
return msg.decode("utf-8", errors="replace")
else:
return msg


def assert_valid_client_secret(client_secret):
"""Validate that a given string matches the client_secret regex defined by the spec"""
if client_secret_regex.match(client_secret) is None:
raise SynapseError(
400, "Invalid client_secret parameter", errcode=Codes.INVALID_PARAM
)
51 changes: 51 additions & 0 deletions tests/util/test_stringutils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright 2020 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from synapse.api.errors import SynapseError
from synapse.util.stringutils import assert_valid_client_secret

from .. import unittest


class StringUtilsTestCase(unittest.TestCase):
def test_client_secret_regex(self):
"""Ensure that client_secret does not contain illegal characters"""
good = [
"abcde12345",
"ABCabc123",
"_--something==_",
"...--==-18913",
"8Dj2odd-e9asd.cd==_--ddas-secret-",
# We temporarily allow : characters: https://github.com/matrix-org/synapse/issues/6766
# To be removed in a future release
"SECRET:1234567890",
]

bad = [
"--+-/secret",
"\\dx--dsa288",
"",
"AAS//",
"asdj**",
">X><Z<!!-)))",
"[email protected]",
]

for client_secret in good:
assert_valid_client_secret(client_secret)

for client_secret in bad:
with self.assertRaises(SynapseError):
assert_valid_client_secret(client_secret)