Enforce redirect_uris verifications in client registration (#276)

This commit is contained in:
Quentin Gliech
2022-07-08 06:41:00 -05:00
committed by GitHub
parent f383f2c8bd
commit b6d26fff27
2 changed files with 374 additions and 23 deletions
+117 -12
View File
@@ -8,9 +8,24 @@ allow {
count(violation) == 0
}
parse_uri(url) = obj {
is_string(url)
[matches] := regex.find_all_string_submatch_n("^(?P<scheme>[a-z][a-z0-9+.-]*):(?://(?P<host>((?:(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])\\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])|127.0.0.1|\\[::1\\])(?::(?P<port>[0-9]+))?))?(?P<path>/[A-Za-z0-9/.-]*)$", url, 1)
obj := {"scheme": matches[1], "authority": matches[2], "host": matches[3], "port": matches[4], "path": matches[5]}
}
secure_url(x) {
is_string(x)
startswith(x, "https://")
url := parse_uri(x)
url.scheme == "https"
url.host != "127.0.0.1"
url.host != "[::1]"
url.port == ""
}
host_matches_client_uri(x) {
client_uri := parse_uri(input.client_metadata.client_uri)
uri := parse_uri(x)
uri.host == client_uri.host
}
violation[{"msg": "missing client_uri"}] {
@@ -18,25 +33,46 @@ violation[{"msg": "missing client_uri"}] {
}
violation[{"msg": "invalid client_uri"}] {
not data.client_registration.allow_insecure_uris
not secure_url(input.client_metadata.client_uri)
}
violation[{"msg": "missing tos_uri"}] {
not input.client_metadata.tos_uri
}
violation[{"msg": "invalid tos_uri"}] {
input.client_metadata.tos_uri
not data.client_registration.allow_insecure_uris
not secure_url(input.client_metadata.tos_uri)
}
violation[{"msg": "missing policy_uri"}] {
not input.client_metadata.policy_uri
violation[{"msg": "tos_uri not on the same domain as the client_uri"}] {
input.client_metadata.tos_uri
not data.client_registration.allow_host_mismatch
not host_matches_client_uri(input.client_metadata.tos_uri)
}
violation[{"msg": "invalid policy_uri"}] {
input.client_metadata.policy_uri
not data.client_registration.allow_insecure_uris
not secure_url(input.client_metadata.policy_uri)
}
violation[{"msg": "policy_uri not on the same domain as the client_uri"}] {
input.client_metadata.policy_uri
not data.client_registration.allow_host_mismatch
not host_matches_client_uri(input.client_metadata.policy_uri)
}
violation[{"msg": "invalid logo_uri"}] {
input.client_metadata.logo_uri
not data.client_registration.allow_insecure_uris
not secure_url(input.client_metadata.logo_uri)
}
violation[{"msg": "logo_uri not on the same domain as the client_uri"}] {
input.client_metadata.logo_uri
not data.client_registration.allow_host_mismatch
not host_matches_client_uri(input.client_metadata.logo_uri)
}
violation[{"msg": "missing redirect_uris"}] {
not input.client_metadata.redirect_uris
}
@@ -49,7 +85,76 @@ violation[{"msg": "empty redirect_uris"}] {
count(input.client_metadata.redirect_uris) == 0
}
# violation[{"msg": "invalid redirect_uri"}] {
# some redirect_uri in input.client_metadata.redirect_uris
# not secure_url(redirect_uri)
# }
violation[{"msg": "invalid redirect_uri", "redirect_uri": redirect_uri}] {
# For 'web' apps, we should verify that redirect_uris are secure
input.client_metadata.application_type != "native"
some redirect_uri in input.client_metadata.redirect_uris
not data.client_registration.allow_host_mismatch
not host_matches_client_uri(redirect_uri)
}
violation[{"msg": "invalid redirect_uri"}] {
# For 'web' apps, we should verify that redirect_uris are secure
input.client_metadata.application_type != "native"
some redirect_uri in input.client_metadata.redirect_uris
not data.client_registration.allow_insecure_uris
not secure_url(redirect_uri)
}
# Used to verify that a reverse-dns formatted scheme is a strict subdomain of
# another host.
# This is used so a redirect_uri like 'com.example.app:/' works for
# a 'client_uri' of 'https://example.com/'
reverse_dns_match(host, reverse_dns) {
is_string(host)
is_string(reverse_dns)
# Reverse the host
host_parts := array.reverse(split(host, "."))
# Split the already reversed DNS
dns_parts := split(reverse_dns, ".")
# Check that the reverse_dns strictly is a subdomain of the host
array.slice(dns_parts, 0, count(host_parts)) == host_parts
}
valid_native_redirector(x) {
url := parse_uri(x)
is_localhost(url.host)
url.scheme == "http"
}
is_localhost(host) {
host == "localhost"
}
is_localhost(host) {
host == "127.0.0.1"
}
is_localhost(host) {
host == "[::1]"
}
# Custom schemes should match the client_uri, reverse-dns style
# e.g. io.element.app:/ matches https://app.element.io/
valid_native_redirector(x) {
url := parse_uri(x)
url.scheme != "http"
url.scheme != "https"
# They should have no host/port
url.authority == ""
client_uri := parse_uri(input.client_metadata.client_uri)
reverse_dns_match(client_uri.host, url.scheme)
}
violation[{"msg": "invalid redirect_uri"}] {
# For 'native' apps, we need to check that the redirect_uri is either
# a custom scheme, or localhost
# TODO: this might not be right, because of app-associated domains on mobile?
input.client_metadata.application_type == "native"
some redirect_uri in input.client_metadata.redirect_uris
not valid_native_redirector(redirect_uri)
}
@@ -2,26 +2,272 @@ package client_registration
test_valid {
allow with input.client_metadata as {
"client_uri": "https://example.com",
"tos_uri": "https://example.com/tos",
"policy_uri": "https://example.com/policy",
"client_uri": "https://example.com/",
"redirect_uris": ["https://example.com/callback"],
}
}
test_missing_client_uri {
not allow with input.client_metadata as {
"tos_uri": "https://example.com/tos",
"policy_uri": "https://example.com/policy",
"redirect_uris": ["https://example.com/callback"],
}
not allow with input.client_metadata as {"redirect_uris": ["https://example.com/callback"]}
}
test_insecure_client_uri {
not allow with input.client_metadata as {
"client_uri": "http://example.com",
"tos_uri": "https://example.com/tos",
"policy_uri": "https://example.com/policy",
"client_uri": "http://example.com/",
"redirect_uris": ["https://example.com/callback"],
}
}
test_tos_uri {
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"tos_uri": "https://example.com/tos",
"redirect_uris": ["https://example.com/callback"],
}
# Insecure
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"tos_uri": "http://example.com/tos",
"redirect_uris": ["https://example.com/callback"],
}
# Insecure, but allowed by the config
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"tos_uri": "http://example.com/tos",
"redirect_uris": ["https://example.com/callback"],
}
with data.client_registration.allow_insecure_uris as true
# Host mistmatch
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"tos_uri": "https://example.org/tos",
"redirect_uris": ["https://example.com/callback"],
}
# Host mistmatch, but allowed by the config
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"tos_uri": "https://example.org/tos",
"redirect_uris": ["https://example.com/callback"],
}
with data.client_registration.allow_host_mismatch as true
}
test_logo_uri {
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"logo_uri": "https://example.com/logo.png",
"redirect_uris": ["https://example.com/callback"],
}
# Insecure
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"logo_uri": "http://example.com/logo.png",
"redirect_uris": ["https://example.com/callback"],
}
# Insecure, but allowed by the config
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"logo_uri": "http://example.com/logo.png",
"redirect_uris": ["https://example.com/callback"],
}
with data.client_registration.allow_insecure_uris as true
# Host mistmatch
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"logo_uri": "https://example.org/logo.png",
"redirect_uris": ["https://example.com/callback"],
}
# Host mistmatch, but allowed by the config
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"logo_uri": "https://example.org/logo.png",
"redirect_uris": ["https://example.com/callback"],
}
with data.client_registration.allow_host_mismatch as true
}
test_policy_uri {
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"policy_uri": "https://example.com/policy",
"redirect_uris": ["https://example.com/callback"],
}
# Insecure
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"policy_uri": "http://example.com/policy",
"redirect_uris": ["https://example.com/callback"],
}
# Insecure, but allowed by the config
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"policy_uri": "http://example.com/policy",
"redirect_uris": ["https://example.com/callback"],
}
with data.client_registration.allow_insecure_uris as true
# Host mistmatch
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"policy_uri": "https://example.org/policy",
"redirect_uris": ["https://example.com/callback"],
}
# Host mistmatch, but allowed by the config
allow with input.client_metadata as {
"client_uri": "https://example.com/",
"policy_uri": "https://example.org/policy",
"redirect_uris": ["https://example.com/callback"],
}
with data.client_registration.allow_host_mismatch as true
}
test_redirect_uris {
# Missing redirect_uris
not allow with input.client_metadata as {"client_uri": "https://example.com/"}
# redirect_uris is not an array
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"redirect_uris": "https://example.com/callback",
}
# Empty redirect_uris
not allow with input.client_metadata as {
"client_uri": "https://example.com/",
"redirect_uris": [],
}
}
test_web_redirect_uri {
allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["https://example.com/second/callback", "https://example.com/callback"],
}
# Insecure URL
not allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["http://example.com/callback", "https://example.com/callback"],
}
# Insecure URL, but allowed by the config
allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["http://example.com/callback", "https://example.com/callback"],
}
with data.client_registration.allow_insecure_uris as true
# Host mismatch
not allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["https://example.com/second/callback", "https://example.org/callback"],
}
# Host mismatch, but allowed by the config
allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["https://example.com/second/callback", "https://example.org/callback"],
}
with data.client_registration.allow_host_mismatch as true
# No custom scheme allowed
not allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["com.example.app:/callback"],
}
# localhost not allowed
not allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["http://locahost:1234/callback"],
}
# localhost not allowed
not allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["http://127.0.0.1:1234/callback"],
}
# localhost not allowed
not allow with input.client_metadata as {
"application_type": "web",
"client_uri": "https://example.com/",
"redirect_uris": ["http://[::1]:1234/callback"],
}
}
test_native_redirect_uri {
# This has all the redirect URIs types we're supporting for native apps
allow with input.client_metadata as {
"application_type": "native",
"client_uri": "https://example.com/",
"redirect_uris": [
"com.example.app:/callback",
"http://localhost/callback",
"http://localhost:1234/callback",
"http://127.0.0.1/callback",
"http://127.0.0.1:1234/callback",
"http://[::1]/callback",
"http://[::1]:1234/callback",
],
}
# We don't allow HTTP URLs other than localhost
not allow with input.client_metadata as {
"application_type": "native",
"client_uri": "https://example.com/",
"redirect_uris": ["https://example.com/"],
}
not allow with input.client_metadata as {
"application_type": "native",
"client_uri": "https://example.com/",
"redirect_uris": ["http://example.com/"],
}
# We don't allow HTTPS on localhost
not allow with input.client_metadata as {
"application_type": "native",
"client_uri": "https://example.com/",
"redirect_uris": ["https://localhost:1234/"],
}
# Ensure we're not allowing localhost as a prefix
not allow with input.client_metadata as {
"application_type": "native",
"client_uri": "https://example.com/",
"redirect_uris": ["http://localhost.com/"],
}
# For custom schemes, it should match the client_uri hostname
not allow with input.client_metadata as {
"application_type": "native",
"client_uri": "https://example.com/",
"redirect_uris": ["org.example.app:/callback"],
}
}
test_reverse_dns_match {
client_uri := parse_uri("https://element.io/")
redirect_uri := parse_uri("io.element.app:/callback")
reverse_dns_match(client_uri.host, redirect_uri.scheme)
}