Skip to content

W3C TPAC 2019

Krzysztof Kotowicz edited this page Sep 16, 2019 · 4 revisions

Trusted Types @ TPAC 2019

https://github.com/WICG/trusted-types

  • A browser API to address DOM XSS
  • Produce safe values for the DOM XSS injection sinks via policies. This reduces the number of dangerous sinks to trustedTypes.createPolicy.
  • Guard creation of policies (which policies can the application create?) and the enforcement at sinks via HTTP Response headers
const myPolicy = trustedTypes.createPolicy('my-policy', {
  createHTML: (s) => { /* security sensitive code here, e.g. call mySanitizer */ }
});
document.body.innerHTML = myPolicy.createHTML(location.hash);
// Running mySanitizer…
document.body.innerHTML = location.hash
// TypeError: HTMLBodyElement.innerHTML requires TrustedHTML assignment.
// Dispatches a securitypolicyviolation event.
  • The 'default' policy is called implicitly if a string reaches a sink ("last resort" policy).

Piloting in Google applications

Integration based on Closure Safe Types. Adding Compile-time flag for Google Closure code (impl.).

if (BUILD_FLAG_TT_POLICY_NAME && window.trustedTypes) {
   policy =  trustedTypes.createPolicy(BUILD_FLAG_TT_POLICY_NAME, ...)
}

Instrumenting Closure Safe Types to wrap over Trusted Types. This rolled out for JS code used in applications under pilot. We also added Content-Security-Policy-Report-Only header to an application.

Outcomes

  • "We use (TT-compliant) Safe Types, and have safeguards for that" challenged
  • A lot of violations for TrustedURLs (img.src, a.href, iframe.src)
  • Uncovered badness and anti-patterns
    • Custom script loaders we didn't know about
    • Legacy allowlists for unmaintained code
    • Not all code is compiled at build time (!)
  • Driving wider refactorings to eradicate the badness

Library integrations

We prepared integrations with popular JS libraries. Namely: React, Angular, Vue, lit-html (Polymer), Karma, Jasmine, DOMPurify. Details at https://github.com/WICG/trusted-types/wiki/Integrations. We see emerging patterns in the integrations.

API changes

Integration with CSP

// Content-Security-Policy: trusted-types a b c;

trustedTypes.createPolicy('a', {...rules}) // OK, returns a policy
trustedTypes.createPolicy('d', {...rules}) // CSP violation, throws

This gives us report-only, defined multiple headers behavior, propagation to other documents. There's a new 'trusted-script' keyword in script-src (for eval and javascript: exemptions.

Actionable violation reports

Enough data to debug an issue when migrating to Trusted Types.

{
    "document-uri": "https://foo.example/",
    "violated-directive": "trusted-types",
    // ...
    "blocked-uri": "trusted-types-sink",
    "line-number": 25,
    "column-number": 40,
    "source-file": "http://foo.example/script/",
    "script-sample": "Element.innerHTML <img src=x>"  // Payload trimmed to 40 chars.
}

More data is available in your JS program (debug from within a policy).

Robust report-only mode

We introduced a way for a default policy to reject a value without throwing errors in report-only mode.

trustedTypes.createPolicy('default', {
   createHTML: (s) => s.includes('<') ? null : s
 });
el.innerHTML = 'harmless'; // no violation report.
el.innerHTML = '<bad>';  // send violation report.
// In report-only, allow <bad>. Otherwise, throw a TypeError.

Collisions on policy names possible (same with trusted-types *)

TrustedURL deprecation

We only want to guard navigation to URLs because of javascript: scheme (it's not a navigate-to equivalent. What if instead of requiring types for a.href, we provided a safe by default, programmatic javascript: URL control on navigation?

Example: Under Content-Security-Policy: trusted-types default; javascript: stops working:

a.href = 'javascript:alert(1)';
a.click(); // violation

... But there's also a way to re-enable some payloads:

trustedTypes.createPolicy('default', {
    createScript: s => s === 'void(0)' ? s : null
});

a.href = 'javascript:void(0)';
a.click(); // allowed

For CSP script-src, this also requires 'trusted-script' keyword. Should it also require 'unsafe-inline'?

Granular control over eval()

This solves 'unsafe-eval' dilemma - you don't have to migrate everything off eval. It requires ECMAScript proposal - https://github.com/tc39/proposal-dynamic-code-brand-checks.

Example: under Content-Security-Policy: trusted-types foo:

trustedScript = fooPolicy.createScript('2');
eval(trustedScript) // 2
eval('2')           // Route through default policy

For CSP script-src, this also requires 'trusted-script' keyword. Should it also require 'unsafe-eval'?

Metadata API

Helps existing libraries produce the right type. Might also be a building block for sanitizers.

trustedTypes.getPropertyType('div', 'innerHTML') // 'TrustedHTML'
trustedTypes.getAttributeType('script', 'src')   // 'TrustedScriptURL'
trustedTypes.getPropertyType('img', 'onload')    // 'TrustedScript'
trustedTypes.getAttributeType('img', 'alt')      // null

Potential future developments

  • String literals as trusted values - https://github.com/tc39/proposal-array-is-template-object
    trustedScriptURL`https://this.literal.is.not.an.injection`
    trustedScriptURL`https://but.this.might.be.${dangerous}.and.will.fail`
  • Built-in policies (HTML sanitizer? script-src whitelist?)
  • Per script capabilities
    <script src=jquery.js trusted-type-policy=my-policy-for-jquery>

Summary