[HN Gopher] Goodbye InnerHTML, Hello SetHTML: Stronger XSS Prote...
___________________________________________________________________
Goodbye InnerHTML, Hello SetHTML: Stronger XSS Protection in
Firefox 148
Author : todsacerdoti
Score : 319 points
Date : 2026-02-24 13:04 UTC (9 hours ago)
(HTM) web link (hacks.mozilla.org)
(TXT) w3m dump (hacks.mozilla.org)
| entuno wrote:
| This kind of thing always makes me nervous, because you end with
| a mix of methods where you can (supposedly) pass arbitrary user
| input to them and they'll safely handle it, and methods where you
| can't do that without introducing vulnerabilities - but it's not
| at all clear which is which from the names. Ideally you design
| that in from the state, so any dangerous functions are very
| clearly dangerous from the name. But you can't easily do that
| down the line.
|
| I'm also rather sceptical of things that "sanitise" HTML, both
| because there's a long history of them having holes, and because
| it's not immediately clear what that means, and what exactly is
| considered "safe".
| voxic11 wrote:
| The idea is you wouldn't mix innerHTML and setHTML, you would
| eliminate all usage of innerHTML and use the new setHTMLUnsafe
| if you needed the old functionality.
| post-it wrote:
| > you would eliminate all usage of innerHTML
|
| The mythical refactor where all deprecated code is replaced
| with modern code. I'm not sure it has ever happened.
|
| I don't have an alternative of course, adding new methods
| while keeping the old ones is the only way to edit an append-
| only standard like the web.
| noduerme wrote:
| Finally, a good use case for AI.
| josefx wrote:
| Wouldn't AI be trained on data using innerHTML?
| Aachen wrote:
| My experience is that they somehow print quite modern
| code despite things like ES6 being too new to be standard
| knowledge even for me and I'm not even middle-aged yet
|
| Maybe the last 10 years saw so much more modern code than
| the last cumulative 40+ years of coding and so modern
| code is statistically more likely to be output? Or maybe
| they assign higher weights to more recent commits/sources
| during training? Not sure but it seems to be good at
| picking this up. And you can always feed the info into
| its context window until then
| skeeter2020 wrote:
| This is not my experience. Claude has been happily
| generating code over the past week that is full of
| implicit any and using code that's been deprecated for at
| least 2 years.
|
| >> Maybe the last 10 years saw so much more modern code
| than the last cumulative 40+ years of coding and so
| modern code is statistically more likely to be output?
|
| The rate of change has made defining "modern" even more
| difficult and the timeframe brief, plus all that new code
| is based on old code, so it's more like a leaning tower
| than some sort of solid foundation.
| SahAssar wrote:
| ES6 is 11 years old. It's not that new.
| chrisweekly wrote:
| > "ES6 being too new to be standard knowledge"
|
| Huh? It's been a decade.
| charcircuit wrote:
| Which is why it can easily understand how innerHTML is
| being used so that it can replace it with the right
| thing.
| stvltvs wrote:
| Honest question: Is there a way to get an LLM to stop
| emitting deprecated code?
| fragmede wrote:
| Theoretically, if you could train your own, and remove
| all references to the deprecated code in the training
| data, it wouldn't be able to emit deprecated code.
| Realistically that ability is out of reach at the
| hobbiest level so it will have to remain theoretical for
| at least a few more iterations of Moore's law.
| Aachen wrote:
| Yeah, using a kilowatt GPU for string replacement is
| going to be the killer feature. I probably shouldn't even
| be joking, people are using it like this already
| charcircuit wrote:
| When the condition for when you want to replace is hard
| to properly specify, AI shines for such find and
| replaces.
| Aachen wrote:
| This one is literally matching "innerHTML = X" and
| setting "setHTML(X)" instead. Not some complex data
| format transformation
|
| But I can see what you mean, even if then it would still
| be better for it to print the code that does what you
| want (uses a few Wh) than doing the actual transformation
| itself (prone to mistakes, injection attacks, and uses
| however many tokens your input data is)
| charcircuit wrote:
| That can break the site if you do the find and replace
| blindly. The goal here is to do the refactor without
| breaking the site.
| lelanthran wrote:
| > When the condition for when you want to replace is hard
| to properly specify, AI shines for such find and
| replaces.
|
| And, in your opinion, this is one of those cases?
| charcircuit wrote:
| It is because the new API purposefully blocks things the
| old API did not.
| littlestymaar wrote:
| This ship has sailed unfortunately, no later than
| yesterday I've seen coworkers redact a screenshot using
| chatGTP.
| Vinnl wrote:
| I kinda like the way JS evolved into a modern language,
| where essentially ~everyone uses a linter that e.g.
| prevents the use of `var`. Sure, it's technically still in
| the language, but it's almost never used anymore.
|
| (Assuming transpilers have stopped outputting it, which I'm
| not confident about.)
| thunderfork wrote:
| Depending on the transpiler and mode of operation, `var`
| is sometimes emitted.
|
| For example, esbuild will emit var when targeting ESM,
| for performance and minification reasons. Because ESM has
| its own inherent scope barrier, this is fine, but it
| won't apply the same optimizations when targeting (e.g.)
| IIFE, because it's not fine in that context.
|
| https://github.com/evanw/esbuild/issues/1301
| delaminator wrote:
| for some values of "everyone" and "never".
| yawaramin wrote:
| Actually...
| https://github.com/microsoft/TypeScript/issues/52924
| Vinnl wrote:
| Ah yeah, I remember that. General point still stands: in
| terms of the lived experience of developers, `var` is
| essentially deprecated.
| plorkyeran wrote:
| I touch JS that uses var heavily on a daily basis and I
| would be incredibly surprised to find out that I am alone
| in that.
| thenewnewguy wrote:
| If you want to adopt this in your project, you can add a
| linter that explicitly bans innerHTML (and then go fix the
| issues it finds). Obviously Mozilla cannot magically fix
| the code of every website on the web but the tools exist
| for _your_ website.
| bulbar wrote:
| It for sure happens for drop in replacements.
| littlestymaar wrote:
| Nobody's talking about old code here.
|
| Having an alternative to innerHTML means you can ban it
| from new code through linting.
| croes wrote:
| If I need the old functionality why not stick to innerHTML?
| orf wrote:
| because the "unsafe" suffix conveys information to the
| reader, whereas `innherHTML` does not?
| goatlover wrote:
| Any potential reader should be familiar with innerHTML.
| kennywinker wrote:
| Right. Like how any potential reader is familiar with the
| risks of sql injection which is why nothing has ever been
| hacked that way.
|
| Or how any potential driver is familiar with seat belts
| which is why everybody wears them and nobody's been
| thrown from a car since they were invented.
| orf wrote:
| yes, and bugs shouldn't exist because everyone should be
| familiar with everything.
| croes wrote:
| But if some are marked unsafe and others are not it gives
| a false sense of security if something is not marked
| unsafe.
| orf wrote:
| So we shouldn't mark anything as unsafe then? And give no
| indication whatsoever?
|
| The issue isn't that the word "safe" doesn't appear in
| safe variants, it's that "unsafe" makes your intentions
| clear: "I know this is unsafe, but it's fine because of X
| and Y".
| croes wrote:
| Maybe we should add the word safe and consider everything
| else as unsafe
| orf wrote:
| Like life, things should default to being safe. Unsafe,
| unexpected behaviours should be exception and thus
| require an exceptional name.
|
| Legacy and backwards compatibility hampers this, but
| going forward...
| tbrownaw wrote:
| Because then your linter won't be able to tell you when
| you're done migrating the calls that can be migrated.
| philipwhiuk wrote:
| Because sooner or later it'll be removed.
| croes wrote:
| And break millions of sites?
| goatlover wrote:
| No because the web has to remain backwards compatible
| with older sites. This has always been the case.
| reddalo wrote:
| You can't rename an existing method. It would break
| compatibility with existing websites.
| extraduder_ire wrote:
| I looked up setHTMLUnsafe on MDN, and it looks like its been
| in every notable browser since last year.
|
| Good idea to ship that one first, when it's easier to
| implement and is going to be the unsafe fallback going
| forward.
| onion2k wrote:
| _I looked up setHTMLUnsafe on MDN, and it looks like its
| been in every notable browser since last year._
|
| Oddly though, the Sanitizer API that it's built on doesn't
| appear to be in Safari. https://developer.mozilla.org/en-
| US/docs/Web/API/Sanitizer
| DoctorOW wrote:
| They do link the default configuration for "safe":
| https://wicg.github.io/sanitizer-api/#built-in-safe-default-...
|
| But I agree, my default approach has usually been to only use
| innerText if it has untrusted content:
|
| So if their demo is this:
| container.SetHTML(`<h1>Hello, {name}</h1>`);
|
| Mine would be: let greetingHeader =
| container.CreateElement("h1"); greetingHeader.innerText
| = `Hello, {name}`;
| itishappy wrote:
| What if I wanted an <h2>?
|
| Edit: I don't mean this flippantly. If I want to render, say,
| my blog entry on your site, will I need to select every
| markup element from a dropdown list of custom elements that
| only accept text a la Wordpress?
| DoctorOW wrote:
| If it's anything complex I'm doing it server side,
| personally
| post-it wrote:
| realSetSafeHTML()
| jncraton wrote:
| You are right that the concept of "safe" is nebulous, but the
| goal here is specifically to be XSS-safe [1]. Elements or
| properties that could allow scripts to execute are removed.
| This functionality lives in the user agent and prevents adding
| unsafe elements to the DOM itself, so it should be easier to
| get correct than a string-to-string sanitizer. The logic of "is
| the element currently being added to the DOM a <script>" is
| fundamentally easier to get right than "does this HTML string
| include a script tag".
|
| [1] https://developer.mozilla.org/en-
| US/docs/Web/API/Element/set...
| entuno wrote:
| It's certainly an improvement over people trying to homebrew
| their own sanitisers. But that distinction of being XSS-safe
| is a potentially subtle one, and could end up being dangerous
| if people don't carefully consider whether XSS-safe is good
| enough when they're handling arbitrary users input like that.
| intrasight wrote:
| Also has made me nervous for years that there's been no
| schema against which one can validate HTML. "You want to
| validate? Paste your URL into the online validation tool."
| Dylan16807 wrote:
| This help? https://github.com/validator/validator
|
| But for html snippets you can pretty much just check that
| tags follow a couple simple rules between <> and that
| they're closed or not closed correctly.
| intrasight wrote:
| That app does look helpful!
| snowhale wrote:
| the browser-native Sanitizer API has one advantage the library
| approaches don't: it uses the same HTML parser the browser uses
| to render. libraries like DOMPurify parse in a separate context
| then re-serialize, and historically that round-trip is where
| most bypasses came from. when the sanitizer and the renderer
| share the same parser, mutation XSS attacks have nowhere to
| hide.
| pornel wrote:
| BTW, HTML allows inline SVG with an XML-flavored syntax that
| interprets <script/> and <title> differently. It's a goldmine
| for sanitizer escapes. There are completely bonkers syntax
| switching and error recovery rules that interact with parsing
| modes (there's even an edge case where a particular
| _attribute value_ switches between HTML and XML-ish parsing
| rules).
|
| Don't even try to allow inline <svg> from untrusted sources!
| (and then you still must sanitise any svg files you host)
| kccqzy wrote:
| If you just serve SVGs through <img> tag it'll be much
| safer. I never understood the appeal of inline <svg>
| anyways.
| rwj wrote:
| Inline reduces round trips.
| toast0 wrote:
| You can use img with a data url?
| lenkite wrote:
| Inline SVG is stylable with CSS styles in the same HTML
| page.
| runarberg wrote:
| Also animatible with the same context (Animation API,
| etc.) as the parent page, so different SVGs can influence
| each other's animations.
| cxr wrote:
| It may be using some of the same deserialization machinery,
| but "parsing" is a broad term that includes things that the
| sanitizer is doing and that the browser's ordinary content-
| processing - rendering path does not.
|
| Even with this being a native API, there are still two
| parsers that need to be maintained. What a native API
| achieves is to shift the onus for maintaining synchronicity
| between the two onto the browser makers. That's not nothing,
| but it's also not the sort of free lunch that some people
| naively believe it is.
| noduerme wrote:
| Some sanitization is better than none? If you're relying on the
| browser to handle it for you, you're already in a lot of
| trouble.
| jaffathecake wrote:
| fwiw, if you serve your page with:
|
| Content-Security-Policy: require-trusted-types-for 'script'
|
| ...then it blocks you from passing regular strings to the
| methods that don't sanitize.
| Cthulhu_ wrote:
| Ideally you should be able to set a global property somewhere
| (as a web developer) that disallows outdated APIs like
| `innerHTML`, but with the Big Caveat that your website will not
| work on browsers older than X. But maybe there's web standards
| for that already, backup content if a browser is considered
| outdated.
| afavour wrote:
| I like the idea of that. But I imagine linting rules are a
| much more immediate answer in a lot of projects.
| staticassertion wrote:
| Doesn't using TrustedTypes basically do that? I'm not really
| web-y, someone please correct me if I'm off.
| madeofpalk wrote:
| Yup, this is basically what TrustedTypes is for!
| cxr wrote:
| It's not an "outdated API". It's still good for what it was
| always meant for: parsing trusted, application-generated
| markup and atomically inserting it into the content tree as a
| replacement for a given element's existing children.
|
| > set a global property somewhere (as a web developer) that
| disallows[...] `innerHTML`
| Object.defineProperty(Element.prototype, "innerHTML", {
| set: (() => { throw Error("No!") }) });
|
| (Not that you should actually do this--anyone who has to
| resort to it in their codebase has deeper problems.)
| onion2k wrote:
| _it 's not at all clear which is which from the names_
|
| There's setHTML and setHTMLUnsafe. That seems about as clear as
| you can get.
| hahn-kev wrote:
| But you can use InnerHTML to set HTML and that's not safe.
| onion2k wrote:
| At this point that API has been around for decades and is
| probably impossible to deprecate without breaking fairly
| large amounts of the web. The only option is to introduce a
| new and better API, and _maybe_ eventually have the browser
| throw out console warnings if a page still uses the old
| innerHTML API. I doubt any browser vendor will be gung ho
| enough to actually remove it for a very long time.
| entuno wrote:
| If that'd been the design from the start, then sure. But it's
| not at all obvious that setHTML is safe with arbitrary user
| input (for a given value of "safe") and innerHTML is
| dangerous.
| HWR_14 wrote:
| That's why I only allow user input of alphanumeric ascii
| characters. No need to worry about sanitation then, and you can
| just remove all the characters that don't match.
|
| (It's a joke, but it is also 100% XSS, SQL injection, etc. safe
| and future proof)
| thaumasiotes wrote:
| > I'm also rather sceptical of things that "sanitise" HTML,
| both because there's a long history of them having holes, and
| because it's not immediately clear what that means, and what
| exactly is considered "safe".
|
| What is safe depends on where the sanitized HTML is going, on
| what you're doing with it.
|
| It isn't possible to "sanitize HTML" after collecting it so
| that, when you use it in the future, it will be safe. "Safe" is
| defined by the use.
|
| But it is possible to sanitize it _before using it_ , when you
| know what the use will be.
| cxr wrote:
| > it's not at all clear which is which from the names. Ideally
| you design that in from the [start]
|
| It was, and there is: setting elementNode.textContent is safe
| for untrusted inputs, and setting elementNode.innerHTML is
| unsafe for untrusted inputs. The former will escape everything,
| and the latter won't escape anything.
|
| You are right that these "sanitizers" are fundamentally
| confused:
|
| > _" HTML sanitization" is never going to be solved because
| it's not solvable.P There's no getting around knowing whether
| or any arbitrary string is legitimate markup from a trusted
| source or some untrusted input that needs to be treated like
| text. This is a hard requirement._
|
| <https://news.ycombinator.com/item?id=46222923>
|
| The Web platform folks who are responsible for getting
| fundamental APIs standardized and implemented natively are in a
| position to know better, and they should know better. This API
| should not have made it past proposal stage and should not have
| been added to browsers.
| Dylan16807 wrote:
| > There's no getting around knowing whether or any arbitrary
| string is legitimate markup from a trusted source or some
| untrusted input that needs to be treated like text. This is a
| hard requirement.
|
| It is not a hard requirement that untrusted input is "treated
| like text". And this API lets you customize exactly what
| tags/attributes are allowed in the untrusted input. That's
| way better than telling everyone to write their own; it's not
| trivial.
| simonw wrote:
| Great to see this start to show up, but it looks like it will be
| a while before browser support is widely distributed enough to
| rely on it being present: https://caniuse.com/mdn-
| api_element_sethtml
| jraph wrote:
| Indeed, as any browser API, it might be for in a few years
| (months if happy with the most recent versions), and we may
| have polyfills in the meantime.
| tuyiown wrote:
| I wouldn't advise polyfills on this one, it entirely depends
| on the browser ability to evaluate cross scripting and cross
| origin rule on a arbitrary snippet. This is not a convenience
| API.
| antonyh wrote:
| A rather deceptive title, given that 'innerHTML' isn't going
| away.
| jandrese wrote:
| I think the title is trying to convince you to switch from
| InnerHTML to SetHTML.
| tuyiown wrote:
| This is nice. The best part is that all aspects of network access
| are now properly controlled so that security transitioned from a
| chain of trusted code to a chain of trusted security setup on
| hosts, with existing workable safe defaults.
| bingemaker wrote:
| Nice one. Will there be any impact on __dangerouslySetInnerHTML
| (React)?
| Aachen wrote:
| So you can still inject <h1> or <br><br><br>... etc into your
| username, in the given example
|
| Preventing one bug class (script execution) is good, but this
| still allows arbitrary markup to the page (even <style> CSS
| rules) if I'm reading the docs correctly. You could give Paypal a
| fresh look for anyone who opens your profile page, if they use
| this. Who would ever want this?
| embedding-shape wrote:
| > So you can still inject <h1> or <br><br><br>... etc into your
| username, in the given example
|
| How exactly, given that setHTML sanitizes the input? If you
| don't want to have any HTML tags allowed, seems you can
| configure that already? https://wicg.github.io/sanitizer-
| api/#built-in-safe-default-...
| Aachen wrote:
| > How exactly, given that setHTML sanitizes the input?
|
| The article says that the output is:
| <h1>Hello my name is</h1>
|
| So it keeps (non-script) html tags (and presumably also
| attributes) in the input. Idk how you're asking "how" since
| it's the default behavior
|
| Stripping HTML tags completely has always been possible with
| the drop-in replacement `textContent`. Making a custom
| configuration object for that is much more roundabout
| embedding-shape wrote:
| Yes, because that's the default configuration, if you don't
| want that, stop using the default configuration? It's still
| sanitizing away the common XSS holes, hence it's a safer
| alternative to .innerHTML, and a more flexible alternative
| to .innerText
| Aachen wrote:
| Shouldn't use innerText anyway (nonstandard, worse
| performance, tries to parse the HTML and gives you
| unexpected behavior if e.g. a style is set that makes an
| element invisible but still has text inside, doesn't work
| on all DOM nodes...)
|
| I can see how it's a way of allowing some tags like bold
| and italic without needing a library or some custom
| parser, but I didn't understand what the point of this
| default could be and so why it exists (a sibling comment
| proposed a plausible answer: hardening on top of another
| solution)
|
| > Yes, because that's the default configuration, if you
| don't want that, stop using the default configuration?
|
| "don't use it if it's not what you want" is perhaps the
| silliest possible answer to the question "what's the use-
| case for this"
| embedding-shape wrote:
| > Shouldn't use innerText anyway (nonstandard, worse
| performance, tries to parse the HTML and gives you
| unexpected behavior if e.g. a style is set that makes an
| element invisible but still has text inside, doesn't work
| on all DOM nodes...)
|
| Maybe you meant .innerHTML? .innerText AFAIK doesn't try
| to parse HTML (why would it?), but I don't understand
| what you mean with nonstandard, both .innerHTML and
| .innerText are part of the standards, and I think they've
| been for a long time.
|
| > but I didn't understand what the point of this default
| could be and so why it exists (a sibling comment proposed
| a plausible answer: hardening on top of another solution)
| [...] the question "what's the use-case for this"
|
| I guess maybe third time could be the charm: it's for
| preventing XSS holes that are very common when people use
| .innerHTML
| Aachen wrote:
| > maybe third time could be the charm: it's for
| preventing XSS holes
|
| That information is in the question, so sadly no this
| still doesn't make sense to me because I don't understand
| any scenario in which this is what the developer wants.
| You always still need more code (to filter the right
| tags) or can just use textContent (separating data and
| code completely, imo the recommended solution)
|
| > Maybe you meant .innerHTML? .innerText AFAIK doesn't
| try to parse HTML (why would it?)
|
| No, I didn't mean that, yes it does, and no I don't know
| why it is this way. If you don't believe me and don't
| want to check it out for yourself, I'm not sure what more
| I can say
| lelanthran wrote:
| > I don't understand any scenario in which this is what
| the developer wants.
|
| Client-side includes.
| benregenspan wrote:
| It seems like the goal of the default configuration is
| preventing script injection while being otherwise very
| permissive. Basically, "safer than innerHTML, even when
| used very lazily". But I would expect guidance to evolve
| saying that it almost never makes sense to use the
| default and instead to specify a configuration that makes
| contextual sense for a given field.
|
| The default might be suitable for something like an
| internal blog where you want to allow people to sometimes
| go crazy with `<style>` tags etc, just not inject
| scripts, but I would expect it to almost always make
| sense to define a specific allowed tag and attribute
| list, as is usually done with the userland predecessors
| to this API.
| cogman10 wrote:
| > Who would ever want this?
|
| The main case I can think of is wanting some forum
| functionality. Perhaps you want to allow your users to be able
| to write in markdown. This would provide an extra layer of
| protection as you could take the HTML generated from the
| markdown and further lock it down to only an allowed set of
| elements like `h1`. Just in case someone tried some of the
| markdown escape hatches that you didn't expect.
| Aachen wrote:
| > This would provide an extra layer of protection
|
| I think this might be the answer. There's no point to it by
| itself (either you separate data and code or you don't and
| let the user do anything to your page), but if you're already
| using a sanitiser _and_ you can 't use `textContent` because
| (such as with Markdown) there'll be HTML tags in the output,
| then this could be extra hardening. Thanks!
| iLoveOncall wrote:
| You'd never want to store the processed HTML anyway, this is
| website building 101.
| efilife wrote:
| I store both, to serve processed HTML faster, and to be
| able to rebuild it just in case. Is this ok?
| byproxy wrote:
| > but this still allows arbitrary markup to the page (even
| <style> CSS rules) if I'm reading the docs correctly.
|
| If that's true, seems like it's still a security risk given
| what you can do with CSS these days:
| https://news.ycombinator.com/item?id=47132102
| circuit10 wrote:
| You can use selectors to gain some information about things
| like input fields, e.g. https://www.invicti.com/blog/web-
| security/private-data-stole...
|
| Or I guess you could completely restyle and change the text
| of UI elements so it looks like the user is doing one thing
| when they're actually doing something completely different
| like sending you money
| qingcharles wrote:
| Back in 2002 (?) I got banned from a certain auction site
| because I managed to inject HTML into my username that made
| it so once I had bid the "Bid" button disappeared for all
| subsequent users.
| itishappy wrote:
| > If the default configuration of setHTML( ) is too strict (or
| not strict enough) for a given use case, developers can provide
| a custom configuration that defines which HTML elements and
| attributes should be kept or removed.
| Aachen wrote:
| Injecting markup into someone else's website isn't what I'd
| call too strict a default configuration
|
| If you mean to convey that it's possible to configure it to
| filter properly, let me introduce you to `textContent` which
| is older than Firefox (I'm struggling to find a date it's so
| old)
| itishappy wrote:
| That's the whole point of the setHTML.
|
| How would I set a header level using textContent?
| Aachen wrote:
| The traditional way: separating data and code
| document.createElement("h1").textContent = `Hello,
| ${username}!`
|
| If you allow <h1> in the setHTML configuration or use the
| default, users with the tag in their username also always
| get it rendered as markup
| matsemann wrote:
| Which is why you only use it where you want to allow some
| kind of html..?
| itishappy wrote:
| It sounds like you're arguing against a specific usecase,
| rather than the technology itself. If you don't want
| arbitrary markup in usernames, setHTML would absolutely
| be the wrong choice, but that's not really a good
| argument against setHTML.
| jerf wrote:
| If I'm reading this right,
| .setHTML("<h1>Hello</h1>", new Sanitizer({}))
|
| will strip all elements out. That's not too difficult.
|
| Plus this is defense-in-depth. Backends will still need to
| sanitize usernames on some standard anyhow (there's not a lot
| of systems out there that should take arbitrary Unicode input
| as usernames), and backends SHOULD (in the RFC sense [1]) still
| HTML-escape anything they output that they don't want to be raw
| HTML.
|
| [1]: https://www.rfc-editor.org/rfc/rfc2119
| benmmurphy wrote:
| i think the use case for setHTML is for user content that
| contains rich text and to display that safely. so this is not
| an alternative for escaping text or inserting text into the
| DOM but rather a method for displaying rich text. for example
| maybe you have an editor that produces em, and strong tags so
| now you can just whitelist those tags and use setHTML to
| safely put that rich text into the DOM without worrying about
| all the possible HTML parsing edge cases.
| evilpie wrote:
| You aren't reading it right. new
| Sanitizer({})
|
| This Sanitizer will allow everything by default, but setHTML
| will still block elements/attributes that can lead to XSS.
|
| You might want something like: new
| Sanitizer({ replaceWithChildrenElements: ["h1"], elements:
| [], attributes: [] })
|
| This will replace <h1> elements with their children (i.e.
| text in this case), but disallow all other elements and
| attributes.
| piccirello wrote:
| `setHTML` is meant as a replacement for `innerHTML`. In the use
| case you describe, you would have never wanted `innerHTML`
| anyway. You'd want `innerText` or `textContent`.
| iLoveOncall wrote:
| But that's what setHTML isn't at all a replacement for
| innerHTML.
|
| You still need innerHTML when you want to inject HTML tags in
| the page, and you could already use innerText when you didn't
| want to.
|
| Having something in between is seriously useless.
| Dylan16807 wrote:
| > You still need innerHTML when you want to inject HTML
| tags in the page
|
| What makes you say this?
| afavour wrote:
| > Who would ever want this?
|
| Anyone who wants to provide some level of flexibility but
| within bounds. Say, you want to allow <strong> and <em> in a
| forum post but not <script>. It's not too difficult to imagine
| uses.
| goatlover wrote:
| Forums would already have code that sanitizes user input when
| it's submitted. Users aren't directly setting html elements.
| afavour wrote:
| And is that sanitization perfect? Kept up to date?
|
| With a safe API like this one that's tied to the browser's
| own interpretation of HTML (i.e. it is perfectly placed to
| know exactly what is and isn't dangerous given it is the
| one rendering it) wouldn't it be much better to rely on
| that?
| kccqzy wrote:
| There's innerText if you don't want markup. Or more verbosely,
| document.createTextNode followed by whatever.appendChild.
| dheera wrote:
| > So you can still inject <h1> or <br><br><br>... etc into your
| username
|
| Are we taking out all the fun of the web? I absolutely loved
| the <marquee> names people had in the early days of Facebook,
| it was all harmless fun.
|
| If injection of frontend code takes down your backend, your
| backend sucks, fix it.
| lelanthran wrote:
| > Who would ever want this?
|
| Your lack of imagination is disturbing :-)
|
| https://github.com/lelanthran/ZjsComponent
| cogman10 wrote:
| Seems like this has a bunch of footguns. Particularly if you
| interact with the Sanitizer api, and particularly if you use the
| "remove" sanitizer api.
|
| Don't get me wrong, better than nothing, but also really really
| consider just using "setText" instead and never allow the user to
| add any sort of HTML too the document.
| evilpie wrote:
| Using an allowlist based Sanitizer you are definitely less
| likely to shoot yourself in the foot, but as long as you use
| setHTML you can't introduce XSS at least.
| GalaxyNova wrote:
| It's worse than nothing, since inevitably people will use this
| thinking it's 100% safe when it's not.
| lelanthran wrote:
| > never allow the user to add any sort of HTML too the
| document.
|
| What about when the author of the page wants to add large html
| fragments to the page?
|
| Are you saying that you cannot think of a single use for this,
| considering how often innerHTML is being used?
| dbvn wrote:
| at what point can we consider the development of "set this
| element's text/html" to be done?
| Aachen wrote:
| When browsers implement a variant that lets you separate data
| and code perhaps. That's what I expected when reading the
| headline: setHtml(code, data, data, ...), just like
| parameterised SQL works: prepare("select rowid from %s where
| time < %n", tablename, mynumber)
|
| This new method they've cooked up would be called
| eval(code,options) if html was anything other than a markup
| language
| itishappy wrote:
| tablenames cannot be parameterized in SQL
|
| https://stackoverflow.com/questions/78516750/parametrize-
| tab...
| shadowgovt wrote:
| Oh, that's nice-to-have. Good work, Mozilla.
|
| It would close the loop better if you could also use policy to
| switch off innerHTML in a given page, but definitely a step in
| the right direction for plain-JavaScript applications.
| bryanrasmussen wrote:
| is there any situation where innerHTML would be preferable? I
| could suppose it might be more performant and so if you were
| constructing something that was not open to XSS it might
| theoretically be better (with the usual caveat that people always
| make mistakes about this kind of thing)
| kevincloudsec wrote:
| naming the old behavior setHTMLUnsafe is what did it for me.
| security features that require developers to opt in don't work.
| making the unsafe path feel unsafe does.
| shevy-java wrote:
| Well, the name SetHTML, or let's say:
| .set_html()
|
| Makes objectively more sense than:
| .inner_html() .inner_html = .set_inner_html()
|
| It is a fairly small thing, but ... really. One day someone
| should clean up the mess that is JavaScript. Guess it will never
| happen, but JavaScript has so many strange things ...
|
| I understand that this here is about protection against attacks
| rather than a better API design, but really - APIs should ideally
| be as great as possible the moment they are introduced and shown
| to the public.
| lloydatkinson wrote:
| To be pedantic that's the DOM API, which is exposed to
| JavaScript.
|
| The DOM API has always felt like, and still does, it was
| written by people that have never made an API.
| pier25 wrote:
| I don't think that's pedantic. Seems like a valid objection
| to me.
|
| So many issues in the client JS world originate from
| insufficient or bad browser APIs.
| lloydatkinson wrote:
| I don't really think it's pedantic it's just that unless
| you preface a lot of comments on HN these days, you'll get
| a lot of whataboutism and straw man arguments.
| giancarlostoro wrote:
| My corporate firewall blocks it due to the "hacks" in the
| subdomain / url. This is silly.
| ok123456 wrote:
| That's why the DNS for hackernews is news.ycombinator.com and
| not hackernews.org
| dogtimeimmortal wrote:
| Title was a bit rage-baity. And I think you can already do
| sanitation by writing a function to check input before passing it
| to innerHTML?
|
| This really just seems like another attempt at reinventing the
| wheel. Somewhat related, I find it ironic how i cannot browse
| hacks.mozilla.org in my old version of firefox("Browser not
| supported"). Also, developer.mozilla.org loads mangled to various
| degrees in current versions of palemoon, basilisk, and seamonkey
|
| It's like there is some sort of "browser cartel" trying to screw
| up The Web.
| Retr0id wrote:
| > you can already do sanitation by writing a function to check
| input before passing it to innerHTML
|
| This is like saying C is memory safe as long as your code
| doesn't have any bugs.
|
| More saliently, it does not consider parser differentials.
| dvh wrote:
| Kids in the '90s: SQL("select * from user where
| name = " + name);
|
| Kids in the '20s: div.innerHTML = "Hello " +
| user.name;
| Legend2440 wrote:
| Kids in the '30s: "Summarize this email: " +
| email.contents
|
| Prompt injection is just the same problem on a new technology.
| We didn't learn anything from the 90s.
| pier25 wrote:
| Tangential but it's amazing in 2026 browsers still don't ship a
| native DOM morph/merge API like morphdom or idiomorph.
| pyrolistical wrote:
| And for those who want a better innerHTML, use insertAdjacentHTML
| https://developer.mozilla.org/en-US/docs/Web/API/Element/ins...
|
| I don't ever use it with user input, but use it often when
| building SPA without frameworks
| austin-cheney wrote:
| Another solution is just use this at the start of your code:
| delete Element.prototype.innerHTML;
|
| Then assignments to innerHTML do not modify the element's
| textContent or child node list and assignments to it will not
| throw an error.
| jjcm wrote:
| What I really want is a <sandbox> element that can safely run
| dangerous code, not something that modifies dangerous code.
|
| Iframes have significant restrictions as they can't flow with the
| DOM. With AI and the increase in dynamic content, there's going
| to be even more situations where you run untrusted code. I want
| configurable encapsulation.
___________________________________________________________________
(page generated 2026-02-24 23:01 UTC)