Worked example - Deciding how to audit the URL for unauthorised requests
Photo by Jens Lelie on Unsplash
Context
My client recently found that some users were getting errors using parts of our web application because they were able to perform actions on the front end which they were not authorised to do on the back end. However, the users were not reporting these errors when they occurred, so my client doesn't know what to do about them.
Have the permissions for some users been set up incorrectly? Is it hindering their ability to work? Is there a bug in the system? Are the wrong permissions required for some actions? Without specific details, it's hard to answer these questions.
My client has asked that we record these errors for future analysis. We need to know (among other things) which user got the error, what page of the application they were on when the error occurred, and what API route they were trying to access.
What I needed to decide
By default, a HTTP request does not include the URL of the webpage which initiates it. When the server rejects a request as unauthorised, it doesn't have access to all the information my client wants recording in this scenario. I needed to decide the best way to get the URL of the webpage the user was on when the error occurred.
I thought of two ways to achieve this (though I'm sure there could be others):
- Send the URL as part of every request (e.g. as a custom header), so that if the server rejects the request as unauthorised it already has the URL and can save it to the database.
- Make a new API request with the URL when any other request is rejected as unauthorised.
Factors I considered
- Adding data to the headers of every request increases payload, slowing down all requests slightly.
- The vast majority of requests won't need this info. It's only relevant for unauthorised requests.
- Relying on the client to make a new API call means the behaviour could fail if the client side code is changed.
- A malicious actor could be making unauthorised requests separately from the client app.
What I decided
The point of this particular audit isn't to be a security log. We have other logging for that, recording different (though overlapping) information. The point of this audit is to know which users have incorrect permissions, allowing them to trigger API requests which they shouldn't be able to use. The reason for knowing their URL is to help debug those issues and clarify what permissions are appropriate in individual cases.
In the light of that, I decided that it's not worth slowing down all API requests, even if only slightly. Instead I wrote some code in the client app to handle unauthorised requests by triggering a new API call to audit them behind the scenes. If that API call fails, or is circumvented by the user, that's not really a problem for us. It isn't part of the application's security, and only exists so that we can better fix whatever problems the user is encountering.
Conclusion
This is an example of the sort of decision programmers make most days. Either approach would have worked fine, and there are probably other approaches which would also have worked. There were pros and cons to both, and I had to choose which seems most appropriate for meeting the needs of my client.