• entuno 4 hours ago

    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".

    • HWR_14 8 minutes ago

      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)

      • jncraton 4 hours ago

        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...

        • intrasight 16 minutes ago

          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."

          • entuno 26 minutes ago

            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.

          • Cthulhu_ 2 hours ago

            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.

            • staticassertion 2 hours ago

              Doesn't using TrustedTypes basically do that? I'm not really web-y, someone please correct me if I'm off.

              • madeofpalk 35 minutes ago

                Yup, this is basically what TrustedTypes is for!

              • afavour 2 hours ago

                I like the idea of that. But I imagine linting rules are a much more immediate answer in a lot of projects.

              • snowhale 4 hours ago

                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 3 hours ago

                  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 2 hours ago

                    If you just serve SVGs through <img> tag it’ll be much safer. I never understood the appeal of inline <svg> anyways.

                    • lenkite 18 minutes ago

                      Inline SVG is stylable with CSS styles in the same HTML page.

                      • rwj 40 minutes ago

                        Inline reduces round trips.

                        • toast0 27 minutes ago

                          You can use img with a data url?

                  • voxic11 4 hours ago

                    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.

                    • extraduder_ire 3 hours ago

                      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.

                    • croes 3 hours ago

                      If I need the old functionality why not stick to innerHTML?

                      • orf 3 hours ago

                        because the "unsafe" suffix conveys information to the reader, whereas `innherHTML` does not?

                        • goatlover 2 hours ago

                          Any potential reader should be familiar with innerHTML.

                          • kennywinker an hour ago

                            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 an hour ago

                              yes, and bugs shouldn't exist because everyone should be familiar with everything.

                          • tbrownaw 3 hours ago

                            Because then your linter won't be able to tell you when you're done migrating the calls that can be migrated.

                            • philipwhiuk an hour ago

                              Because sooner or later it'll be removed.

                            • reddalo 3 hours ago

                              You can't rename an existing method. It would break compatibility with existing websites.

                              • post-it 4 hours ago

                                > 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.

                                • thenewnewguy 3 hours ago

                                  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.

                                  • Vinnl 3 hours ago

                                    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.)

                                    • delaminator 3 hours ago

                                      for some values of "everyone" and "never".

                                      • thunderfork 3 hours ago

                                        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

                                      • bulbar 3 hours ago

                                        It for sure happens for drop in replacements.

                                        • littlestymaar 2 hours ago

                                          Nobody's talking about old code here.

                                          Having an alternative to innerHTML means you can ban it from new code through linting.

                                          • noduerme 3 hours ago

                                            Finally, a good use case for AI.

                                            • Aachen 3 hours ago

                                              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 3 hours ago

                                                When the condition for when you want to replace is hard to properly specify, AI shines for such find and replaces.

                                                • Aachen 3 hours ago

                                                  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 an hour ago

                                                    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.

                                                • littlestymaar 2 hours ago

                                                  This ship has sailed unfortunately, no later than yesterday I've seen coworkers redact a screenshot using chatGTP.

                                                • josefx 3 hours ago

                                                  Wouldn't AI be trained on data using innerHTML?

                                                  • Aachen 3 hours ago

                                                    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 2 hours ago

                                                      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 an hour ago

                                                        ES6 is 11 years old. It's not that new.

                                                      • charcircuit 3 hours ago

                                                        Which is why it can easily understand how innerHTML is being used so that it can replace it with the right thing.

                                                        • stvltvs 3 hours ago

                                                          Honest question: Is there a way to get an LLM to stop emitting deprecated code?

                                                          • fragmede 3 hours ago

                                                            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.

                                                  • jaffathecake 3 hours ago

                                                    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.

                                                    • onion2k an hour ago

                                                      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.

                                                      • entuno 22 minutes ago

                                                        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.

                                                        • hahn-kev an hour ago

                                                          But you can use InnerHTML to set HTML and that's not safe.

                                                        • DoctorOW 4 hours ago

                                                          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 3 hours ago

                                                            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?

                                                          • noduerme 4 hours ago

                                                            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.

                                                            • post-it 4 hours ago

                                                              realSetSafeHTML()

                                                            • simonw 4 hours ago

                                                              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 4 hours ago

                                                                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 4 hours ago

                                                                  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.

                                                              • shevy-java 38 minutes ago

                                                                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 36 minutes ago

                                                                  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.

                                                                • Aachen 3 hours ago

                                                                  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?

                                                                  • piccirello 2 hours ago

                                                                    `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`.

                                                                    • cogman10 3 hours ago

                                                                      > 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 3 hours ago

                                                                        > 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!

                                                                      • itishappy 3 hours ago

                                                                        > 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 3 hours ago

                                                                          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 2 hours ago

                                                                            That's the whole point of the setHTML.

                                                                            How would I set a header level using textContent?

                                                                            • Aachen 2 hours ago

                                                                              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
                                                                              • itishappy 2 hours ago

                                                                                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.

                                                                                • matsemann 2 hours ago

                                                                                  Which is why you only use it where you want to allow some kind of html..?

                                                                          • byproxy 3 hours ago

                                                                            > 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 2 hours ago

                                                                              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 2 hours ago

                                                                                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.

                                                                            • jerf 2 hours ago

                                                                              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

                                                                              • evilpie 12 minutes ago

                                                                                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.
                                                                                • benmmurphy 2 hours ago

                                                                                  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.

                                                                                • kccqzy 2 hours ago

                                                                                  There’s innerText if you don’t want markup. Or more verbosely, document.createTextNode followed by whatever.appendChild.

                                                                                  • afavour 2 hours ago

                                                                                    > 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.

                                                                                    • embedding-shape 3 hours ago

                                                                                      > 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 3 hours ago

                                                                                        > 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 3 hours ago

                                                                                          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 3 hours ago

                                                                                            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 3 hours ago

                                                                                              > 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 2 hours ago

                                                                                                > 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

                                                                                              • benregenspan 3 hours ago

                                                                                                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.

                                                                                        • dheera an hour ago

                                                                                          > 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.

                                                                                        • cogman10 3 hours ago

                                                                                          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.

                                                                                          • GalaxyNova 9 minutes ago

                                                                                            It's worse than nothing, since inevitably people will use this thinking it's 100% safe when it's not.

                                                                                            • evilpie 3 hours ago

                                                                                              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.

                                                                                            • kevincloudsec an hour ago

                                                                                              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.

                                                                                              • tuyiown 4 hours ago

                                                                                                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.

                                                                                                • bryanrasmussen an hour ago

                                                                                                  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)

                                                                                                  • giancarlostoro 33 minutes ago

                                                                                                    My corporate firewall blocks it due to the "hacks" in the subdomain / url. This is silly.

                                                                                                    • dbvn 3 hours ago

                                                                                                      at what point can we consider the development of "set this element's text/html" to be done?

                                                                                                      • Aachen 3 hours ago

                                                                                                        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

                                                                                                    • shadowgovt 2 hours ago

                                                                                                      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.

                                                                                                      • antonyh 4 hours ago

                                                                                                        A rather deceptive title, given that 'innerHTML' isn't going away.

                                                                                                        • jandrese 2 hours ago

                                                                                                          I think the title is trying to convince you to switch from InnerHTML to SetHTML.

                                                                                                        • bingemaker 4 hours ago

                                                                                                          Nice one. Will there be any impact on __dangerouslySetInnerHTML (React)?