13

I work at a small consultancy and we often make web apps for our clients. One part of the web app that is often repetitive to write is the authentication system. In a lot of our web apps we would like to support OAuth login from the various providers as well as email-based registration.

However, programming this in every web application is time-consuming. Ideally what we'd like is a generic, authentication proxy server that sits in front of the application server and handles everything related to authentication (including login, registration, and logout). I'm wondering if something like this would be secure? Or if there is any security-related reason why it should not be done like this?

I'm imagining it would look like this:

reverse proxy auth server

Auth Server

  • This is basically a reverse proxy server. It has its own database with information related to authentication (for example, email address, password, etc).
  • It checks each request for a specific cookie with session information (for example, a JWT in a cookie called SESSION). This session information would be proof that the user has logged in correctly at some point in the past. If the session information is correct, it adds an X-UserId header to the request and forwards it to the Application Server. If there is no cookie with authentication information, or if the session information is incorrect, then it forwards the request to the application server without the X-UserId header.
  • It will have specific routes for logging-in, logging-out, and registration (for example, /login, /logout, and /register). It will handle these requests completely on it's own. The Application Server will not even be aware of these requests.

Application Server

  • This is a normal web application server.
  • It performs no authentication, relying entirely on the X-UserId header from the Auth Server.
  • It is not accessible from the outside world. The only way to access it is by going through the Auth Server.
  • It also has its own database. It is filled with all the information related to the application (except for authentication information).

Here is an example of how login could work, as well as the interaction between the Auth Server and Application Server:

Here are the steps for getting the login.html page.

  1. The client sends a request for /login.html.
  2. The Auth Server receives the request. The Auth Server tries to look for a cookie called SESSION. It doesn't exist, so it forwards the request to the Application Server without adding a X-UserId header.
  3. The Application Server receives the request. There is a route defined for /login.html and it doesn't require the X-UserId header to exist, so it returns the HTML for login.html.
  4. The Authentication Sever receives the response, and forwards it back to the user.

The login.html page could contain the JS code for actually logging in. Here is one possible way to do it.

  1. The client sends an AJAX request to /login/email to login using an email address and password.
  2. The Auth Server receives the request. Since this is a request for the Auth Server, it does not forward the request to the Application Server.
  3. The Auth Server looks at the request body for an email address and password. It checks the email address and password in its Auth DB.
  4. If the email address and password are correct, it returns an HttpOnly cookie called SESSION that contains a JWT.

After this succeeds, the user could be directed to /home.html. The steps for this are similar to /login.html, except that the user is actually required to be authenticated:

  1. The user sends a request to /home.html.
  2. The Auth Server receives the request. The Auth Server checks the SESSION cookie and decodes the JWT. The Auth Server gets the User Id from the database based on the information in the JWT. Let's say that the User Id is 5.
  3. The Auth Server adds a X-UserId=5 header to the request and forwards it to the Application Server. (In the case where the SESSION cookie doesn't exist, or the JWT is not able to be successfully decoded, the Auth Server will forward the request to the Application Server without adding a X-UserId header. The following steps are written assuming that the authentication succeeds and the Auth Server adds the `X-UserId header.)
  4. The Application Server receives the request. Since the request is for /home.html, the Application Server knows that a valid User Id is required, so it checks the X-UserId header.
  5. The Application Server successfully gets the User Id from the X-UserId header and uses it to pull data out of the Application DB and build the home.html page.
  6. The Application Server sends the response with the home.html page to the Auth Server.
  7. The Auth Server receives the response, and forwards it back to the User.

Here is some additional information that didn't really fit above:

  • The Auth Server would remove the X-UserId header if sent by a user before forwarding the request to the Application Server.
  • The Auth Server could be relatively general, and used on multiple different projects. The Application Server wouldn't have to worry at all about authentication.
  • The Application Server would need to be in charge of authorization (for example, making sure that each user could only edit their own information).
  • The Auth Server would completely handle new user registration. This could include an OAuth flow (for using OAuth as provided by Google, Twitter, Facebook, etc), as well as simple email-based registration. When a new user registers, the Auth Server would create a User Id and link it to the user's email address, Google username, Twitter username, etc. The Application Server would only ever see this User Id (never the user's email address, Google username, Twitter username, etc).

Is a setup like this secure?

illabout
  • 233
  • 1
  • 2
  • 5
  • I'm not too familiar with Kong, but the documentation suggests that Kong could be used as a reverse proxy to do authentication for a bunch of micro-services. I imagine this would work similar to how I am describing the Auth Server above. – illabout Jun 06 '18 at 07:51
  • 1
    As long as there is no way to spoof the `X-UserId` by sending it directly by the user (looks like you handled this), by somehow bypassing the Auth Server or by bugs like header injection this looks like a sound setup. Just to be sure the Auth Server might maybe replay-safe sign `X-UserId` so that it definitely cannot be spoofed. – Steffen Ullrich Jun 06 '18 at 08:23
  • 1
    There are lots of areas for things to go wrong in implementation. Ensure keys are unique for each application, and that a user can't use their session ID from one application to login to another. Use RSA algorithm. Using JWTs is a client-side state mechanism, and by design isn't as secure as server-side state. Also, as @SteffenUllrich says, be wary of spoofing, especially if the back-end web servers are ever exposed to the internet directly in any way. – SilverlightFox Jun 06 '18 at 08:56
  • @SteffenUllrich Do you have any links talking about how replay-safe signing works? In practice, if there was a third-party that could capture requests/response between the Auth Server and Application Server, that sounds like it would be a pretty big problem in its own right. – illabout Jun 06 '18 at 11:19
  • @SilverlightFox I'm not sure I understood your comment. Where are you suggesting that RSA be used? I imagine that the cookies passed to the client should be signed by the server (and possibly encrypted), but I didn't think that RSA would really play a role here. Instead, just some sort of cryptographic hash function and something like AES would be fine. My suggestion of using JWTs for this was more of an example and not necessarily what we are planning on doing. Sorry if it caused any confusion! – illabout Jun 06 '18 at 11:30
  • 1
    @illabout: You might something like `X-UserId: Id Time HMAC` where `HMAC=hash(shared_secret, Time, Id)`. If having a shared secret is not an option you could also use public key cryptography (i.e. RSA or similar) and do a digital signature with the private key and validate it with the public key. Also, such values might not only leak by somebody sniffing but by your application reflecting input data to the user, for example in error messages. Insofar it would be better if a value can not be replayed (therefore timestamp which should be verified by the application to be current). – Steffen Ullrich Jun 06 '18 at 11:33
  • 1
    I mean use JWT with RSA signature, because they can't be brute forced. – SilverlightFox Jun 06 '18 at 11:53

2 Answers2

3

Reverse Proxies are pretty common for what you are asking. Quite a few companies make servers designed for what your asking so you could use that as a reference.

For instance, I've used WebSeal (IBM ISAM) quite a bit at company's (seems popular for some reason around me). They have modules already build for OAuth, and most other type of authentication.

You can use these servers to:

  • provide a single "Identity" across multiple systems with difference User stores.
  • Provide a more secure method of Authentication for users, on legacy systems which may lack the features you want (e.g Provide OAuth authorizations for users, while the proxy uses Basic Auth for a back-end call.)
  • Isolate systems by forcing connections through a single point
  • Combinations for all of these.

Design notes:

  • OAuth is an AUTHORIZATION protocol NOT an AUTHENTICATION protocol.
  • When you are trying to establish identity, look to OpenID or JWT or SAML or run as your owned Identity Provider.
  • When you are trying to authorize a request look at OAuth 2.0 or JWT.

  • Use an ID Token for Identity (e.g OpenID spec, or if rolling your own look at JWT's)

  • Have Web Applications use an authorization token to get a access token.

  • Use access tokens to provide protected resources.

Shane Andrie
  • 3,780
  • 1
  • 13
  • 16
1

This is a great setup.

Look into google's beyond corp documentation to learn more about how they think about networks and this type of access.

https://cloud.google.com/beyondcorp/

If you do want to roll your own, you can use something like duo network gateway. But the problem with reverse proxies is you have to point all the DNS to the same IP of the reverse proxy and manually configure each resource.

Another approach is a combination of PAC files and a forward proxy that requires authentication. Then you can specify that ANY URL be sent through the forward proxy, the forward proxy performs authentication, and passes the traffic to the resource wherever it is. The resource can be configured to only allow source IP address.

Jonathan
  • 2,288
  • 13
  • 16