Wow, where did that last year go?! It's time for our annual penetration test again over at Report URI and just like we did last year, we'll be publishing the entire report, publicly, for everyone to see.

Penetration Test

If you want to know what a penetration test is, why we get penetration tests or what happened in our last penetration test, you should head over to my previous post Report URI Penetration Test which I published 1 year ago in Dec 2020. That post also details which company we use, why we use them and all of the details around our scope and methodology for the test. For now though, we don't need to cover that again so onwards to the results!

The Scope

As I talked about in our last blog post, limiting the scope of a test isn't a good thing and only makes the test less useful. We also provided the full source code of our application and Cloudflare Worker along with a full list of servers including their public and private IP addresses to enable the tester to be more effective.

This means the tester can spend more time testing our application instead of trying to find their way around or figure out how things work, and that results in a more thorough test.

The Results!

Just like last year, we weren't expecting to have many issues, and we were hoping for no issues, but realistically there's always going to be something, and there was.

I'm really, really happy with these results though, having a total of only five issues, with four of them rated 'Low' and one of them rated 'Info'. That's awesome!

Account Enumeration

Account enumeration is a common problem for online services and something that I regularly pick up on when using services. It's incredibly difficult to fully eliminate account enumeration, but I feel like we've taken all reasonable steps to reduce the obvious enumeration vectors. You can't go to our password reset page and enter an email address to get a response like "That account doesn't exist" or "Password reset email sent" like you can on many services. We deliver the same neutral response of "If that account exists, a password reset email has been sent" in either scenario so it's not obvious what happened behind the scenes.

The penetration test report highlights a known enumeration vector we have on our site and it is something we've discussed before but haven't yet thought of a good way to fix. The amount of time it takes our service to respond based on whether the user exists in our database is different, and you can observe that from the outside to determine if the user exists or not. One of the examples is the change password feature, which obviously checks to see if another user exists with that password and behaves differently depending on whether they do or not.

That's quite a big difference in response time from 788ms to 2,339ms and is a big enough gap to be useful to an attacker. The only way to fully solve this issue is to have ~constant time responses where any response from this endpoint would take ~2,500ms regardless of whether the user existed or not. Another location this is really apparent is the login page, because if the user exists we have an expensive hash operation to do on the password, which we don't have to do if the user doesn't exist.

At this stage we've marked this particular issue as "Won't Fix" as we feel we've taken all reasonable steps to make account enumeration difficult on our service. It's also worth noting that we heavily rate limit endpoints like this using our Cloudflare WAF so if abuse were to take place, the rate at which it can happen is greatly reduced.

Excessive Session Timeout

The example given in the report shows a request being made after 15 hours and indeed that would still be valid as we allow 24 hours for session validity.

Internally we've had various discussions around how we should handle things like a 'Remember Me' feature, remembering 2FA on the present device and our use of Clear Site Data on logout. We don't want to reduce the session timeout under 24 hours until we have something in place around either 'Remember Me' or remembering 2FA (which is arguably less secure) and that means at present, the current behaviour will probably remain while we decide on how we want to improve going forwards.

Vulnerabilities in Outdated Dependencies Detected

This is a valid find and one that highlighted that our current method for detecting issues like this is not sufficient. We had an issue found in a JS lib in the test last year and as a result we introduced a JS dependency check.

The problem is that the check was too basic and the lib with the issue was not loaded on our homepage. As part of our remediation efforts we've updated the jqueryui library and managed to remove the moment library.

Further to that we also have a new issue open for ensuring that all of our JS dependencies are checked so this shouldn't happen again in the future.

HTTP Security Headers Not Enabled

Well... As "Mr Security Headers" I guess I should just pack my bags and leave after this one! It's a fair find in that our CDN endpoint doesn't set all security headers, but we also don't serve anything other than CDN assets from that endpoint.

We only have a standard Nginx 403 or 404 error page for empty directories or missing files and those error pages indeed do not have security headers on them. There are also some security headers that might be a benefit on our CDN assets, so we've added those too.

This was a really easy change as we use a Cloudflare Worker in front of Report URI and I've also talked about how easy it is to add Security Headers with a Cloudflare Worker.

Insecure SSL Certificates

Considering that I deliver a training course called Practical TLS and PKI, one might be concerned to see an issue like this raised in a penetration test report! This is a bit of a non-issue though because we use Cloudflare in front of Report URI and we're using Let's Encrypt certificates, which have a maximum validity of 90 days.

At the point of the report, our certificate was ~30 days old and had ~60 days of validity remaining. With some contextual knowledge about our setup, with certificates being automatically managed by Cloudflare and that Let's Encrypt certificates are only valid for 90 days anyway, this is nothing that requires action on our side and is expected.

Reflecting on the findings

I think this was another really good test and I'm glad that we haven't introduced any new issues over the last 12 months. To only have five issues and to have them all rated as Low or Info is a really good outcome and means that our efforts to build a secure application are working.

As I've always maintained, it's not about being perfect and having no issues, it's about being transparent about the issues you have and working to resolve them quickly.

Download Full PDF Report