Google, Mozilla Close to Finalizing Sanitizer API for Chrome and Firefox Browse
portswigger.net
portswigger.net
- First of all it makes sense that this feature is provided by the browser itself and they take some responsibility if it doesn't work right.
- Currently the best library for sanitization is probably DOMPurify, and the native Sanitizer API is around 100x faster than DOMPurify, so that would speed up some things dramatically.
I just hope it won't take years for Safari to implement this.
1000%. Safari likes to talk big about rejecting new APIs to protect security & privacy, but there's a long list of APIs they haven't implemented just like this, that are strictly beneficial for users.
That both Firefox & Chrome have shipped working implementations of this (a serious fix to solve a top 10 OWASP security issue) before Safari has even shown any intent in look at it says a lot imo.
A feature not being supported is clear-cut and workable. This current mess where a feature might be supported, with different parts of the spec available only to Safari 14.1 Bug Sur and up but not 14.1 on Catalina is just tiresome.
Aren't we overdue for another indexedDB fuck up by the Safari Dev team?
Not only that, they restrict existing functionality. For example, all local storage is destroyed if you don’t access a site in seven days. At first blush that makes sense but it means there’s no way to reliably persist data to disk. If you run a web app you more or less have to create a backend, account signups, etc etc. Not only is it a lot of extra work it’s also going to be a huge security vulnerability. The result ends up being entirely counter to Apple’s stated intent.
Or why would they do it, considering it’s extra work compared to the status quo?
Looks like absolutely useless feature that will just make bloated browsers more bloated.
Then there are cases of different browsers parsing things differently and/or bad sanitization/serializing giving different results on repeated invocation or just being broken on the server-side. A simple client-side option is gonna be a lot simpler.
Additionally, the Sanitize API does a much better job of handling contextual parsing then many other similar backend APIs. What happens when you parse an HTML fragment assuming it will live in a `div`, and then it actually get inserted into a `table` cell? The spec goes into this is more detail here: https://wicg.github.io/sanitizer-api/#strings
The downsides, of course, are those associated with any thick-client/thin-server API design—more logic on the front-end means more logic to reimplement for different consumers.
Personally, I would probably still stick with Nokogiri for my own applications, but I can see both sides of the trade-off.
In this case, as long as there is an easily-available OSS polyfill, it'll be okay. Ideally sites will only load the polyfill when they need to (primarily only if they're on iOS).
Sure, some cooperate users hold out much longer, but those seem to be a tiny minority.
It is wrong to rely on everyone using a latest version of a browser. Every site should support at least 5 years old browsers and a good site will be usable in 10 years old browser including builtin browser of Windows.
Sadly, in reality most sites are made so poorly that they don't open on 5-years old smartphone. It shows how low qualified became modern web developers.
But this API doesn't implement anything we couldn't do before, it's a more correct and much faster implementation of something we already have libraries for. The vast majority of users will have an almost immediate benefit from sites using this API, both in speed and security.
That's what I take this comment chain to be about, since talking about "a majority" doesn't make sense otherwise. Supporting browser versions used by at most 2% of all users is the name of the game in webdev, that's what made IE so annoying.
An attacker will always be able to use an older browser version that does not have the built-in feature.
You do not purify DOM for the attackers' browser, they can just open the dev console and execute arbitrary JS anyway, you purify it so that user-input that one renders is also safe for others users to see without allowing attacker controlled scripts to be executed or DOM elements that leak user info on load to be inserted.
And you can always simply start to show a banner that tells "contnet blocked, upgrade your browser" in a few years, once a big enough majority of your target user base upgraded to a browser that supports it.
It matters if users use the secure browsers, and with Chrome, Firefox, and (hopefully) Safari implementing it the vast majority of them will within a few release cycles.
Why can't HTML be composed client-side using proper, contextual APIs instead of "sanitizing" it afterwards? It won't work. It reminds me of PHP magic quotes - they didn't work.
We'd still need a sanitizer for URLs, of course, those are one of the pesky parts of the web specs.
DOMPurify performs a string->string conversion, so it's got no context information. I don't understand how this can work. It didn't work for PHP magic quotes. It doesn't work for SQL queries. Why can and should it work for HTML?
Remember that "work" implies not just "safe". It implies "it must show what the user wanted to see". Otherwise
var sanitize = function(input){ return "<p>";}
Would "just work" perfectly.This API is simply a DOM-based whitelist for preventing script execution coupled with a contextual parser. No more, no less. It doesn't solve every problem with accepting untrusted HTML, sure, but it's good enough for a wide variety of use-cases (One example that comes up frequently is embedded markdown, another good example is a mail client—both situations where formatted, user-controlled data is important). The benefit of doing this client-side instead of server-side is that you can stay up to date with any changes that the client may make to how it's processing HTML that may have security implications. (The downsides, of course, are those associated with any thick-client/thin-server API design—more logic on the front-end means more logic to reimplement for different consumers)
That's just a quick example. There are plenty of other conversions of user data that can't simply be displayed as plain text and need to be converted to HTML. That conversion doesn't always happen in the browser. Sometimes it's actually just straight HTML that needs to be displayed, but because it came from a user, it needs to be sanitized.
But, pardon me, the proper way to render markdown in the browser would be to let the markdown converter output a node or element. The markdown converter, when doing the conversion, knows what to do, which part of markdown must be converted to what HTML element, and what should be inserted as textContent.
So I would imagine that when the markdown parser encounters
*** This is a section
writes something like: var el = document.createElement("h3")
el.textContent = ...
so an element is returned by the parser at the end, not a string."Sanitizing" a string afterwards will either yield a safe (but possibly wrong) result, or a correct (but possible unsafe) result.
You cannot decide what is safe if you don't have context. Unless "safe" means something like "nothing that looks like a script". What then if the markdown converter decides that it needs js for some feature?
All of this stuff is possible to do already with 3rd-party frameworks with varying levels of performance/reliability, and there may be other methods you use to sanitize your output. If you're a responsible developer, if you have experience trying to guard against XSS, or if you're working with a framework like React/Vue/whatever, this may not change much about your life.
However, I think it's understated how many sites struggle with this, and how many of them do sanitization poorly, and it is much better to be able to point them towards a single API and say, "look, just call this function."
One of the bigger vulnerabilities I've ever found in a website (https://danshumway.com/blog/gamasutra-vulnerabilities/) was in part a problem directly caused by bad sanitization. When I reported that vulnerability, I didn't advise them to fix their sanitizer because I didn't feel confident it would really fix the problem or that there wouldn't be other issues in the future. I could have pointed them towards something like DOMPurify, but instead I advised that they start embedding posts in iFrames with reduce permissions so any scripts that did run wouldn't be able to get at user data.
I wonder if there was more of a standard around this stuff if some engineer on their team might not have caught the problem earlier.
Similar to the SameSite changes to cookies, getting a native sanitizer isn't about forcing you to stop doing serverside sanitization or even changing your workflow much at all. It's about making the defaults safer and making it easier for hobby developers (and companies too) to avoid messing up because they don't understand the risks of calling otherwise innocuous functions.
What's even more exciting is some of the work going into trusted types (https://web.dev/trusted-types/). The sanitizer API gives you a function you can call to get reasonably safe output without a lot of configuration or a 3rd-party framework or knowing what to download. The trusted types API is designed to make it harder to accidentally go outside of that sanitizer. It's not a silver bullet, but it's a big deal for companies where you're trying to set org-wide policies and avoid XSS attacks sneaking in through a random feature by a team that you're not watching closely enough.
It's promising to see Firefox/Google working together on this stuff, I hope they continue with other APIs. The ideas are good, they just need iteration and more input, and these kinds of API areas are imo where Google/Mozilla tend to work together pretty well. I'm fairly optimistic about them.
But, as always, specific & easy-to-use APIs are going to win out over more "fully general" ones. Are you suggesting that everybody learn DSSSL and write queries like
((match-element? nd '(section title))
(format-number-list
(hierarchical-number '("chapter" "section") nd)
"1"
"."))
Simply to be able to safely display some markup? I for one would much rather work on an AST with normal javascript instead of having to learn another DSL.Hell, no ;) Just that they pickup SGML insertion contexts as a concept where to escape what chars when that was known in the late 1970s already (ISO 8879 was published in 1986, but took a loong time through the committees). It's incredibly lame they haven't figured DTD/markup grammars and can only handle hard-coded HTML insertion contexts - one more thing to fall off the cliff as HTML evolves, and unnecessarily so.
OTOH, it always is fun to show HNers what could've would've been using DSSSL/Scheme in browsers ...
Don't let the false narrative of HTML exceptionalism taint your reasoning: markup language are envisioned as a rational technique for authoring, reusing, and preserving text and other documents. Unlike moribund browser tech putting ads and monopolistic media distribution models at the helmet. Soon, there will be only Chrome left, with FF only a token implementation also financed by Google.
Where is this data they are sanitizing coming from? Why would you want every browser treating this differently?
The reason being that all browsers parse potential XSS (and mXSS) content slightly differently so that a server side sanitizer by definition will never parse content in exactly the same way as a specific version of a given browser, since it's not sharing exactly the same runtime.
This semantic gap between browser rendering quirks and server side approximations can be exploited by an attacker to slip content past the server side sanitizer.
And that's why the browser vendors are actively working with Cure53, the author of DOMPurify, in order to ship Sanitizer API.
I'm just surprised this has taken so long.
Aside from that, a subset of people using DOMPurify—maybe even a plurality—seem to treat it as a talisman and can't explain how or if they've configured it correctly to provide the kind of protections they need for their use case. Security is not a separable concern.
It is not clear to me what subtle things are wrong with the parent comment. But I'm pretty sure I can see the flaw in claiming that something is wrong without saying how. It's an impossible-to-disprove accusation which therefore does not advance the conversation.
Your comment is nothing but vague, unverified claims. Why do should you be given the benefit of the doubt but not the person you're replying to? If you have a real concern, share it so that it can be evaluated and everyone, including the original poster, can learn from it.
And then you proceed to imply developers who have UIs susceptible to XSS are to blame, even though this is a known attack vector where browsers parse adversarial trees in an exploitable way that is completely opaque to everyone?
This is silly. Hold your tongue or speak clearly, don’t jeer from a high horse
This doesn't make much sense to me, my app doesn't even rely on any servers, what do you suggest I do then?
> Where is this data they are sanitizing coming from?
It's untrusted, what does it matter where exactly it comes from?
> Why would you want every browser treating this differently?
Why do you say that each browser would tread this differently? There's a spec for how this should work: https://wicg.github.io/sanitizer-api/
Although I agree that there is nothing wrong with client-side sanitization, does this even apply to non-networked apps? Are you protecting your user from?
Or perhaps by "doesn't rely on any servers" you mean "serverless", which doesn't literally mean no servers. If your app's client hits a database-as-a-service somewhere then that is a server and it very well can sanitize for you.
Kind of from themselves, the app can open arbitrary files, you can't have people nuke themselves by opening a random file they got from the internet, or by pasting in something they don't understand. Also a plugin may sort of escape from the boundaries put on it by XSS-ing its user by outputting some malicious stuff.
> Or perhaps by "doesn't rely on any servers" you mean "serverless", which doesn't literally mean no servers. If your app's client hits a database-as-a-service somewhere then that is a server and it very well can sanitize for you.
No I actually meant no servers at all, it's a 100% local Electron app.
Some pages render data from GET parameters, which allows XSS by giving people a link. There's also scenarios where data from localstorage, cookies or manually copy-pasted data is an attack vector. Imagine I get you to paste my document into your text editor and that allows me to extract all your data.
The above example is a vulnerability in the app just the same, it just requires the attacker to consider different avenues of delivery (such as sharing the malicious script on a forum) then in traditional server connected apps.
You can have no back-end code but still can call external APIs hosted on servers you don't control or which can be compromised by malicious user generated content.
There is also a spec for how html should work.
And finally, 100% of the useful functionality of this ‘api’ could be implemented using a Javascript library or server side. Yes, browsers have slight disagreements over handling broken html. So the first step of server side sanitation is to remove the broken stuff. This also removes a lot of cross browser incompatibility. And then you select tags and only allow the tags you want. Basic xss sanitation like it’s 1999.
Nobody needs any built in api for this. This proposal is about solving a solved problem.
This isn't the sort of API that unlocks some previously impossible feature, but DOMPurify is incredibly slow and I would trust the browser's implementation more, obviously. Rewriting DOMPurify better has always been an option, but that's not an easy value proposition for most developers, hence the best we've got is still DOMPurify.
- It isn’t hard to do what this api does in Javascript as fast as the browser can do it. There already is api to convert strings to DOM and it isn’t exactly rocket science to iterate over the result and drop tags that aren’t on a list. I’m sure there can be a library that does it slowly but it doesn’t have to.
Right, so what?
> - It isn’t hard to do what this api does in Javascript as fast as the browser can do it. There already is api to convert strings to DOM and it isn’t exactly rocket science to iterate over the result and drop tags that aren’t on a list. I’m sure there can be a library that does it slowly but it doesn’t have to.
Of course it's DOMPurify's fault for being that slow, nobody is arguing that it must be rewritten in C++ to be fast.
It's not as simple as you put it though, unless you have an extremely strict list of tags containing no tags in which case it's trivial, but it's also of very little use.
Since your comment sounds overly arrogant to my ears I'd like to point out that by following the rules that you mentioned for making a sanitizer (DOMParser + drop non-whitelisted tags basically) you can only produce either a useless or a broken sanitizer, it's impossible to make a useful and working sanitizer that way. Proof-ish: either you drop <img> nodes, in which case you've made a pretty useless sanitizer in my book, or you leave it in, in which case you leave yourself open to XSS via stuff like the onerror attribute.
It's not trivial to write a sanitizer that both works and it's useful, almost no developers should need to learn all the nuances necessary for writing one, hence the platform itself should provide it.
So you allow img tags with the attributes you know and drop everything else.
And if it’s useful to have a prepared list of safe tags, why would that need to be provided by the browser?
> It's not trivial to write a sanitizer that both works and it's useful
While I would disagree with this claim, if it is true it is even more reason to not set this in stone by building it into browsers and instead provide a basic external library that makes the policy decisions that are useful to you.
And of course this is what is going to happen anyway, because you can’t count on this always being available, so you need to include a library that provides this functionality in JavaScript if the browser doesn’t. Which is trivial because this doesn’t involve anything JavaScript doesn’t have access to.
That's why you should be using an API that relies on the browser's parser.
Apart from performance this smells of not using a whitelist mechanism (I hope this is not the case).
What makes you think that? I just skimmed the draft and it seems to use a sensible whitelist as default. Developers can allow or deny additional elements/attributes as they like.
The browser knows if a certain piece of data will perform execution or not, as it is the software implementing the functionality. It is the correct app to ask, as it is the one being exploited.
Further, it's not a panacea. Defense-in-depth still applies, so server-side and other mitigations will still be appropriate. Build and use threat models to understand what is appropriate.
FLoC was a google project (this is FF and Google + library author), and it looked bad from the start.
> adblock was being removed from Chrome, which still works for me.
Adblock, in a way, will still work. Just even worse than now (where uBlock Origin on FF is better than on Chrome). The Manifest V3 change was postponed by google, currently [0] they plan to stop supporting V2 in January 2023
[0]: https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/
> In the meantime, we will continue to add new capabilities to Manifest V3 based on the needs and voices of our developer community. Even in the last few months, there have been a number of exciting expansions of the extension platform.
I have yet to see where Chrome is explicitly telling anyone they plan to phase out support for adblockers, nor where they are making it clear that is their intention. V3 is not yet completed, and is actively being worked on. If they actually do disable adblockers then that is a different story.
That was never their intention, not what the uproar was about, so it's to be expected that you're not seeing any evidence of it. The problem with Manifest V3 is not that it disables adblockers; not sure where you got that idea from.
The problem is that it severely restricts how effective they can be. Many of the things uBO does cannot be done in v3. That's what the uproar is about.
https://github.com/uBlockOrigin/uBlock-issues/issues/338#iss...
https://github.com/uBlockOrigin/uBlock-issues/issues/338#iss...
Why would they ever want to do that? It would be a PR nightmare if they came out and explicitly said "fuck you".
Instead they opted to take away capabilities from their APIs with the result of severely limiting adblocker capabilities, under the guise that this improves security and performance, which is not entirely wrong, but at the same time hides the fact that there would have been easy enough alternatives that preserve the capabilities of adblockers and some other API users while making sure the security threats they stated they are concerned about can be prevented[0]. Yet they didn't even really look at what was proposed and insisted on crippling their API in a way it cripples adblockers.
I can only conclude that improving security and performance is just one of the engineering goals of their solution, while the other (unstated) goal is to fuck with adblockers.
[0] They were particularly worried about extensions being able to intercept requests, examine requests and exfiltrate sensitive data. One way this can be easily solved is by adding a special sandbox for request blocking that has no accessible output to the extension or anywhere else (no backchannel, no access to the network or file system). You can load scripts (and data) into it, but it may only ever talk to the browser itself during request handling. This breaks the "exfiltrate" part.
V3 is finalized. And while they say they accept proposals for future changes, they already did not accept or even consider proposals in the timeline leading to V3.
>changes aren't being forced until 2023
They pushed the timeline back, because of all the push back they got. And also, the deadline for new extensions using V2 is Jan 2022, so a few months from now. So
>but there is still a lot of time for the new API to be revised and improve on the features you mention.
Not true either, you have until Jan 2022, a few months from now, to spec and implement and roll out such a revised or new API.
This is not going to happen. They had enough time to do all that when the issues were first raised, but didn't. They had enough time to do all this when the first serious proposals for better solutions were made, but they didn't. Why at this point I am wondering: will they ever?
Sure, the already established extensions will get a little bit of a longer grace period where things would theoretically happen. That doesn't help you if you want to create something new, tho.
>You can even make the suggestions yourself or work on the code to fix it.
Have you ever tried to get code into chrome(ium)? That's hard enough by itself. Now try to get code in that affects something google considers important... and they consider this important at least now, for the mere fact it was "news".
Trying to work with them is what gorhill did, and a lot of other people too, before he figured out they were set on going the cripple-adblockers route and sounded the alarms.