 How many people here write JavaScript on a regular basis? Very cool. And how many people here come from a security background? Anybody? All right, very cool. So hopefully I have a lot of content that's new for you then today. So my name is Mike. This is TreeStuff. We both work, or I used to work in Google Security Engineering Group. TreeStuff still does. And in Security Engineering, it's our job to provide the tools for developers to allow them to produce secure and robust software. So a lot of what we're talking about today is kind of how you can use some of the new tools that we're producing in your day-to-day work to make it easier to produce secure and robust software. And we're talking specifically about DOMXSS. So if you're using a modern client-side web application framework, DOMXSS is probably one of your biggest security problems. It's a kind of XSS that happens because the APIs that the browser provides to JavaScript to let you dynamically change the page are insecure by default. And so what are the moving parts that are involved in DOMXSS? Well, you have a source of some values from an attacker, and those reach a DOM sync. And so mostly the inputs to your system are going to be strings. So they might be strings that you get from a URL. They might be strings that you receive as data via JSON or some other method. And one example, you might look in a window lot location. You might grab part of that, and you might make some decision based on that. And if that substring then reaches a DOM sync, one of these powerful APIs like element.interhtml, bad things can happen. And so a string is just a string until part of your program decides to treat it as trusted code. And that's when it can do a lot of bad. It can take user data, phone home with it. It can present an interface that you did not expect to present to the user and trick them into doing things they didn't want to do. So for example, let's say our application is running on example.com, an attacker engineers a user into going to this URL. And then your code looks in location.hash, takes a substring, finds some element on the page, and uses that string as a string of HTML. That string then goes through the HTML parser. It creates an image. The HTML parser then sees this on error attribute, which then gets routed to the JavaScript parser when that image fails to load. That on error handler is called and causes a side effect. So this is the classic case of DOMXSS. And why is DOMXSS so prevalent in modern client-side web application frameworks? Well, there's two reasons. It's easy to introduce. You have to use these powerful DOM APIs to do your job. And it's hard to detect. Let's talk a little bit first about why it's easy to introduce. So DOMXSS syncs, there's a lot of them. We've got inner HTML. We've got source attributes. Each of these attackers have figured out how to craft payloads that let them subvert your program so that it's working towards their ends instead of yours. So the first thing, you can't do work without these DOM APIs. And all of these DOM APIs are vulnerable if an attacker controls the input. So the problem we have is that any code with access to DOM APIs can introduce XSS. And since the DOM APIs are global, that basically means any code in your application. So who can tell me, is this function safe? Come on. This is JS interactive. You guys can be a little more interactive. Go for it. Yeah, exactly. You have to know how it's called to determine whether it's safe. And so let's say we look through the code base. And the only place it's called is in this function G. This function G is passing document dot body as node. And it's passing Y as the value for inner HTML. Is this safe now? Exactly. We don't know where Y comes from. So let's say we look through the code again. And we find that the only place that G is called is here. And it's called with Z, which is the string hello world. So is it safe? Yeah, it's safe. And the is there is critical. So it's safe in the present tense. If tomorrow somebody were to add another call to the function F or G, that situation might change. So we could exhaustively review our code, connect these dots, and conclude that it's safe. But that judgment would only be valid for the current version of the code. And so the problem here is that we've got a use of F that uses a powerful DOM sync, uses inner HTML. But its security is tightly coupled with all of its callers, its present callers, and its future callers. This means that security review is really hard to do. So do you want to do that? Thank you, Mike. So is this really a problem, though? Does this lead to actual vulnerabilities in actual code? Yes, it does. So I work in the Google security team. One of my jobs is actually deciding on the rewards for the external people who report security bugs in our software, such that we can patch them and secure our users. And we are talking about millions of dollars paid yearly to research security researchers who find bugs in our code. It turns out that roughly half of the payments each year come to the researchers who find web bugs in our software. That's not really surprising, right? Google has a lot of, essentially, web properties, web applications. And accesses is by far the most prevalent bug from this web category, right? Cross-site scripting is responsible for the biggest payouts that we do as part of our bug bounty program. And DOM-based cross-site scriptings, which is the one that we are focusing on right now, is roughly right now 2 thirds of the payments, right? 2 out of 3 bugs that are accesses in, let's say, Google software are DOM-based accesses. So this pattern, these problematic things about the JavaScript APIs and the browser APIs does lead to real security issues in the code. But this is just our data. Other data sets still show that cross-site scripting for web applications is a super prevalent problem, unfortunately. Fortunately, though, we do know how to address this. In Google, as part of my team tasks, we were thinking for years now on how to limit that, how to minimize the possibility that tens of thousands of front-end engineers can introduce a vulnerability without even realizing that, because no one, obviously, wants to create a vulnerable software. And we have found a solution that is practically responsible for a significant reduction in the number of cross-site scripting bugs in our software. And this is a software, or this solution is mostly focused on server-side accesses, which DOM-based accesses is not one of. But this approach is very successful. And it's so successful that we think it makes sense to port it to the browsers themselves, such that also the client-side accesses can be protected through this manner. So we are trying to bake it for years now into the browsers themselves, such that the JavaScript on the web pages can use our methods of trying to protect the applications against this very bad, prevalent vulnerability type. And this is called trusted types. Trusted types are essentially data objects, are types that encapsulate not just the value, but also the security properties of that value. To give a very, very short introduction into this one, the core idea behind trusted types is to make the DOM API secure by default, which we think it is not right now. Because, for example, as soon as you start using inner HTML as a function, you most likely have just introduced a vulnerability if you ever put user-controlled value into that thing, as we call it. So we try to switch the situation to make the DOM API secure by default and strongly typed at the same time. We're relying on, well, JavaScript types, essentially, to give us those interesting properties. So once you start using trusted types in your application, the DOM syncs all those cloud of functions that Mike has shown you before, reject strings. You cannot call them with strings, with minor caveats that I will demonstrate later on. Instead, you have to create those typed objects that then can be safely passed to this function. And those typed objects are of different kinds, because we have different contexts of various values. So we have a trusted HTML object which you can safely pass to, for example, in a HTML setter or document write function. We have a trusted script URL object which you can pass to, say, a source property of a script element. And then we have a trusted script object which you can safely pass to, say, an eval function or something that directly executes JavaScript snippet. And once we have those types, we can put so many security rules and controls over it, which is exactly what we will demonstrate in a second. So let's look at how to use trusted types in the browsers. Yeah, so we have a file here, right? The way you enable trusted types in your application or enable the trusted types enforcement for the DOM things is by using a content security policy. Normally, you would set an HTTP response header, but just to demonstrate it in an HTML file, I just used a meta tag to do that same thing, right? So you have a content security policy. And there's this new directive, as we call it, require trusted types for script. This simply means I don't allow strings to be passed to those sensitive functions. I require a typed value. And just to demonstrate that this solution already has some teeth behind it, I will just use it in a Firefox loading a polyfill. We do have a native implementation in Chrome, but just to show you the demo on Firefox, right? So this is just loading the trusted type polyfill. And there's something. Don't look at it yet, the TT magic.js, right? So let's see this one in action. Nothing really interesting, right? There's a Hello World application. But what is interesting here is, as soon as I try to have this code in my application that, for example, does this, and this potentially could be an accesses payload, this doesn't happen, right? The page didn't reload. The new content didn't appear. We actually got a type error here, which says that, well, come on, with document drive, you actually need to use a trusted HTML value. And just to demonstrate that you can use a trusted HTML value with this one is let's call this magic function with some value. Don't look at the magic yet, but essentially yes, you can use the same still just with a different value type. And this is the very base demonstration of the API functionality. So let's go back to the presentation, right? Did we actually change anything for security? Because we're just changing the, let's say, the contract of a given function. We're changing the signature of it. But the very important change that is here, which is actually fundamental, is that when enabling trusted types and having the exact same code here, if we change this Z assignment to magically create a trusted HTML value, the safety becomes decoupled from the things, from those potentially dangerous functions. What that means that when looking at the security, when reviewing an application for security, I can totally ignore all the other parts of the code. Now, the only lines of code, let's say, only functions are the ones who create trusted values. These can potentially introduce something insecure, but the rest of the application code base is completely irrelevant for DOM accesses. And this is a fundamental change. Like we end up or we move from a state where the API is unsecured by default and very clunky to work with, let's say, to a state in which an action has to be deliberate to interact with this API. So to go back to the example that Mike showed, without trusted types, any code with access to DOM since it can introduce XSS, however, with trusted types, only the code that creates trusted types can introduce XSS, which is a nice property. But then again, I am a security engineer, right? And the way I am reading this statement is slightly different. I'm saying code creating trusted types can introduce XSS. That's a bad thing. I mean, I don't want XSS. So what can we do about it? Well, we can guard the creation of those trusted values. And we do that in trusted types API with objects called policies. Policies define the rules that convert the string values because in the end, you do read strings for various environments, some of them are totally controlled. And convert those strings into trusted values or trusted types. Let's see a demo. So second thing, not much has changed. We still have this required trusted for script directive in CSP. We have the polyfill, but now we call this specific function exposed on the window object, right? Every window now has a trusted types property and a trusted types property has a create policy function. And what we can do through this is well, create policies. So I am creating a policy named strip tags, which is very similar to what like, say PHP did with the strip tags function years ago, right? And what this policy simply says that this policy is able to create trusted HTML objects, but any input to it will be somehow transformed. In this particular case, I'm doing a very lazy HTML sort of sanitization, but not really, just neutralizing the value somehow, right? So I do some prefix and then I replace every opening bracket to an HTML escape of it. And let's see how that works in practice. Oh, and one more thing, this just is a JavaScript object. You create it assigned into a variable, you can use that variable wherever you like in your program. Yeah, so let's see it. Nothing seemingly has changed, but now we can use document.write and then strip tags policy, let's say something really bad, sorry, image source equals, I'm really bad at typing, sorry, on error equals alert. Oh, let's go one, three, three, seven, why not? Yeah, this will succeed because the policy creates trusted HTML values unless it throws, then it of course doesn't. No, I think I have them, right? Oh, yeah, strip tags policy is not a function, but it's interesting. Oh yes, of course, sorry. You need to call it's created HTML function. And there you go, the document.write was called, but actually was the transformation of the value, which correctly escaped the lesson character. What does it show us? Well, first of all, now not only the type creation is the security sensitive code, but we can put rules on top of it. We can define how the trusted objects can be created, adhering to which rules. And of course you can have seven different policies for different parts of your application with different, let's say, trust properties. And different rules can apply to different objects, right? So only policies by now can introduce DOM access in your application, which is a huge property. But then again, I read it slightly differently. Policies can still introduce access and this is clearly, clearly bad. So let's improve on this. Let's see how we can actually control the policies. Now, seemingly no thing has changed, right? We still have a content security policy. We have the same directive here, but there is something else here. We have a separate directive that is called trusted types and then a list of tokens essentially. And this one simply says strip tags. Well, this says that in my application, I'm allowing only the strip tags policy to be defined. Other policies simply cannot be created or policies with a different name cannot be created. So I can still use the strip tags policy that does something the same logic as before, but let's imagine you have something else in your application. Most of you already do something like that, which is loading some extra code, let's say something that displays an advertisement. And this advertisement is suddenly malicious. So what this does is it tries to, well, ha-ha, create its own policy that does no transformation whatsoever. And then supply XSS payload through that policy object, right? This is so bad. This is an intentional bypass, but still a pretty bad situation, right? But thankfully, trusted types protect against that. And once you load this, you can see that this policy, ha-ha, is disallowed. Your browser, well, in this case, polyfill, but your browser prohibits the policy creation. So you can't just sneak in additional policies here, which is desirable property, let's say. But also you cannot actually create a policy with the name that was already used. So what was it, strip tags, right? Strip tags, let's imagine this one empty object, yes. Strip tags policy already exists. It's already registered, right? So you can only use the policies in your application that already were somehow white listed, allow listed in the content security policy header. This one, right? So you give even more control over how policies get created. And with that, I will switch back to Mike. So without trusted types, any code could assign any value to anything. With trusted types enabled in, without any policy controls, any code can create a policy to create a value that will pass a sync. And with policy controls, only code that runs early enough to get one of these trusted names, one of these names in the security metadata can create a policy. And so this last dynamic really allows a security support team to support a application development team. So how many people here know about GitHub code owners? Wonderful, I think this is actually probably the most people I've seen aware of that particular feature ever. So code owners is an underrated feature of GitHub and what it allows you to do is loop in experts when certain files change. So you define a file that has a format kind of like dot get ignore. So you've got globs over paths in your code base. And then you can say for this glob, these people should sign off on any changes to it. And so what that lets you do is have the vast bulk of your code which can change however you like, but this small subset of files requires review by an expert. And so we've got trusted types. We're allowing my policy and my other policy and we're not allowing any other policies. Whoever controls this header gets to decide which policies are allowed and code owners can guard the files that control those policies. And so what that means is you're ensuring that your policy code is reviewed regularly. Next slide, next one. So policies at runtime, they're just JavaScript objects, but they're JavaScript objects whose code has been carefully reviewed. And you can use the usual JavaScript language features to define how other parts of the code interacts with them. So if you define them inside an immediately executed function, then they're only visible to other code in that function. That means that you can define a policy that would be unsafe if globally available, but is safe based on the callers that can appear in the scope in which it's defined. Chistoff gave an example of a policy that auto escapes. So that might be a policy that should be available more widely to a larger amount of code. So it's very flexible as you have a lot of flexibility as far as how you craft these policies and how you expose them. And then once you're doing the review, these are some best practices that we've kind of come up with. So you don't want to depend on global state. And when you're kind of making decisions about what to allow and what not to allow, you always want to be whitelisting, you never want to be blacklisting, looking for bad patterns when writing security relevant code. It's very hard to reason about everything, but it's much easier to reason about only. And I think when trying to craft a security mechanism, it's worth kind of explicitly stating what it is that you're providing. So what trusted types does is it allows you to make arguments kind of like this. So we, the application development team with our security support people, we're producing code that is secure against DOMXSS because DOM syncs only accept trusted types and only policies can create those types. And the security experts have reviewed all that policy code and hopefully they're good at their job. And we restrict policy changes to a small, not infrequently changing subset of the code. So those reviews can be thorough. So if it were the case that every developer was writing policy code and it were frequently changing, we wouldn't get much benefit. So we need to keep the amount of security sensitive code kind of isolated and small. And I worked in Google security engineering group for six, seven years. And I was heavily involved in improving tools in libraries. So I did a lot of work on things like template languages, sanitizers, and I was on a team that, we took rotations. So every couple of months I'd spend a week reviewing all of the changes to policy code across Google. So there was a team about 10 people supporting a development group that was, I think close to 10,000 engineers. Not all of them were involved in web application work, but a small security team was able to scale to a large application development group because the number of the amount of policies code was actually fairly small. Almost all of the policy uses are in some common infrastructure. And nice thing is that, the nice thing that trusted types does is we don't have to reason about where things come from. We don't have to reason about where they're going. A small security group can kind of focus all of their attention and a security engineering group can focus all of their improvement effort on this small core of policy code. And so what happens now is, code doesn't become vulnerable because somebody makes a mistake in application code. Kind of introducing vulnerability is, like what we saw after several years of working on applications like Gmail was most of the XSS's were my fault. Or somebody in a function like me, not the common application developer's fault. So, the system is flexible enough that it's possible to bypass. This policy code has to be correct. And this allows it to be a good migration target for a lot of applications. So we've built enough flexibility in here. If you've got an application and you wanna create a legacy policy that just converts things, get it working with trusted types and then later tighten that down, kind of remove the need for that legacy policy you can. And so, and it's also possible to bypass this system. So if there's some dodgy code that runs before the policy that defines the strip tags policy, and it uses the name strip tags, then it might run first. So you have to do a couple of things like make sure that the trust, the small amount of trusted code that defines policies runs early in your bootstrapping process. And I mentioned kind of tools and frameworks as key integration points. So a small number of policies account for almost all of the policy uses. Template systems, sanitizers and builder APIs account for the vast majority. And that lets a small team kind of work on rotations to review the remaining ones. So we have time to talk about integrations. Yeah. So we spent years kind of working on our own internal infrastructure to make sure that there's high quality libraries and policies available to Google code. We don't want the web development community as a whole to have to recreate that process. So we've been working on a basic set of integrations that mean that a lot of this trusted type stuff should actually be transparent to the bulk of developers. So Facebook has integrated trusted types into React. And I think you need to, it's in an experimental mode right now. So you need to set something in your React feature flags to enable it, but then React is aware of and respects trusted types. So just using React with trusted types turned on, unless you're doing something kind of odd that requires one of these safety hatches, it should just work for you. And tools like DomPurify.Sanitize can now be producers of trusted types. We know that DomPurify with a wide set of configurations is produces HTML that is safe because it strips out things like script tags and all the other dodgy parts. So we can trust, so when it produces, it uses a policy internally to create a trusted HTML value. And yeah, so if you want to know more, this is a link to go to. It has a lot of information about trusted types. Trusted types is available in Chrome now. Chrome has an intent to ship. So it's right now, it's hidden behind a flag, but it should be coming to Chrome by default soon. In the meantime, there's a polyfill. Firefox hasn't decided yet that hasn't committed to shipping yet. But you saw in the demo that we loaded a polyfill and that gave us the benefit of trusted types in the browser. And there's a mailing list that you can look at. The specification is currently before the W3. Yeah, so trustedtypes at googlegroups.com. We'd love to deal with your questions there. Or you can look us up on Twitter. So any questions? So the question was about integrating, well, essentially building an HTML sanitizer in the browser. Yes, there are plans of having that. It's not that easy because essentially it turns out that sometimes HTML sensitization needs to be customized to the application and the amount of the customization knobs is just like staggering. But Firefox, as far as I know, plans to start experimenting on something like that, Q1 next year, and we are also looking forward to that. We actually, yes, also are thinking that robust HTML sensitization is not an easy task and it's up to the web platform to solve it as well. Yes, you had a question, Rief? Yes, it's all about the example being too simple. So one thing that we found kind of in doing this is it's best to do the conversion as close to where we know why something is safe as possible. That usually means moving the trusted value creation earlier in the process. And so by the time this value reaches F, we've lost the context. By the time it reaches F, all we know about it is that it's stored in a variable named X. We don't know that it's a string that appears literally in the source code. So here we can reason, this string was written in the source code by a trusted developer, therefore we can trust it. But we lose that context once it's there. So it had to get back to the context of the application. Yeah, one of the nice things about this is by automatically checking at the syncs, that means and having a mechanism where explicitly making trust decisions explicit, we can move those trust decisions to where we have the most context.