WebThingsIO / gateway

WebThings Gateway - a self-hosted web application for monitoring and controlling a building over the web
http://webthings.io/gateway
Mozilla Public License 2.0
2.62k stars 339 forks source link

Implement OAuth 2.0 Authorization Server Metadata - closes #3143 #3147

Closed benfrancis closed 3 months ago

benfrancis commented 3 months ago

Closes #3143.

Following this implementation, a GET request to /.well-known/oauth-authorization-server will respond with a JSON document which looks like the following...

{
    "issuer": "http://localhost:8080",
    "authorization_endpoint": "http://localhost:8080/oauth/authorize",
    "token_endpoint": "http://localhost:8080/oauth/token",
    "response_types_supported": [
        "code"
    ],
    "scopes_supported": [
        "/things",
        "/things:readwrite"
    ]
}

...with the host set to match the host of the request.

benfrancis commented 3 months ago

@tim-hellhake One concern I have with this implementation is that because a gateway may be served via multiple host names (e.g. foo.webthings.io or gateway.local or mydomain.com) it currently takes the host from the Host header in an HTTP request. I wonder if this might be vulnerable to a host header injection attack.

If so, is there a better way to determine the host the gateway is being served on? What if there are more than one or a custom static or dynamic DNS setup is in place?

tim-hellhake commented 3 months ago

@benfrancis

One concern I have with this implementation is that because a gateway may be served via multiple host names (e.g. foo.webthings.io or gateway.local or mydomain.com) it currently takes the host from the Host header in an HTTP request. I wonder if this might be vulnerable to a host header injection attack.

Technically, yes, but what's the point of fooling yourself? Usually, the attacker fakes the Host header to trick the server into doing things it shouldn't. Since the server does not rely on the Host header, the attacker is the only one affected.

There is one thing, though... If a proxy caches the request, the attacker might be possible to poison the cache to lure users to a malicious server.

Keycloak, a pretty mature authorization server, also relies on the host header. The key is to pin the hostname via the config parameter or configure the reverse proxy to override the Host header so that you can trust it.

The best way to mitigate this attack vector is to allow only localhost or the configured tunnel hostname. It would also be nice to allow the user to configure the hostname to support custom setups.

If so, is there a better way to determine the host the gateway is being served on? What if there are more than one or a custom static or dynamic DNS setup is in place?

I don't see a way to achieve this without knowing the exact user setup.

benfrancis commented 3 months ago

@tim-hellhake Thank you for the feedback. Let's look at providing a way to statically configure the canonical domain of the gateway rather than deriving it dynamically from a Host header in the future. I note there are other places in the codebase where we currently use the Host header too. This could be part of #82.