Fraunhofer-AISEC / omejdn-server

Omejdn is an OAuth2/OpenID connect server for IoT devices which use their private keys to request OAuth2 access tokens in order to access protected resources and websites or apps which retrieve user attributes.
Apache License 2.0
13 stars 24 forks source link

JWKS endpoint as a high risk resource of the entire dataspace #74

Open matgnt opened 1 year ago

matgnt commented 1 year ago

Hi Team,

I've been doing some risk analysis regarding DAPS / omejdn-server and came up with 1 potential risk that might be worth mentioning in https://github.com/Fraunhofer-AISEC/omejdn-server/blob/master/docs/Setup/Production.md

In general I'm arguing that the jwks.json endpoint is the most critical resource in the entire Dataspace. Why? Because in case someone can add their public key to it, all connectors would trust tokens created by this entity.

Right now, I did not find a way to do this, but I would say there is at least a risk. I think it could happen in combination with another potential issue.

To not make it too abstract, a potential scenario could be the following: An attacker smuggles in 1 line of code as part of a bigger change:

configure do
  # Load Plugins
  PluginLoader.initialize

  config = Config.setup
  set :environment, (proc { Config.base_config['environment'].to_sym })
  enable :dump_errors, :raise_errors, :quiet
  disable :show_exceptions
  bind_ip, bind_port = config['bind_to'].split(':')
  set :bind, bind_ip
  set :port, bind_port if bind_port
  enable :sessions
  set :sessions, secure: (config['front_url'].start_with? 'https://')
  set :session_store, Rack::Session::Pool
  # ----> following line enables possible attack! <---
  set :protection, :except => :path_traversal
end

This disables some very important path param checks. With this, and access to the API, an attacker could do:

POST "/api/v1/config/clients/%2E%2E%2Fomejdn%2Fevil/keys"

In such a case, the path param will be resolved to ../omejdn/evil and the evil certificate would be stored in ../omejdn/evil.cert instead of the clients directory! And consequently being loaded into the JWKS endpoint.

Flow

daps_jwks_risk

Recommendation

Therefore, I would recommend for production environments to clearly separate the JWKS endpoint - which is rather static content anyway - from the rest of the rather dynamic DAPS environment. I think this lowers the risk of such an attack dramatically.

In some cases, I could even imagine, to independently monitor the JWKS content.

Any thoughts on this?


Matthias Binzer Robert Bosch GmbH

bellebaum commented 1 year ago

Hi,

I agree that the endpoint is somewhat critical, but so is the entire authorization server.

There are some implicit assumptions that we have on the software in use. These include a certain integrity of the executed code. An attacker capable of disabling the Path traversal checks of the relevant Rack Middleware is likely also able to modify the code in a way that uses the existing keys to its liking, hence separating the JWKS endpoint would not be a sufficient protection for the particular scenario you are describing.

Additionally, though very specific to your example, usage of the Admin API requires authorization of the client using the omejdn:admin scope. An entity with this scope is considered an administrator, so they can freely modify and add/remove clients anyway, which in turn lets them place arbitrary information in a DAT.

I understand that separating the static resources would be a desirable goal for some use cases, but it would not lessen the requirements on proper code integrity and access management configuration. The unprotected endpoints (like /token) never write anything to configuration files, so there should already be some separation between code an attacker is able to execute and code which could modify the static resources. The attack surface enabling an attacker to modify these resources while not simultaneously enabling them to also gain much more privileges seems relatively small.

If you want to simply separate these resources, you can always configure your reverse proxy (which you should use anyway; Omejdn does not support TLS by itself) to ignore the Cache-Control headers and cache responses to the static endpoint indefinitely. Or you can do the "caching" yourself by creating static documents. These solutions would be way simpler than writing another software artifact.