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

Override .innerHTML, or define safelySetInnerHTML(...)? #11

Closed
mikewest opened this issue Sep 26, 2017 · 11 comments
Closed

Override .innerHTML, or define safelySetInnerHTML(...)? #11

mikewest opened this issue Sep 26, 2017 · 11 comments
Labels

Comments

@mikewest
Copy link
Member

It might be difficult to audit sink assignments (e.g. el.innerHTML = whatever;), as bad usage looks exactly like good usage. The auditor needs to have knowledge of the context ("Is the restrictive flag set?") in order to know whether or not a given usage is safe.

Perhaps introducing a safe variant of the sink would be better: el.safelySetInnerHTML(whatever) or something similar.

@mozfreddyb
Copy link
Collaborator

What's the goal here auditing sinks or sources?
How important is it to make this work seamlessly for existing, secure applications?

There are two roads I can envision, the first is to expose a context-sensitive escaper built upon streaming HTML that allows something like

safehtml`<a href="${url}">${text}</a>`

The other is to globally overwrite innerHTML with a thing that silently(?) sanitizes the input and removes all active code, given some sane defaults (yep, this is the hard part).
The opt-out would be unsafelyAssignInnerHTML() that will reduce the amounts of code to be audited.

@koto
Copy link
Member

koto commented Sep 28, 2017

Ideally, we should be able to audit sources, because that one scales better. However, refactoring existing applications will probably lead to quickly replacing any .innerHTML assignment with the types-based equivalent.

I'm fine if:

  1. innerHTML accepts a TrustedHTML always,
  2. There's a variant that escapes (or otherwise silently sanitizes using a hard-to-agree-on mechanism),

I'd rather not expose the unsafe variant of sink setters, as it promotes bad practice of putting validation close to the sink. We can't prevent developers from using .innerHTML = TrustedHTML.unsafelyCreate('foo'), but we can discourage other patterns like .unsafelySetInnerHTML('foo') by simply not adding them, and only exposing safe ones.

@koto
Copy link
Member

koto commented Sep 28, 2017

But back to the original question though; Disabling innerHTML setter completely in the enforcement mode might be problematic because of templating frameworks that bind to element properties.

@mikesamuel
Copy link
Collaborator

Can we treat the below as separable questions?

  • Should a FeaturePolicy flag opt-in cause innerHTML to only accept TrustedHTML
  • Should a FeaturePolicy flag opt-out of innerHTML being present
  • Should HTMLElement be extended with a safeInnerHTML that only accepts TrustedHTML

@koto
Copy link
Member

koto commented Feb 15, 2018

We can, yes. But we might end up creating an overtly complex system (even when considering this single sink) I'd try to avoid. Should we need the answer to all of the above to be 'yes'?

My personal answers would be

Should a FeaturePolicy flag opt-in cause innerHTML to only accept TrustedHTML

yes

Should a FeaturePolicy flag opt-out of innerHTML being present

no, this will likely be an adoption killer and there are other ways of prohibiting innerHTML usage (polyfill, linter, ...)

Should HTMLElement be extended with a safeInnerHTML that only accepts TrustedHTML

maybe.

@mozfreddyb
Copy link
Collaborator

You could replace "innerHTML setter" with the fragment parsing algorithm.

This will magically catch innerHTML, outerHTML, insertAdjacentHTML, DOMParser, createContextualFragment.

@koto
Copy link
Member

koto commented Feb 19, 2018

You could replace "innerHTML setter" with the fragment parsing algorithm.

Is it possible to hook into that algorithm from user land JS code? If not, the polyfill would need to override all of its callsites separately.

@mikesamuel
Copy link
Collaborator

@koto, I think <template> parses its body as a fragment, so

const myTemplate = document.createElement('template')
myTemplate.innerHTML = htmlParse
myTemplate.content  // A document fragment that won't have loaded images or executed scripts

https://html.spec.whatwg.org/multipage/scripting.html#dom-template-content

@mozfreddyb
Copy link
Collaborator

mozfreddyb commented Mar 5, 2018

What @mikesamuel said, and you'd have to overwrite the HTMLElement's prototype.

@koto
Copy link
Member

koto commented Jun 4, 2018

Take a look at the new design, with policies: https://github.com/WICG/trusted-types#policies. With it, we're trying to convince the developers there's a better way, and they should focus on the sources. Direct sink usage with a string is still allowed, but can go through a defined policy. 3e47991

@koto
Copy link
Member

koto commented Sep 13, 2018

I think the current approach is to reuse the original sink names, with optionally the nw contract (i.e. they accept types). Closing this one then.

@koto koto closed this as completed Sep 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants