-
Notifications
You must be signed in to change notification settings - Fork 22.8k
TrustedTypes: Add boilerplate for the xxxHtmlUnsafe() methods #40420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Preview URLs
(comment last updated: 2025-07-18 03:04:48) |
> [!WARNING] | ||
> This API parses its input as HTML, writing the result into the DOM. | ||
> APIs like this are known as [injection sinks](/en-US/docs/Web/API/Trusted_Types_API#concepts_and_usage), and are potentially a vector for [cross-site-scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS) attacks, if the input originally came from an attacker. | ||
> | ||
> For this reason it's much safer to pass only {{domxref("TrustedHTML")}} objects into this method, and to [enforce](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) this using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive. | ||
> This means you can be sure that the input has been passed through a transformation function, which has the chance to [sanitize](/en-US/docs/Web/Security/Attacks/XSS#sanitization) the input to remove potentially dangerous markup, such as {{htmlelement("script")}} elements and event handler attributes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking maybe for (all) these cases we modify the boilerplate like this.
> [!WARNING] | |
> This API parses its input as HTML, writing the result into the DOM. | |
> APIs like this are known as [injection sinks](/en-US/docs/Web/API/Trusted_Types_API#concepts_and_usage), and are potentially a vector for [cross-site-scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS) attacks, if the input originally came from an attacker. | |
> | |
> For this reason it's much safer to pass only {{domxref("TrustedHTML")}} objects into this method, and to [enforce](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) this using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive. | |
> This means you can be sure that the input has been passed through a transformation function, which has the chance to [sanitize](/en-US/docs/Web/Security/Attacks/XSS#sanitization) the input to remove potentially dangerous markup, such as {{htmlelement("script")}} elements and event handler attributes. | |
> [!WARNING] | |
> This API parses its input as HTML, writing the result into the DOM. | |
> APIs like this are known as [injection sinks](/en-US/docs/Web/API/Trusted_Types_API#concepts_and_usage), and are potentially a vector for [cross-site-scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS) attacks, if the input originally came from an attacker. | |
> It is better to use XSS-safe methods where possible, such as {{domxref("ShadowRoot.setHTML()")}}. | |
> | |
> You should ensure that this method is called with as restrictive a sanitizer as possible. | |
> If [Trusted Types are enforced](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types), you will need pass {{domxref("TrustedHTML")}} objects into this method. | |
> The policy for creating these types should be a pass-through, as the inputs do not need to be sanitized twice. |
@@ -22,12 +29,12 @@ setHTMLUnsafe(input, options) | |||
### Parameters | |||
|
|||
- `input` | |||
- : A string or {{domxref("TrustedHTML")}} instance defining HTML to be parsed. | |||
- : A {{domxref("TrustedHTML")}} or string instance defining HTML to be parsed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My intent is that for all injection sinks we will put the trusted type as the first option.
@@ -227,6 +235,31 @@ With this approach you can create safe HTML, but you aren't forced to. | |||
|
|||
{{EmbedLiveSample("setHTMLUnsafe() live example","100","350px")}} | |||
|
|||
<!-- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a (hidden) example of what we might show for the htmlunsafe examples.
The point being that you need to do this to avoid the methods throwing exceptions.
What we need to show here is a very restrictive sanitiser that still allows something that is XSS unsafe.
- Obvious thing is a script element, but is there anything else that might be better for an example? It is "the most unsafe" thing, so seems unsubtle to allow it.
- Will modify to use default sanitizer then add an allowed "bad" item, so that we're demonstrating "disallow what you can".
Will also note that if you're using the default sanitizer there is no reason not to use the safe method setHTML.
Note, this is a "call for help". Is it reasonable? Should we be recommending something else? Who can we ask?
@@ -8,6 +8,13 @@ browser-compat: api.Document.parseHTMLUnsafe_static | |||
|
|||
{{APIRef("DOM")}} | |||
|
|||
> [!WARNING] | |||
> This API parses its input as HTML, writing the result into the DOM. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
> This API parses its input as HTML, writing the result into the DOM. | |
> This method parses its input as HTML, writing the result into the DOM. |
> For this reason it's much safer to pass only {{domxref("TrustedHTML")}} objects into this method, and to [enforce](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) this using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive. | ||
> This means you can be sure that the input has been passed through a transformation function, which has the chance to [sanitize](/en-US/docs/Web/Security/Attacks/XSS#sanitization) the input to remove potentially dangerous markup, such as {{htmlelement("script")}} elements and event handler attributes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
> For this reason it's much safer to pass only {{domxref("TrustedHTML")}} objects into this method, and to [enforce](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) this using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive. | |
> This means you can be sure that the input has been passed through a transformation function, which has the chance to [sanitize](/en-US/docs/Web/Security/Attacks/XSS#sanitization) the input to remove potentially dangerous markup, such as {{htmlelement("script")}} elements and event handler attributes. | |
> For this reason it's much safer to pass only {{domxref("TrustedHTML")}} objects into this method, and to [enforce](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) this using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive. | |
> You can reduce the risk by passing {{domxref("TrustedHTML")}} objects into this method instead of string, and [enforcing trusted types](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive. | |
> This ensures that the input is passed through a transformation function, which has the chance to [sanitize](/en-US/docs/Web/Security/Attacks/XSS#sanitization) the input to remove potentially dangerous markup, such as {{htmlelement("script")}} elements and event handler attributes. |
So one thing to keep in mind is that right now no browser ships the sanitizer and even once they do on older browser releases all these functions will very much be XSS sinks. So for that reason I think MDN should be consistent with it's guidance, if the approach is to recommend using trusted types then it should be done here. Regarding the case of once you have the sanitizer API and you use it, it does get interesting. While the sanitizer API is better designed (avoids mXSS issues) it isn't enforcing anything in particular (of course on the safe variants it will). Trusted types allows you to enforce that it goes through an approved policy, which you control the substance of. This is a tricky case where you don't necessarily want to provide a no-op policy because your sanitizer config might be wrong or too lax for your liking. But equally you probably don't want to run it through two sanitizers. I think the general advice still applies though. They should be passing a TrustedHTML argument. It's very context dependent as to what the policy should look like though. I'll raise an issue on the sanitizer API repo to discuss guidance on this point. |
@lukewarlow You make some good points. I've been imagining the release in browsers of sanitizer as imminent, but of course, there is no guarantee that this is true.
Thank you. |
This updates the following methods to explain how they are used with TrustedTypes:
Document.parseHTMLUnsafe()
Element.setHTMLUnsafe()
ShadowRoot.setHTMLUnsafe()
Initially I have just subedited a little and added the (not-quite-right) boilerplate from #37952. This is mostly to create a environment for working out where to go moving forward.
Part of #37518 (tracking issue)
These methods are tricky because the standard boilerplate recommending using trusted HTML is not true, or at least not reasonable to follow:
Warning
This API parses its input as HTML, writing the result into the DOM.
APIs like this are known as injection sinks, and are potentially a vector for cross-site-scripting (XSS) attacks, if the input originally came from an attacker.
For this reason it's much safer to pass only {{domxref("TrustedHTML")}} objects into this method, and to enforce this using the
require-trusted-types-for
CSP directive.This means you can be sure that the input has been passed through a transformation function, which has the chance to sanitize the input to remove potentially dangerous markup, such as {{htmlelement("script")}} elements and event handler attributes.
The reason is that these methods take a sanitizer argument that can inject the input string in a context-aware way. You could use the transform but you would be better off using this sanitizer, and you wouldn't do both. So why do we need TT in this case, and what would it look like.
The simplistic answer is probably that if we're enforcing TTs then we'll need to pass at TT or have a default TT or this will fail. So for this case the simple answer would be "you don't need to do anything special but you should create an unsafeSanitizedHTML policy and use that for all these APIs. In other words it isn't useful.
Is there a way to make it useful. I played with prompting ChatGPT to generate code that achieves the goal of TT, which is to ensure that all code is audited, and that auditing is in a known place. It didn't really use TT for this, but it did come up with the idea of
safeParseHTML()
which wraps the method with something that ensures you always pass the required policy, and ensures that your sanitizer is always one that is "registered/trusted".@wbamberg @lukewarlow Would love your thoughts on how this API should be used with trusted types.