Security in Gatsby
By taking advantage of the benefits of static content, Gatsby inherits several security principles. However, even with this, your users can be exposed to malicious attacks. Read on for further information on potential attack vectors and how you could prevent them.
Cross-Site Scripting (XSS)
Cross-Site Scripting is a type of attack that injects a script or an unexpected link to another site into the client side of the application.
JSX elements automatically escape HTML tags by design. See the following example:
When you try to inject the JSX element above, it will render hello <script src='https://path/to/badness.js'></script>
as a string inside the p
tag.
On the other hand, fields in your application may need to render inner HTML tags, such as a content field in a blog or a comment in a comments section, that are built into rich-text editors.
In order to render those HTML tags you need to use an HTML parser (e.g. html-react-parser) or the dangerouslySetInnerHTML
prop, like so:
That is when you expose your application to XSS attacks.
How can you prevent cross-site scripting?
The most straightforward way to prevent a XSS attack is to sanitize the innerHTML string before dangerously setting it. Fortunately, there are npm packages that can accomplish this; packages like sanitize-html and DOMPurify.
Cross-Site Request Forgery (CSRF)
Cross-Site request forgery is a type of exploit that deceives the browser into executing unauthorized actions. By default, in any request made, the browser automatically appends any stored cookies of the destination domain. Combining this with a crafted request, a malicious website can read and write data without the user’s action or knowledge.
For example, assume that the comments in your blog are sent in a form similar to this one:
A malicious website could inspect your site and copy this snippet to theirs. If the user is logged in, the associated cookies are sent with the form and the server cannot distinguish the origin of it. Even worse, the form could be sent when the page loads with information you don’t control:
How can you prevent cross-site request forgery?
Don’t use GET requests to modify data
Actions that do not read data should be handled in a POST request. In the example above, if the /blog/addcomment
endpoint accepts a GET request, the CSRF attack can be done using an <img />
tag:
CSRF Tokens
If you want to protect a page your server will provide an encrypted, hard to guess token. That token is tied to a user’s session and must be included in every POST request. See the following example:
When the form is sent, the server will compare the token received with the stored token and block the action if they are not the same. Make sure that malicious websites don’t have access to the CSRF token by using HTTP Access Control.
Same-Site Cookies Attribute
This cookie attribute is targeted to prevent CSRF attacks. If you need to create a cookie in your application, make sure to protect them by using this attribute, that could be of Strict
or Lax
type:
Using the SameSite
attribute allows the server to make sure that the cookies are not being sent by a cross-site domain request.
Check out MDN Docs for more information on configuring a cookie. You will also want to note current browser support which is available on the Can I Use page.
This cookie attribute is not a replacement for a CSRF Token (and vice-versa). They can work together as security layers in your website. Otherwise, a Cross-Site Scripting attack can be used to defeat these CSRF mitigation techniques. Check out OWASP CSRF prevention cheat sheet for more information.
Third-party Scripts
Some third-party scripts like Google Tag Manager give you the ability to add arbitrary JavaScript to your site. This helps integrate third-party tools but can be misused to inject malicious code. To avoid this, be sure to control access to these services.
Check Your Dependencies
In your Gatsby project, you are going to have some dependencies that get stored in node_modules/
. Therefore, it is important to check if any of them, or their dependencies, have security vulnerabilities.
Using npm
In npm, you can use the npm audit
command to check your dependencies. This command is available in all npm versions greater than 6.0.0
. Check npm docs for more options.
Using yarn
Similar to npm, you can use the yarn audit
command. It is available starting with version 1.12.0
though it is not yet available in version 2. Check the yarn docs for more options.
Key Security
Gatsby allows you to fetch data from various APIs and those APIs often require a key to access them. These keys should be stored in your build environment using Environment Variables. See the following example for fetching data from GitHub with an Authorization Header:
Storing keys in client-side
Sometimes in your Gatsby website, you will need display sensitive data or handle authenticated routes (e.g. a page that shows a user’s orders in your ecommerce). Gatsby has an Authentication Tutorial if you need assistance with setting up authentication flow. Use cookies to store the credentials client-side, preferably with the SameSite
attribute listed above. Check out MDN Docs to further understand these attributes and how to configure them.
Content Security Policy (CSP)
Content Security Policy is a security layer added in web applications to detect and prevent attacks, e.g. the XSS attack mentioned above.
To add it to your Gatsby website, add gatsby-plugin-csp to your gatsby-config.js
with the desired configuration. Note that
currently there is a compatibility issue between gatsby-plugin-csp and other plugins that generate hashes in inline styles, including gatsby-image.
Note that not all browsers support CSP, check can-i-use for more information.
Other Resources
- Security for Modern Web Frameworks
- Docs React: DOM Elements
- OWASP XSS filter evasion cheatsheet
- OWASP CSRF prevention cheat sheet
- Warn for JavaScript: URLs in DOM sinks #15047
- How to prevent XSS attacks when using
dangerouslySetInnerHTML
in React - Exploiting XSS via Markdown
- Auditing package dependencies for security vulnerabilities
- CSRF tokens
- SameSite cookies explained