Security is more important than ever before. Breaches can happen to anyone, whether it’s a large corporation or a small site. We need to be careful not to expose customer data, and we need to be aware of the many attacks that are possible and the errors that can happen when deploying a website or app.
A topic is often overlooked by frontend developers
Nothing in development happens in isolation — a front end does nothing without a back end, and a back end can’t be used without a front end into it, and on and on it goes. We all work together and washing your hands of things that “aren’t my job” is irresponsible and dangerous.
So yes, the responsibility for securing the user’s data is shared between both backend and frontend. And here’s the quick-n-dirty on the two most common ways we can protect our people:
- 1. Use a strong content security policy
- 2. Enable XSS protection mode and avoid typical mistakes
- Enable XSS protection mode
- Avoid typical XSS mistakes
- 3. Use a modern framework that handles security automatically
- 4. Be more selective with third-party scripts
- What should be aware…
- Tips when integrating a third-party service
- 5. Consider using textContent instead of innerHTML
- 6. Keep your dependencies up to date
- 7. Don’t leak referrer value
1. Use a strong content security policy
Sound content security policy (CSP) is the cornerstone of safety in frontend applications. CSP is a standard that was introduced in browsers to detect and mitigate certain types of code injection attacks, including cross-site scripting (XSS) and clickjacking.
Strong CSP can disable potentially harmful inline code execution and restrict the domains from which external resources are loaded. You can enable CSP by setting
Content-Security-Policy header to a list of semicolon-delimited directives. If your website doesn’t need access to any external resources, a good starting value for the header might look like this:
Content-Security-Policy: default-src 'none'; script-src 'self'; img-src 'self'; style-src 'self'; connect-src 'self';
Here we set
connect-src directives to self to indicate that all scripts, images, stylesheets, and fetch calls respectively should be limited to the same origin that the HTML document is served from. Any other CSP directive not mentioned explicitly will fallback to the value specified by
default-src directive. We set it to
none to indicate that the default behavior is to reject connections to any URL.
However, hardly any web application is self-contained nowadays, so you may want to adjust this header to allow for other trusted domains that you may use, like domains for Google Fonts or AWS S3 buckets for instance, but it’s always better to start with the strictest policy and loosen it later if needed.
2. Enable XSS protection mode and avoid typical mistakes
Enable XSS protection mode
If malicious code does get injected from user input, we can instruct the browser to block the response by supplying
"X-XSS-Protection": "1; mode=block" header.
X-XSS-Protection header to ensure better security for older browsers that don’t support CSP headers.
Avoid typical XSS mistakes
“Let’s say we wanted to address a user by their name by linking to them from a marketing email,” James Hall, director of digital innovation agency Parallax, suggests. “Adding
?name=James to the query string, and then simply adding that to the DOM, would be a quick way to do it.”
document.querySelector('.tagline').innerHTML = nameFromQueryString
James warns that using code like the above, however, means anyone can inject code into your site and take over. He cautions that just by changing the name to
<script src="my.malicious.site">, an attacker could craft a URL that can make a fake payment page look like it’s serving from your SSL-encrypted website.
3. Use a modern framework that handles security automatically
Modern UI frameworks like React, Vue, and Angular have a good level of security built into them, and can largely eliminate the risks of XSS attacks. They automatically encode HTML output, reduce the need for using XSS-susceptible DOM APIs, and give unambiguous and cautionary names to potentially dangerous methods, like
4. Be more selective with third-party scripts
What should be aware…
Third-party services like Google Analytics, Intercom, Mixpanel, and a hundred others can provide a “one line of code” solution to your business needs. At the same time, they can make your website more vulnerable, because if a third-party service gets compromised, then so will be your website. Especial care should be taken when using Google Tag Manager, Segment, or any other tools that allow anyone in your organization to integrate more third-party services.
Tips when integrating a third-party service
- Make sure to set the strongest CSP policy that would still permit that service to work normally. Most of the popular services have documented what CSP directives they require, so make sure to follow their guidelines.
- Make sure to include
integrityattribute when possible. Browsers have the Subresource Integrity feature that can validate the cryptographic hash of the script that you’re loading and make sure that it hasn’t been tampered with.
This how your
script tag may look like:
5. Consider using
textContent instead of
To prevent XSS attacks, you can use a sanitization library like DOMPurify (see below, under 11), but front end consultant Zell Liew suggests that, if you’re changing text only, you can use
textContent instead of
“Let’s say you want to get a text value from an input field, and you want to output that text value into the DOM. Here’s the code to get the text value:
const value = input.value
But the user can try to enter something malicious, like this snippet:
<img src="x" onerror=alert("HACKED!")>
If you use
innerHTML, you’ll create the
<img> element and run the
onerror handler. This is where XSS begins.” Using
textContent instead, as it can only output text and doesn’t generate any HTML.
6. Keep your dependencies up to date
A quick look inside
node_modules folder will confirm that our web applications are lego puzzles built out of hundreds (if not thousands) dependencies. Ensuring that these dependencies don’t contain any known security vulnerabilities is very important for the overall security of your website.
The best way to make sure that dependencies stay secure and up-to-date is to make vulnerability by checking a part of the development process. To do that, you can integrate tools like Dependabot and Snyk, which will create pull requests for out-of-date or potentially vulnerable dependencies and help you apply fixes sooner.
7. Don’t leak referrer value
Finally, be really mindful of the data you’re exposing to the front end in your code.
Limit access to browser features & APIs
Part of good security practice is restricting access to anything that is not needed for the proper use of our website.
When you click on a link that navigates away from your website, the destination website will receive the URL of the last location on your website in a
referrer header. That URL may contain sensitive and semi-sensitive data (like session tokens and user IDs), which should never be exposed.
To prevent leaking of
referrer value, we set
Referrer-Policy header to
This value should be good in most cases, but if your application logic requires you to preserve referrer in some cases, check out this article to know the way we should break down all possible header values and when to apply them.
There are many variations of UI-first attacks that malicious users can take advantage of, but you can greatly increase your chances of defending against them if you follow the recommendations given in this article.