My developer journal: Sprint V — Finishing up
Building a web chat app
The last entry to my Developer Journal for Lambda Labs project, where we build a service chat app for hotels and its guests.
This was the last sprint of our 5 week Lambda Labs. In the last 4 weeks, we’ve finished all the features we intended for the app and this last week was focused on polishing up the app and fixing any bugs we’ll be able to find. The last functionality we had to implement was restricting access to our API endpoints.
Restricted routing
As I was thinking about the implementation of this functionality over the weekend, I realized I’ll most likely need it in other projects as well. That’s where I’ve decided to build the middleware as a NPM package that I can reuse for other projects as well.
The first implementation of the package did not work properly 😐, as I was not passing the req, res, next
properly to the middleware. However that was a quick fix once I realized the mistake I made. Originally I wanted to connect the middleware on one place with server.use(...)
, passing in a config object and access array which would specify access rules for every endpoint and every access right on the server. This meant I would need access to req.params
property of the request object, so I could restrict access to specifics routes and route parameters. Unfortunately I found out that req.params
is only available to the route which has actually declared parameters. A middleware does not have any parameters, so there is currently no way to get route parameters in a middleware with server.use
. So the latest iteration of the package was targeted for every endpoint, where the developer would have to specify the rules which would allow access to that specific endpoint. It can be use on the whole route with router.use(...)
only on routes, which had the same access rules.
The package now needs a config
object, where the developer specifies where should the middleware look for access identifier (req.body, req.headers.authorization
), what is the identifier (user_type, ID, …) and the JWT secret string for HMAC algorithms. The other parameter the middleware takes is an access
array or string, listing the values of identifiers, which are allowed to that endpoint.
The implementation of this NPM package was pretty straight forward in our back end. I have double checked all endpoints and accesses we wanted to allowed for different user_types and then simply plugged in the middleware.
However adding the Authorization
header on all client requests with the JWT token was a lot more work. The reason being we were not very consistent with the way we build HTTP requests on our client app. Some requests were build with the fetch
API and some were build with the axios
library. Once I added headers with JWT to every request, me and my team mate Diana went and checked every single request and endpoint twice to see whether all the data is passing properly and if there are any endpoints which would be accessible by outside world/users we did not intend to allow. After initial debugging (Authorization
vs. authorization
)🤦♂️ I was very happy everything worked properly. But I found some possible security holes which could be exploited by malicious user with enough knowledge. I created tickets for these to test and fix at a later point.
Breaking our own app
The app has 4 levels of users. Top 3 are different hotel staff members (super admin, admin and receptionist) and the fourth is the guest user. I was testing if lower level users could do damage to higher level users. I realized, we had a security issue on two of our endpoints, where all hotel staff user types had access.
I tried to do the an attack as a admin, trying to delete the super admin user from the app using only the information available from the app and some JavaScript/HTTP knowledge. I was able to learn the super admin ID from one of requests response, which I could use to target the specific account. I passed the admins JWT into the request config object, so I had proper access to the endpoint on back end. I was also able to find the domain name and route for deleting users from other functionality, where the admin can delete lower level user accounts (check out guest, delete staff member). The last thing to do was to put all this information together and exploit our missing verification on back end, as we didn’t check for requesting user type and target user type. The result was successfully deleted super admin account.
The fix for this exploit was pretty simple, as I knew exactly what the vulnerability was. I’ve implemented a function that would check for requesting user type and target user type and prevent any unauthorized change requests.
All my other attempts to break the app were not successful 🎉. Besides this I was reviewing Pull Requests from my team mates who also worked on fixing small bugs, styling issues and improving the UX.
The final result
The result of these last 5 weeks can be found here for the hotel app and here for the guest chat app. We’ve also made a short video, where we demo apps feature and styling.
My final words belong to my team and their invaluable contributions to this project. Thank you for pushing forward every day for the last five weeks. Without you, this project would not be what it is today. I am grateful for every experience we had and am looking forward to working with you in future. Thank you Diana, Mark, Connor, Antonio and our PM Luke. 🙏