-
Notifications
You must be signed in to change notification settings - Fork 20.6k
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
Trusted Types - use setHTML instead of innerHTML internally #5094
Comments
@koto What do you think? |
@kkmuffme thanks for the ideas. That said, |
Thanks for your feedback. I know it's a bit too early, it's more like something I wanted to put on the roadmap. Specifically, setHtml will be a non-flag feature in Chromium (Chrome, Edge, Chrome for Android) in 2 weeks and in Firefox until the end of the year (at latest), which means 75% of the browser market has support for this until then. |
That looks like good timing to have it considered then, thanks. I’m on PTO now but I’ll have a closer look when I’m back. |
Sorry didn't read all and don't know how that fit in the discussion but since the sanitizer MVP API will be enabled by default in 8 days, maybe Jquery could leverage it, in addition to trusted types |
The feature is relatively new indeed; The advantages are already listed out in the bug, they are all true. For completeness sake however I'd like to point out potential disadvantages to consider:
All that aside, I think switching the default to be secure against XSS would be a tremendous change. For You probably want to include config hooks that let one specify SanitizerConfig or a Sanitizer in case some configs need to be tweaked (e.g. you don't want forms in your application). |
Thanks for the input, @koto. The possibility of the sanitizer adding more elements to the blocklist sounds risky: it may break some apps and browsers try to avoid breaking changes. I think the concerns presented & especially cross-browser issues will make this a 5.0.0 item. For context, we’re removing what we can from 4.0.0 pending issues & will plan a release within a few months. 4.x will still support IE 11 but 5.0.0 will drop all IE versions. If by the time 5.0.0 comes out Safari implements the API, at least we’d have cross-browser issues resolved. |
I see the value in skipping sanitization of trusted types however if current jquery doesn't sanitize at all, unconditionally, changing the policy to sanitize by default is a performance regression risk right? |
@LifeIsStrange You could benchmark this, but I'd suspect the cost is mostly in the HTML parser, which the browser runs anyways whenever |
The sanitize API is far beyond this stage (DomPurify which is the base for this exists since 8 years) that there are things added willynilly. Currently the Sanitize API is only updated to handle additional XSS vectors within what Sanitize does already (e.g. Firefox just had a security issue fixed very recently). While it's true, that the sanitize API might remove additional insecure things in the future, these would be probably very limited use cases (since all major things are in it already now). Additionally, the things are removed only because of security concerns - which is a good thing (and if they break something: it's good they broke, since your js is probably unsafe)
You are right. However, security always comes at a cost. Here's a basic benchmark (please run in Chrome v105, I tested on Firefox only and there benchmarks are off due to security reasons/limits of performance.now, so this is just a rough estimate when done in Firefox) Shows that innerHTML is about 10 times faster than setHTML() in Firefox. Looks like much, right? As koto correctly states:
|
A 10x slowdown is not insignificant. Remember the jQuery overhead would apply on top of this additional one. I imagine hooking into the sanitizer should be optional anyway, though, so the default behavior would be still fast. |
I think worrying about performance at the current time isn't really relevant - the native sanitizers will get faster over the next few months (the test I ran is not representative, as Firefox's performance tools are limited for security reasons. Somebody would need to run the above benchmark on Chrome 105 to get a reliable number) The current implementation (TT-agnostic with prior sanitizing) is a 35x slowdown. I'd say once Chrome 105 is released, we could create a PR for this simple change. Then run synthetic and real world benchmarks to see what the impact on performance really is - and then go from there. |
Just some thoughs on how to improve performance too in case it turns out this really is an issue:
|
I am not sure how It wouldn't be secure for
I don't see a need for a passthrough policy inside jQuery, could you elaborate? More specifically, I don't know how Trusted Types would be able to workaround a performance bottleneck of a native sanitizer, if there is one. If you have a dynamic data that needs sanitizing, use a sanitizer. If you have a dynamic data that doesn't need sanitizing, use DOM |
I totally agree with you - my comment was in regards on the impact on performance (and I was wrong about .text() of course), as LifeIsStrange said this might be a problem for some people.
Exactly, however this is not what the average jQuery codebase looks like nor what tons of developers do. |
Lines 398 to 400 in 016872f
Lines 304 to 311 in 016872f
Line 109 in 016872f
jquery/src/manipulation/buildFragment.js Lines 26 to 28 in 016872f
Currently the jquery/src/manipulation/buildFragment.js Line 48 in 016872f
So, if I understand correctly, we'd just have to make sure that any PR that introduces |
Yes. Additionally: anything that is from user input (non-static string?) must be sanitized before using even when used with a non-innerHTML method. e.g. |
@koto @mgol with Chrome 105 (first Chrome with official support for setHTML) the benchmark already is twice as fast as the benchmark from a few weeks ago with Firefox: |
@koto @mgol Chrome 107 setHTML is only about 4 times slower than no sanitizing, however at 45k/ops/sec (compared to 185k/ops/sec without setHTML) it's so fast, there won't be any feelable (measurable?) difference for the user. I guess by the time this feature is implemented in jQuery, the performance aspect can be completely ignored already, as it's irrelevant by then, since the difference is small. |
Chrome since version 119 temporarily removed the HTML Sanitizer API: The reason for the removal is that the specification is incomplete, which has changed significantly since the addition of Sanitizer to Chrome. Once the specification is ready, the API will be returned. |
Btw wrt to discussions about using setHTML or innerHTML depending on if the argument is already trusted. It would be better to use setHTMLUnsafe instead of innerHTML because the parsing between setHTML and setHTMLUnsafe will be more similar (thinking stuff like declarative shadow dom). |
Followup to #4409 and it's PR #4927 which were more like for handling TrustedTypes (TT-agnostic).
With Chrome shipping this API soon (v105 https://chromestatus.com/feature/5786893650231296, currently v104) and given the exploits with ".sanitize" (in DomPurify, but also in https://developer.mozilla.org/en-US/docs/Web/API/Sanitizer/sanitize, see https://web.dev/sanitizer/#api-surface), the TT-agnostic approach currently taken can be insufficient/insecure.
Suggested change:
Instead of using
.innerHTML =
, jQuery should use.setHTML( ... )
when available (https)/supported by the browserClosing thoughts
The current way Trusted Types are handled in jQuery is potentially insecure and still allows for XSS attacks - running afoul of the purpose of Trusted Types.
Furthermore, this minor change, will help make the web safer for users and life easier for developers.
The text was updated successfully, but these errors were encountered: