- Scope of this document
- Use UTF-8 and specify this in a
meta
tag - Add
rel="noopener"
to outbound links in new windows - Use Subresource Integrity
- Use cautious form defaults
- Use the
sandbox
attribute foriframe
s - Use the
type
attribute forobject
s
You should be aware of the security implications of your frontend code. This document outlines some steps you can take to reduce the risk of markup exposing us, or our users, to security flaws.
Note: this document does not cover JavaScript or interactions which may touch the server, e.g. XSS or HTTP headers. (It is assumed security-related headers are set in the HTTP headers, not in meta
tag equivalents.)
Additionally, you're encouraged to familiarise yourself with the OWASP HTML5 Security Cheat Sheet.
Do this: <meta charset="utf-8">
.
Not specifying the appropriate character set has historically been a source of charset-related security problems as well as a source of rendering bugs.
Ideally this should be specified in both the HTTP header and a meta
element, in case one breaks.
Links leak the context of the opening window to the opened window, via the window.opener
object. This allows the opened window to alter the opening window via JavaScript, regardless of whether the window is opened via JavaScript or simply a target="_blank"
attribute. Exploiting this context leaking is a security problem.
As of 2020, recent versions of all major browsers treat target=_blank
links as noopener
by default. However, we still recommend including this attribute because as an HTML feature it will be served to older browsers, and is cheap to implement.
Often we serve assets such as JavaScript or CSS files via CDN's for performance reasons. But what if that resource is corrupted, either on the CDN or over the network?
Files served via CDN's (or your own asset servers) are high-priority targets for attackers, because they enable attackers to inject payloads into many sites with just one breach.
Subresource Integrity protects against corrupted resource files by instructing the browser to calculate a hash of the downloaded file's content, then compare that hash to one we previously computed for the known-good file. If the hashes don't match, the browser doesn't parse the loaded file.
We tell the browser the hash of the known-good file by embedding the hash value in the HTML—using the integrity
attribute on the corresponding script
or link
element.
This may sound complicated but it's not too hard to do!
Example markup (from the MDN article on Subresource Integrity):
<script src="https://example.com/example-framework.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
(Use of the crossorigin
attribute depends on CORS support of the asset server.)
Subresource Integrity can be used in conjunction with a Content Security Policy but does not require a CSP to work.
As Subresource Integrity should be relatively easy to add to a frontend build tool chain, you should use it for any script
or link
element, especially as browser support for Subresource Integrity is good.
We must not write data that comes from the user into the DOM without first sanitising that data. Input includes not only form data, but also things like user agent strings, request headers, URL parameters and cookies.
Sanitising input is the responsibility of the server in most cases, but we must also sanitise input in rich JavaScript applications (e.g. URL fragments) at the risk of exposing our users to attacks like DOM XSS/Client-Side XSS.
Such sanitisation is beyond the scope of this document, but as authors of HTML forms we can mitigate some classes of client-side attack by specifying some form widget attributes. This is no substitute for sanitisation but constitutes part of a "belt & braces" approach to security, as well as reducing common sources of bugs.
form
— specifyaccept-charset="utf-8"
. Charset issues are a common source of bugs generally. If a page has been parsed as UTF-8 the browser should default to sending data as UTF-8, but it's easy to specify theaccept-charset
as extra protection in case something breaks, so why not do it?input
— if the value of the type attribute istext
,email
,search
,password
,tel
, orurl
, specify themaxlength
attribute.textarea
— specify themaxlength
attribute.input type="file"
— use theaccept
attribute to specify which types of file can be uploaded (specify by extension or MIME type). Again this is no substitute for server validation but improves usability.- for non-essential "autocomplete" functionality, consider using a
datalist
element instead of JavaScript. keygen
is deprecated, don't use it.
Whether the browser autofills input elements is controlled by the autocomplete
attribute, for which there are a large number of potential values.
Firstly, while there are security concerns about autofill & Personally Identifiable Information (PII),
...in-browser password management is generally seen as a net gain for security. Since users do not have to remember passwords that the browser stores for them, they are able to choose stronger passwords than they would otherwise. For this reason, many modern browsers do not support autocomplete="off" for login fields — MDN - Turning off form autocompletion
As such, while you can disable autofill for sensitive fields (see MDN article above) browser vendors feel it is not a good idea overall.
Secondly,
User agents should verify that all fields with the autocomplete attribute are visible within the viewport before automatically entering data. — HTML 5.3 W3C Working Group Note
So to be sure, avoid enabling autocomplete for elements which may be visually-hidden.
Even if you control the content of the iframe
, do use the sandbox
attribute if you can. This attribute restricts the functionality of the framed document (why "sandbox"?).
Note the default sandbox is very strict (e.g. no JavaScript, forms cannot be submitted) so tailor the value of the attribute to your use case. Mike West has written a great article about iframe sandbox
and MDN has a lovely reference article on sandbox
.
Either the data
or type
attribute for the object
element
must be specified. You are encouraged to always ensure type
is specified.
The value should be a valid MIME type, also know as a "content type".
Embedded objects are generally not to be trusted -- security issues have arisen in the past from browsers being "tricked" into treating content as the wrong type, including same-origin policy violations.
For more information please see the W3C HTML Living Standard.