Every year, just as we start to put up the Christmas Tree, we have another tradition at Report URI which is to conduct our annual penetration test!

šŸŽ…šŸŽ„šŸŽ --> šŸ©»šŸ”šŸ„·

This will be our 6th annual penetration test that we've posted completely publicly, just as before, and we'll be covering a full run down of what was found and what we've done about it.

Penetration Tests

If you find this post interesting or would like to see our previous reports, then here are the links to each and every one of those!

Report URI Penetration Test 2020

Report URI Penetration Test 2021

Report URI Penetration Test 2022

Report URI Penetration Test 2023

Report URI Penetration Test 2024

The Results

2025 has been another good year for us as we've continued to focus on the security of our product, and the results of the test show that. Not only have we added a bunch of new features, we've also made some significant changes to our infrastructure and made countless changes and improvements to existing functionality too. The tester had what was effectively an unlimited scope to target the application, a full 'guidebook' on how to get up and running with our product and a demo call to ensure all required knowledge was handed over before the test. We wanted them to hit the ground running and waste no time getting stuck in.

Finding an Info rated issue, three Low rated and a Medium severity issue definitely gives us something to talk about, so let's look at that Medium severity first.

CSV Formula Injection

The entire purpose of our service is to ingest user-generated data and then display that in some way. Every single telemetry report we process comes from either a browser or a mail server, and the entire content, whilst conforming to a certain schema, is essentially free in terms of the values of fields. We have historically focused on, and thus far prevented, XSS from creeping it's way in, but this bug takes a slightly different form.

Earlier this year, June 11th to be exact, we released a new feature that allowed for a raw export of telemetry data. This was a commonly requested feature from our customers and we provided two export formats for the data, the native JSON that telemetry is ingested in, or a CSV variant too. You can see the export feature being used here on CSP Reports.

Because the data export is a raw export, we are providing the telemetry payloads that we received from the browser or email server, just as you would have received them if you collected them yourself. It turns out that as these fields obviously contain user generated data, and you can do some trickery!

The specific example given by the tester uses a NEL report, but it's not a problem specific to NEL reports, it's possible across all of our telemetry. Looking at the example in the report, you can see how the tester crafted a specific payload:

This type value contains an Excel command that will make it through to the CSV export as it represents the raw value sent by the client. The steps to leverage this are pretty convoluted, but I will quickly summarise them here by using an example if you want to target my good friend Troy Hunt, who runs Have I Been Pwned which indeed uses Report URI.

  1. Identify the subdomain that Troy uses on our service, which is public information.
  2. Send telemetry events to that endpoint with specifically crafted payloads which will be processed in to Troy's account.
  3. Troy would then need to view that data in our UI, and for any reason wish to export that data as a CSV file.
  4. Then, Troy would have to open that CSV file specifically in Excel, and bypass the two security warnings presented when opening the file.

There are a couple of points in there that require some very specific actions from Troy, and the chances of all of those things happening are a little far-fetched, but still, we gave it a lot of consideration. The problem I had is that the export is raw, it's meant to be an export of what the browser or email server provided to us so you have a verbatim copy of the raw data. Sanitising that data is also tricky as there isn't a universal way to sanitise CSV because it depends on what application you're going to open it with, something I discovered when testing out our fix!

Our current approach is to add a single quote to the start of the value if we detect that the first non-whitespace character is a potentially dangerous character, and that seems to have reliably solved the issue. I'm happy to hear feedback on this approach, or alternative suggestions, so drop by the comments below if you can contribute!

Vulnerabilities in Outdated Dependencies

Not again! Actually, I'm pretty happy with the finding here, but I will explain both of the issues raised and what we did and didn't do about them.

Bootstrap v3.x

The last version of Bootstrap 3 was v3.4.1 which did have an XSS vulnerability present (source). We have since cloned v3.4.1 and patched it up to v3.4.4 ourselves to fix various issues that have been found, including the XSS issue raised here. The issue is still flagged in v3.x though, which is why it was flagged in our custom patched version, so in reality, there is no problem here and we're happy to keep using our own version.

Another tricky one because the Snyk data that we refer to lists no vulnerability in this library (source) which is why our own tooling hasn't flagged this to us. That said, the NVD does list a CVE for this version of the jQuery Cookie plugin (source) but I can't find other data to back that up, including their own link to Snyk which doesn't list a vulnerability. Rather than spend too much time on this for what is a relatively simple plugin, we decided to remove it and implement the functionality that we need ourselves, solving the problem.

With both of those issues addressed, I'm happy to say that we can consider this as resolved too.

Insufficient Session Expiration

This issue has been raised previously in our 2021 penetration test, and our position remains similar to what it was back then. Whilst I'm leaning towards 24 hours being on the top end, and we will probably bring this down shortly, I also feel like the recommended 20 minutes is just too short. Going away from your desk for a coffee break and returning to find you've been logged out just seems a little bit too aggressive for us.

Looking at the other suggested concerns, if you have malware running on your endpoint, or someone gains physical access to extract a session cookie, I feel like you probably have much bigger concerns to address too! Overall, I acknowledge the issue raised and we're currently thinking something in the 12-18 hours range might be better suited.

Insecure TLS Configuration

You could argue that I know a thing or two about TLS configuration, heck, you can even attend my training course on it if you like! The issue that somebody like a pen tester coming in from the outside is that they're always going to lack the specific context on why we made the configuration choices we did, and I also recognise that it's very hard to give generic advice that fits all situations.

We have priority in our cipher suite list for all of the best cipher suites as some of the first choices, and we have support for the latest protocol versions too. We're doing so well in fact that we get an A grade on the SSL Labs test (results):

I'm happy with where our current configuration is but as always, we will keep it in constant review as time goes by!

No Account Lockout or Timeout Mechanism

This is a controversial topic and it's quite interesting to see it come up because we specifically cover this in the Hack Yourself First workshop that I deliver alongside Troy Hunt! I absolutely recognise the goal that a mechanism like this would be trying to achieve, but I worry about the potential negative side-effects.

Using account enumeration it's often trivial to determine if someone has an account on our service, so you might be able to determine that [email protected] is indeed registered. You then may want to start guessing different passwords to try and log in to Troy's account, and there lies the problem. How many times should you be able to sit there and guess a password before something happens? An account lockout mechanism might work along the lines of saying 'after 5 unsuccessful login attempts we will lock the account and require a password reset', or perhaps say 'after 5 unsuccessful login attempts you will not be able to login again for 3 minutes'. Both of these would stop the attacker from making significant progress, but both of them also present an opportunity to be abused by an attacker too. The attacker can now sit and make repeated login attempts to Troy's account and keep it in a perpetually locked state, denying him the use of his account, a Denial-of-Service attack (DoS)! It's for this reason I'm generally not fond of these account lockout / account suspension mechanisms, they do provide an opportunity for abuse.

Instead, we rely on a different set of protections to try and limit the impact of attacks like these.

  1. We implement incredibly strict rate-liming on sensitive endpoints, including our authentication endpoints. This would slow an attacker down so the rate at which they could make guesses would be reduced. (This was disabled for the client IP addresses used by the tester)
  2. We utilise Cloudflare's Bot Management across our application, which includes authentication flows, and if there is any reasonable suspicion that the client is a bot or in some way automated, they would be challenged. This prevents attackers from automating their attacks, ultimately slowing them down. (This was disabled for the client IP addresses used by the tester)
  3. We have taken exceptional measures around password security. We require strong and complex passwords for our service, check for commonly used passwords against the Pwned Passwords API, use zxcvbn for strength testing, and more. This makes it highly unlikely that the password being guessed could be guessed easily, and you can read the full details on our password security measures here.
  4. We support, and have a very high adoption of, 2FA across our service. This means that there's a very good chance that if the attacker was able to guess a password, the next prompt would be to input the TOTP code from the authenticator app!

Given the above concerns around lockout mechanisms, and the additional measures we have in place, I continue to remain happy with our current position but we will always review these things on an ongoing basis.

That's a wrap!

Given how much continued development we see in the product, and how much our infrastructure is evolving over time, I'm really pleased to see that our continued efforts to maintain the security of our product, and ultimately our customer's data, has paid off.

As we look forward to 2026 our "Development Horizon" project board is loaded with cool new features and updates, so be sure to keep an eye out for the exciting new things we have coming!

If you want to download a copy of our report, the latest report is always available and linked in the footer of our site!