Secure Web Application Guidelines (SWAG) provide recommendations on how to develop more secure web applications. SWAG are best practices you can follow to reduce the risk of introducing security vulnerabilities into your web application, or help you respond to vulnerabilities. In this document, we collect web platform features that you can implement in a web application and practices you can follow to help mitigate or respond to various attacks.
The SWAG document is developed in context of the Best Practices for OSS Developers Working Group which is dedicated to raising awareness and education of secure code best practices for open source developers. SWAG presents security guidelines specific to web developers.
## Audiences for This Document {#audience}
The primary audience for this document are web developers.
## Recommended Security Features
This section recommends web platform features you can implement in a web application to help mitigate or respond to various attacks.
### Use HTTPS
Ensure your web application uses [HTTPS](https://developer.mozilla.org/en-US/docs/Glossary/HTTPS) (HTTP over [TLS](https://developer.mozilla.org/en-US/docs/Glossary/TLS)) for all its pages and other resources that it loads. Implementing HTTPS is crucial for securing data in transit. It protects against eavesdropping and man-in-the-middle attacks.
To support HTTPS a web application needs a TLS certificate. [Let's Encrypt](https://letsencrypt.org/) is a widely used nonprofit Certification Authority which issues free TLS certificates.
Not all TLS configurations are equally secure: if you need to configure your own server, consult a resource such as Mozilla's [TLS Recommended Configurations](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations).
Modern web hosting services support HTTPS for you, either by default or through a configuration setting. In this situation, the hosting service is likely to manage your certificate and configure the server on your behalf.
You should serve all pages over HTTPS, not just pages that you consider especially sensitive.
When a page loads resources (scripts, stylesheets, fonts, images, and so on), these resources should also be served over HTTPS. If a page is loaded over HTTPS and it attempts to load resources over HTTP, then the browser will either try to upgrade the load request to use HTTPS or will block the request: this is called [mixed content blocking](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content).
If it's not possible for you to update your code to load resources from HTTPS URLs (for example, because your HTML has been archived) you can include a [content security policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) that contains the [`upgrade-insecure-requests`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#upgrading_insecure_requests) directive, and the browser will automatically upgrade these requests to HTTPS.
To support clients which request pages over HTTP, you can listen for HTTP requests and use a [301 Moved Permanently](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301) response to redirect to the HTTPS version. If you do this, then you should also send the [HTTP `Strict-Transport-Security`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) (HSTS) response header: this informs clients that you wish them to use HTTPS, and will cause the browser to connect using HTTPS directly for any subsequent visits, even those made using HTTP URLs.
#### Learn more
- [Let's Encrypt](https://letsencrypt.org/)
- [TLS Recommended Configurations](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations) (Mozilla)
- [Transport Layer Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html) (OWASP)
- [HTTP Strict Transport Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html) (OWASP)
- [Strict-Transport-Security](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) (MDN)
### Implement a strict Content Security Policy (CSP)
A CSP helps mitigate [cross-site scripting (XSS)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS) and other code injection attacks by specifying which resources a document is allowed to load. A CSP can also help control whether a page can be embedded in another page, thus protecting against [clickjacking](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking), and can help ensure that all resources are loaded over HTTPS.
- To mitigate against XSS, we recommend that you implement a [strict CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#strict_csp), which uses a [nonce](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#nonces) or a [hash](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#hashes) to indicate to the browser which scripts it expects to see in the document. However, any CSP that prevents uncontrolled execution of inline scripts is much better than none.
- To enforce the use of trusted types, set the [`require-trusted-types-for`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for) directive. See the [Sanitize input before including it in pages as HTML](#sanitize-input-before-including-it-in-pages-as-html) guideline.
- To defend against [clickjacking](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking), set the [`frame-ancestors`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) directive.
- To help ensure that all resources are loaded over HTTPS, set the [`upgrade-insecure-requests`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests) directive. See the [Use HTTPS](#use-https) guideline.
#### Learn more
- [Content Security Policy guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (MDN)
- [Content Security Policy Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html) (OWASP)
- [Strict CSP guide](https://web.dev/articles/strict-csp) (web.dev)
### Set the SameSite attribute on sensitive cookies
The [`SameSite`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value) cookie attribute is a defense in depth against a variety of attacks, including [clickjacking](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking), [CSRF](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF) and various [cross-site leaks](https://xsleaks.dev/). It takes one of three values: `Strict`, `Lax`, or `None`.
The strictest value is `Strict`, which prevents the cookie from being included in any cross-site requests: that is, any requests which originate from a different [site](https://developer.mozilla.org/en-US/docs/Glossary/Site) from the site that set the cookie.
However, this will prevent the cookie being included in some cross-site requests for which you might want it. For example, if the user is logged into your site, and the user then clicks a link to your site from a different site, you probably want to show the logged-in version of your page, and for this to happen the request must include the user's cookie.
The `Lax` value is intended to allow for this use case, but offers correspondingly weaker protection against cross-site attacks.
As a general guide, then, you should try to use `Strict` for some cookies and `Lax` for others:
- `Lax` for cookies that you will use to decide if a logged-in user should be shown a page
- `Strict` for cookies that you will use to authorize requests that change the server's state, such as transferring money or changing the user's settings.
Note that `Lax` is the default value in some but not all browsers, and in those browsers, the implementation of `Lax` when it is the default is more permissive than the normal implementation of `Lax`. This means that you should actively set `Lax`, and not rely on it being the default.
#### Learn more
- [`SameSite`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value) (MDN)
- [SameSite cookies explained](https://web.dev/articles/samesite-cookies-explained) (web.dev)
- [Bypassing SameSite cookie restrictions](https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions) (PortSwigger)
### Use Fetch metadata
Many attacks, including [CSRF](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF) and some [cross-site leaks](https://xsleaks.dev/), depend on an attacker's site making an HTTP request to the target site: that is, making a cross-site request.
Fetch metadata headers are a collection of HTTP request headers which provide information about the context of an HTTP request. Fetch metadata headers include:
- [`Sec-Fetch-Site`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site): Whether the request is same-origin, same-site, or cross-site.
- [`Sec-Fetch-Mode`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Mode): The request's [`mode`](https://developer.mozilla.org/en-US/docs/Web/API/Request/mode).
- [`Sec-Fetch-User`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-User): Whether the request is a user-initiated navigation.
- [`Sec-Fetch-Dest`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Dest): The request's [`destination`](https://developer.mozilla.org/en-US/docs/Web/API/Request/destination).
When handling a request, a server can examine these headers, and use them to reject certain cross-site requests, and so defend against the corresponding attacks.
To protect against CSRF attacks, you should understand where your site is implementing HTTP requests that change the server's state in a significant way (for example, transferring the user's money), and should consider using Fetch metadata headers to prevent these requests from being made cross-site.
To protect against certain cross-site leaks, you should decide whether you need other sites to be able to load your site's resources, and either block cross-site loads entirely or only allow them for allowlisted sites. This is called a _Resource Isolation Policy_.
#### Learn more
- [Defend against CSRF using Fetch metadata](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF#fetch_metadata) (MDN)
- [Protect your resources from web attacks with Fetch Metadata](https://web.dev/articles/fetch-metadata) (web.dev)
- [Isolation Policies](https://xsleaks.dev/docs/defenses/isolation-policies/) (xsleaks.dev)
### Sanitize input before including it in pages as HTML
Before interpolating input as HTML, always [sanitize](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS#sanitization) it, to defend against cross-site scripting (XSS) attacks.
To do this, adopt one or both of the following strategies:
- Use the [Sanitizer API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API) if it is available, choosing APIs such as [`Element.setHTML()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML) that automatically sanitize their input.
- When using [injection sinks](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API#injection_sink_interfaces) such as [`Element.innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML), use the [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) along with the [`require-trusted-types-for`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive, to ensure that input is being passed through a sanitization function.
#### Learn more
- [Cross-site scripting (XSS)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS) (MDN)
- [HTML Sanitizer API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API) (MDN)
- [Trusted Types guide](https://web.dev/articles/trusted-types) (web.dev)
- [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) (MDN)
### Encode input before including it in pages as text
Before interpolating input as text, use [output encoding](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS#output_encoding) to ensure that the input will be treated as text rather than as a language such as HTML. This is a defense against cross-site scripting (XSS) attacks.
- When interpolating input into a page as text, either in the browser or in the server, use a templating engine that performs output encoding.
- Be aware of the [context](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS#document_contexts) in which you are interpolating input, and ensure that the appropriate output encoding will be performed in that context.
#### Learn more
- [Cross-site scripting (XSS)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS) (MDN)
- [Output encoding](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding) (OWASP)
### Use prepared SQL statements with parameterized queries
If your web app uses a database and can execute SQL commands on it that are parameterized by user input, always use [prepared SQL statements with parameterized queries](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html). This is a defense against SQL injection attacks, by ensuring that the user input is treated as data rather than SQL commands.
#### Learn more
- [SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html) (OWASP)
### Implement restrictions on framing
Controlling whether your site can be embedded in another site using an [`