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

Commit

Permalink
Validate client_secret parameter (#6767)
Browse files Browse the repository at this point in the history
  • Loading branch information
anoadragon453 committed Jan 24, 2020
1 parent fa4d609 commit 9f7aaf9
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 6 deletions.
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<!!-)))",
"a@b.com",
]

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)

0 comments on commit 9f7aaf9

Please sign in to comment.