Stop the cross-site script kiddies from pwning you.
How to keep your codebase safe against stray "<script>alert("Hello")</script>" snippets.
In this essay, I will talk about Cross-Site Scripting (XSS), the bane of my existence in 2021.
So you know how you take various forms of input from your user on your website? It might be a cute cat photo, a funny but slightly cringe tweet or a bad joke written by a friend but you can’t say it’s bad because they’re a friend and it would hurt their feelings. Turns out hackers try to put in all kinds of weird stuff in those fields. The most common thing they try is HTML or Javascript.
So if you render any user-input on your website without properly cleaning it up, the browser of some bumbling user of yours will think, “well, this looks like valid code, I’ma run this!”, making some hacker out there really happy as they make your site alert out “hello world” via Javascript without your permission or knowledge. Of course, the next thing that the hacker will try is to get your users’ passwords (spoiler: it’s probably all “password123”) or worse, get them to buy a NFT. That’s cross-site scripting.
Your users are mad at you now because it was your site that made them buy a JPEG of a monkey for millions of dollars. The world is burning, you have to take your site down, your reputation is in absolute shambles, you’re getting fired, your RSUs are worth 10% of what they were worth yesterday.
You could have avoided this, it didn’t have to be this way. But how?
Let me guide you through the amazing world of XSS controls.
The first thing you want to do is make sure you use a framework with safe defaults. As we all know, developers are lazy plagiarists. If you leave the average developer alone for a few hours with a Jira ticket to work on, they will have cargo-culted 500 lines of code 99% of the time. You want your developers to cargo-cult safe code, not unsafe code. The easiest thing to do here is to just use React. Just do it, don’t be weird. No Vue, no Angular, just use React like a normal person.
The second thing you want to make sure is that developers know that using any method that starts with “dangerously” is not a good idea. This might seem obvious on the outset, but to the unique species of a developer trying to get some code to work before a deadline, it’s not. You can go full dictator here and ask that you review every change that calls dangerous methods like “dangerouslySetInnerHTML”. The only thing that can make developers stop using dangerous methods is another developer or a CI build telling them not to use it. Just make sure that you mark any dangerous patterns you find as dangerous. It’s probably also worth looking over any existing dangerous patterns in the codebase (so that you can partner with a friend to get that sweet bug bounty money, just kidding. Unless…?? Nah, just kidding).
The third and final thing you want to do is to accept that all of the above will fail. Someone will find some weird-ass edge case where something got through. You CANNOT stop this if your codebase is larger than a few hundred files. So, you need a fallback. The fallback you need is a secure Content Security Policy (CSP). Content Security Policies tell the browser what code is ok to execute and what isn’t. Try to be as strict here as possible, this will save you on multiple occasions. And remember, if you allow `unsafe-inline` in your CSP, you might as well just not have CSP.
This should protect you against 99% of XSS attacks, for the rest, reach out to me on Twitter so that I can ghost you after spending 5 hours on the problem and still not solving it.
I like this way story telling