trusted-types icon indicating copy to clipboard operation
trusted-types copied to clipboard

Can we conditionally enforce Trusted Types based on document response type in XHR?

Open shhnjk opened this issue 2 years ago • 5 comments

After publishing the XHR vector, there was feedback asking to enforce Trusted Types on XHR document response.

Is it possible to change XMLHttpRequest (i.e. XHR) to the following?

  1. Change XHR.open to accept string or TrustedScriptURL in the url parameter
  2. When/if XHR.responseType is set to document, Trusted Types will check if the XHR.open's url parameter was string or TrustedScriptURL. If it was string, this will throw TT violation.

WDYT?

shhnjk avatar Feb 15 '22 20:02 shhnjk

I've used Jun's example to show how a developer might update their XMLHttpRequest to stop using .innerHTML (correctly rejected by TrustedTypes), and then naively update it to use responseType = "document", and .appendChild().

craigfrancis avatar Feb 15 '22 20:02 craigfrancis

Even without responseType set to document, you can still bypass Trusted Types with XHR, by abusing responseXML:

<!DOCTYPE html>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types 'none';">
<script>
  const xhr = new XMLHttpRequest();
  xhr.open("get", "data:text/xml,<x xmlns='http://www.w3.org/1999/xhtml'><img src='' onerror='alert(1)' /></x>")
  xhr.send();
  xhr.onload = ev => {
    document.body.append(xhr.responseXML.firstChild);
  }
</script>

securityMB avatar Apr 08 '22 08:04 securityMB

Both these vectors require fetching new (same-origin, local scheme) documents and inserting nodes from there. I suspect they would be less probable to legitimately (authors are not malicious in TT threat model) exist in code than navigation-based vectors (e.g. create a same-origin iframe, clone nodes off that).

We already explicitly call out https://w3c.github.io/trusted-types/dist/spec/#cross-document-vectors as remaining security considerations, and the complimentary security controls mentioned there (e.g. script-src CSP) would also work for these cases. I wonder whether the best course of action would not be to recognize such vectors in security considerations.

The alternative that follows the TT approach would be to require TrustedDocumentURL or equivalent in fetch-related functions, which seems quite unergonomic for the authors for no good enough benefit. Fetch is probably as popular as navigations, and we made the error in the past of trying to control navigation sinks via TT, and had to backtrack with TrustedURL deprecation.

For authors that pass dynamic values to XHMLHttpRequest.open and then copy the resulting nodes to the main document CSP (either connect-src or script-src) seems like a much more practical advice than pure TT and rewriting all their fetch API usage.

koto avatar Jan 19 '24 12:01 koto

Aren't we also considering blocking XSLT? That's also cross-document, no? We should at least be consistent.

And blocking XHR doesn't seem like the end of the world. fetch() doesn't have a bulit-in parser for HTML/XML and won't get one.

annevk avatar Jan 19 '24 12:01 annevk

Aren't we also considering blocking XSLT? That's also cross-document, no? We should at least be consistent.

Vectors from https://github.com/w3c/trusted-types/issues/359 would also be subject to CSP (it's unsafe-inline, essentially), so there's always a trivial way of consistently saying "it's a cross-document vector" and not changing the sinks. I don't think we should just default to that without considering alternatives.

What might be the source of inconsistency is the way we handle the sinks for different known bypass vectors that deal with document objects.

From the ones we know about:

  1. XSLTProcessor.importStylesheet allows for bypassing parser entry points,
  2. XHR vectors combine fetching and parsing,
  3. iframes etc. use same-origin windows,
  4. (anything else)?

We definitely cannot address 3. with TT being realm-scoped. I think we might explore blocking of XSLT functionality to stop https://github.com/w3c/trusted-types/issues/359 and https://github.com/w3c/trusted-types/issues/358, the challenge is to find the best entry point, and whether we need a "Level 2 TT" for that, since there are websites enforcing TT we should avoid breaking by enforcing on new sinks or blocking functionality.

blocking XHR doesn't seem like the end of the world.

I worry it might be quite taxing, at least for the current codebases. There probably is some telemetry, but quickly grepping GitHub gives 6.7M instances of innerHTML assignment and 2.4M of XHR usage. Needing to refactor such a common sink for little visible benefit is a a huge tax on developers adopting a security feature.

What might be more practical is blocking document responseType and responseXML, but given one can also take nodes off data: iframe, I question the actual utility we'd be making with that.

koto avatar Jan 19 '24 13:01 koto