 Welcome to one of the final talks of Google I.O. this time around and thank you for coming. My name is Artur Jans and I'm here with my colleague Lukas Weichselbaum. We are both information security engineers at Google and this means that our talk is a little bit different than what you might be used to seeing over the past couple of days because our main responsibility is not to build new products, features or APIs, but it's to safeguard the data that Google users have in our applications. And we do this by protecting our applications from main classes of vulnerabilities that might otherwise allow attackers to compromise our users' data. And this is what we want to tell you about today. We want to specifically, we want to tell you about four new web platform mechanisms that are adopted in web browsers that you can use in your applications to protect your users. But before we talk about the defenses, the mechanisms that let you protect your users, it helps a lot to talk about the potential attacks and the classes of vulnerabilities that might allow attackers to attack your apps. So our talk is divided into two parts. First we'll talk a little bit about the attacks and the vulnerabilities that your application might have to deal with. And second, we will talk about the defenses. So let's start with the vulnerabilities. At Google, we have run for over the past eight and a half years reward programs under which we pay security researchers money for disclosing vulnerabilities to us. You actually don't have to be a security researcher. Anyone who finds a security flaw in a Google application can be rewarded. We the rewards range from $100 to up to $30,000 per bug. We have three main reward programs. There's one for Chrome. There's one for Android. But the one that we will focus on today, we just call it the Google VRP. That's the one that covers almost all of our user-facing products. So that includes web applications, server side infrastructure, Chrome extensions, mobile apps, hardware, and so on. Across all these programs, we have paid over $15 million since the launch of our VRPs. Under the application security, the Google VRP, we pay roughly about a million dollars per year for hundreds of vulnerabilities across thousands of applications. So what are those vulnerabilities? Let's talk about the details of the bugs that we're seeing. So if you look to the left, there are definitely issues that are not related to the web. Those are issues in server side logic, in authorization, in mobile apps, and so on. And we're not going to focus on these. But if you look closely to the right side of the graph, a large number, actually the majority of the vulnerabilities that we see at Google, both when it comes to the awards that we give to researchers and the raw number of bugs that we get, the majority of those bugs are web issues. So flaws that allow an attacker to attack users that are logged into our services and extract or modify some of the data that they have. So let's focus on these web security vulnerabilities. And afterwards, we'll tell you about how you can protect your applications. So there are two main classes of flaws that we have seen on the web over the past two decades. The first one is cross-site scripting or, in general, injections. And the way cross-site scripting can happen in your application seems fairly benign. So there are many different patterns that lead to XSS, but the two most common ones are if you compose your server-side responses with any user-controlled data and you don't specifically escape that data, that leads to cross-site scripting. Similarly, if you use any of a number of JavaScript APIs that are unsafe by default and the data that you pass to these APIs, or, for example, the inner HTML assignment on the slide here, that also results in an XSS. And usually in your applications, these vulnerabilities will not be as obvious as these examples. Usually you would have a variable that might contain user data, or at some point in the past it didn't, but then code got modified, and now it leads to an XSS bug. So it's not very easy to spot these vulnerabilities when you're writing code, but it's quite easy to introduce these vulnerabilities. And those are just two examples. There are many other ways to unwittingly introduce XSS into your app. And what an attacker can do when that happens is they can, when a user that is logged into your application visits the attacker's website, the attacker can navigate that user to your application with the cross-site scripting payload, usually in the URL. And then when your application server responds to that user's request, it will contain the script that is injected by the attacker. And the user's browser will then get confused. It will think that the script that it sees comes from your application, and that script will execute with the full privileges of your application. It will have complete access to the user's session, to the user's data. The attacker will be able to read all of the user's data, to modify it, and to do anything that your application can normally do, which is very scary. The second class of, broad class of vulnerabilities that we see is related to insufficient isolation on the web. So the web has many gaps in many ways in which applications can interact with each other, and there are no very strict boundaries that protect applications from interference by other apps. A common type of vulnerability in this category is cross-site request forgery. So if you look at the example on the top, this is just a legitimate form that would let me transfer $10 to Lucas. It's perfectly fine. But unless you take specific steps to protect your application against cross-site request forgery, what an attacker can do is create a form like the one in the example below, which also interacts with the same application server, and in this case, the attacker will make me send infinity dollars to an attacker-controlled account. And the root cause for this is that when the browser sends a request to our application server, that request gets made with all of the cookies of that application, regardless of who actually sent that request. So the server cannot easily determine where the request is coming from, which means that attackers might be able to confuse the server to either take action as the logged in user, or maybe exfiltrate, steal some of that user's data. And this is just one example. There are many types of issues that are based on the same premise that are caused by the same lack of isolation. For security engineers, this is really exciting. We like to analyze these kinds of bugs. But for the purpose of our talk today, it's enough to know that this class of issues is growing. Unfortunately, there are new web APIs. There are changes to the web security model that make these types of vulnerabilities more likely to occur and also more damaging. So we are worried about them, and we'd like to comprehensively protect against them. So now you might be thinking, oh, this is just a Google-specific thing, and other applications on the internet don't really have this problem. But in fact, if you look at the data that other application providers have published, and this is data from Hacker One, which is a bug bounty provider, which collects vulnerabilities for thousands of companies that have signed up for Hacker One, and then let researchers be paid for them. If you look at data from external application ecosystems, you can see that they have the exact same problems that we see across Google. So let me zoom into the six most frequent categories of vulnerabilities that Hacker One has reported for all the companies that use their service. And you can see that the most common ones are also these endemic web bugs. That includes cross-site scripting, cross-site request forgery. The ones, the categories in dashes are the ones that might be web-related and might not. But there's a very large number of these across their customers as well. And finally, there is data published by our friends at Mozilla that also shows the exact same trend. So if you look at the payouts that they have issued from their reward program, it's also a large part of it is for cross-site scripting and some of these other web issues. OK, so now you know what the most common web vulnerabilities are. And we've seen that they are divided between injections and bypassing isolation on the web. So let's talk about the defensive mechanisms divided into injection defenses and isolation mechanisms. And to talk about injection defenses, I'll invite my colleague Lucas to the stage. Thank you, Arthur. Hello, everybody. My name is Lucas. And I'm really excited to show you a couple of very promising new web app firm features that allow you to defend your application against these injection attacks, especially cross-site scripting, which is a huge issue across the industry. First, let me start with content security policy. Content security policy is, if configured correctly, a very strong mitigation against stored and reflected cross-site scripting vulnerabilities. So content security policy, also called CSP, allows the developer to very fine-grained, specified which scripts are allowed to execute on your site. And it also allows the developer to specify which plugins can be loaded. There's a couple of more things you can do with CSP. But these two properties are the important ones for the cross-site scripting defense. And there's one thing I really want to stretch here, and that content security policy is a defense-in-depth mechanism. So it's a safety net in case any other primary security features you have in your application go wrong, right? So even if you do escaping or if you have a very hard application, from time to time, a bug can slip through. And then it's great if you have a content security policy that actually can protect your users from the consequences of that cross-site scripting vulnerability. So how can you enable CSP on your site? That's actually quite easy. CSP is an HTTP response header. So if you send the header with the name Content Security Policy on the response, the browser will pick up the header, read the policy you have specified, and then it will apply that policy to the content of that response. So it's a client-side security feature. And CSP also supports a report-only mode, which is very handy if you test CSP on your site for the first time, and you want to make sure that nothing breaks when rolling out CSP. So if any one of you has used CSP before, you might have seen a content security policy like that. That's a host-based whitelist policy. And as you can see, they can get pretty complex. They're very hard to maintain. And even worse, they're usually bypassable. So we really don't recommend to use these anymore. And unfortunately, these are still the type of policies we see mostly in the world. But there is an even better way to do CSP. We usually recommend to use a non-spaced CSP. The huge advantage of this type of policy is that you don't have to customize it for your application. It's basically always this policy, just the non-svalue changes, which we'll show you on the next slide. As a matter of fact, we have rolled out this type of policy on over 200 Google applications. And just in the last 12 months, we were able to mitigate over 20 cross-scripting vulnerabilities on a various of Google products. So it's actually quite effective. So what is the idea behind a non-spaced CSP? As the name already says, it's about nonces. And nonce is a random token that is meant to be not guessable. And very important in the case of CSP, this value changes for every response. So in order to allow a script to execute on your website, you must make sure that you set a nonce attribute on every script tag, and the value of this nonce attribute has to match the value of the nonce in the content security policy. And only if that's the case, the browser, the user agent, will allow the script to execute. So the huge advantage of this approach is if you have a cross-scripting vulnerability on your site, the attacker can still inject the script tag. But since the nonce value changes on every response and it's not guessable, he will not know the nonce. And the browser will block the execution of that script tag right away. And by that, mitigating the consequences of an excess vulnerability, and by that, you can keep your users safe. So there's one issue with that approach. If you just use a nonce to bless scripts, you might run into issues. If you source scripts from CDNs, if you have any third-party JavaScript code like widgets or payment integration, because in order for a script to run, we have said every script tag has to have a nonce attribute. And if these script tags now dynamically create script tags on the fly, then these script tags must have a nonce attribute as well. So if you control the JavaScript, that's fine, because you can refactor this code. But if the JavaScript is hosted somewhere else outside of your control, these things will break. So although nonce-based CSP is around for a while, it really didn't take off in the past because of this problem. So thankfully, with CSP3 in the web platform, a new keyword was introduced, which is called strict dynamic. And the mechanism is really simple. The only thing it does, it allows already trusted scripts, like scripts that have a nonce attribute, to execute code normally, and to also load child scripts without having to explicitly set the nonce attribute on that child script. So now, even if you don't control all the JavaScript code yourself because it's hosted somewhere else, you will be able to use a nonce-based CSP. And that really unblocks the entire approach. So what do you have to do to roll out the nonce-based CSP on your application? It's three simple steps. You start with removing CSP blockers. You have to do this because a strong CSP usually disables a couple of dangerous patterns in HTML. And in order to not break your legitimate functionality, you have to refactor your site in case you use these patterns. The most common patterns are inline event handlers and JavaScript URISE. Thankfully, it's quite easy to refactor both of them. For inline event handlers, you just remove the event handler from the tag. And instead, you register the event programmatically through JavaScript API, for example, with the add event listener call. JavaScript URISE, if you're like a no-op, like in this example, you just remove them. Or if there's more logic, you can also refactor them in an inline script. Step two, that one is very important as well. You basically have to make sure that all the legitimate scripts on your site have the nonce attribute set. Otherwise, the browser will block the execution. Usually, you just update your server-side templates to also include the nonce attribute. And since the nonce is generated on the server anyhow, the server knows the nonce for a response, put the nonce in the script tag, and also use the same nonce in the response header for the content security policy. If you can use a nonce-only CSP without strict dynamic, because, for example, you don't have any third party JavaScript dependencies, you also have to propagate the nonce to the dynamically created script tags. That can be done through the set attribute method. And last but not least, you have to enforce the content security policy. That is essential because without the response header, the browser will not enforce it, right? So you have to set the content security policy string. We usually recommend to use this policy. That is the policy we use in most Google applications. And it's actually quite easy to adopt. But if you can, I would recommend to remove ansif evil. But then you also have to make sure that your application does not use JavaScript evil. And in case you don't have third party code dependencies in JavaScript, you can also try to use a nonce-only CSP because this one offers the most security properties. But it's usually quite hard to adopt. So there's a couple more CSP adoption tips and tricks. For example, how you can use a CSP for static applications like single page apps. In that case, you have to use CSP hashes instead of nonces. How to improve the debugability of CSP violation reports. And also how you can use fallbacks for all the browsers in content security policy. So we don't have time to cover all of these in detail in this talk. But we collected a couple of these tips and tricks at csp.withgoogle.com. And in case you didn't memorize all the previous slides, there's also a guide on how to deploy CSP on your website on that side. And very important, in case you diverge from the CSP templates we showed you here, like the strict CSP we had at the beginning, make sure you run your CSP through the content security policy evaluator. Because it's actually quite easy to introduce bypasses in CSP. And that tool will basically show you if you have any bypasses. So if there's a red exclamation mark, it basically means your CSP can be very easily bypassed. And you probably should do something about it. Yes, the tool is free and open source. So yeah. So it's a summary. The really nice property about non-space CSPs is that they are always the same. You don't have to configure it to fit your application. That's a huge benefit over the host-wise-based CSPs. And they're also more secure than the widely-spaced approach, because they don't suffer from widely-spaced bypasses. And a non-space CSP is, as we said, a very strong mitigation against stored and reflected cross-sets scripting vulnerabilities. And thanks to the new strict dynamic keyword in CSP3, you can now use a non-space CSP even if you have a modern web application that sources JavaScript from various places, right? So you now know how to defend against stored and reflected cross-sets scripting attacks. But what about DOM-based accesses? How can you defend your application against these? For that, I will introduce it to trusted types. But first, let's talk about DOM accesses one more time. So DOM accesses usually is a client-side cross-sets scripting vulnerability. So it all happens in the browser. Usually, there's not much server-side interaction involved, although there's variants. And very often, the root cause is that the user-controlled string is converted into code. And in the case of DOM-based accesses, that usually happens through dangerous DOM APIs, like inner HTML. We have a very small example of DOM-based accesses on the slide. In the first line, you can see that it reads out the URL fragment, the pink part on the slide. And then in the second line, this string, which is controllable by an attacker, gets assigned into the inner HTML sync. And this is where you introduce a DOM accesses vulnerability. And that's actually a huge problem, because for developers, it's very easy to introduce this type of accesses by accident. And at the same time, for security researchers, it's quite hard to spot these accesses vulnerabilities, because there's over 60, and the number is growing, dangerous DOM APIs that can cause this type of vulnerability. The slide just names the most known ones, but there's a really long list of these. And to make things worse, usually, the source and the sync for the DOM-based accesses are usually quite far apart, right? Whenever you assign a variable to an inner HTML, for example, it's really hard to find out, is this a user-controllable string? Is it a constant? Was this originally from a database? Is it sanitized? It's really tricky, right, if you have a larger application. And the problem is becoming worse, also because more and more web applications are written on the client side, like modern frameworks really foster client-side code. So we really need a mechanism to defend our applications against these type of vulnerabilities. So for that, we have trusted types now. And the basic idea is rather simple. Instead of assigning strings to dangerous DOM APIs, you only allow assignments of typed objects. Let me give you an example of that. If you, for example, want to assign something to the inner HTML sync, you will not be able to assign a string anymore. You will have to use a trusted HTML object. And that by itself is already quite a nice concept that can be enforced by a compiler, a linter, a pre-submit check. But trusted types goes one step further, and that's the really nice part of it. It can also be enforced by the browser. If you set a content security policy with the trusted types directive, it will basically instruct the browser to start disallowing a string assignment to any of these dangerous DOM APIs. So the previous example we had for the DOM-based cross-scripting vulnerability will stop working because that's a string assignment. And instead, you have to use a typed object as a developer. And that object can only be created through a trusted types policy. And since it's delivered through content security policy, there's also report-only mode, which is great for testing your application with trusted types without breaking anything, right? So instead of blocking the string assignment, it will still be allowed, but there will be a console warning. And if you specify a CSP report UI, you also will get CSP violation reports for these assignments. So the question is, how do we create a trusted type object? And that works through the trusted types API, which is either supported by the browser, or if it's not supported, you can also install a polyfill. And you have to invoke the create policy method, specify the name of the policy, and the policy function. In our simple example, to in order to create a trusted HTML object from a string, we run the string through a custom sanitizer. But for example, you could also have a white list of things that you want to allow or any other custom logic. And then to assign this trusted HTML object to an inner HTML, you first have to invoke your policy called create HTML, pass in the string that you want to convert into a trusted HTML object, and then you can assign this to the DOM sync. And if this is enforced through the content security policy, the browser will only allow this type of assignment in your application. One thing to point out here is that you also have to list all the policies in the content security policy in the trusted types directive that you want to be allowed to create these trusted types objects. There's also a special case, the default policy, which if you register a default policy, string assignments will not just fail. They will basically be piped through the default policy instead. So if you assign a string to inner HTML, instead of just blocking it, the string will be piped through the default policy. And then here you can specify custom logic. For example, you can have a sanitizer for all these assignments, or like in that example, you can just log the string assignments in your application. And this approach is very nice because it basically lets you find all the insecure string assignments in your application. And you basically then can go through the list and convert these into trusted object assignments. So as a summary, trusted types heavily reduces the attack surface of your application because it kind of aligns all the data flows into a very specific way. Whenever you want to assign something to an interest home API, the data has to go through a policy. And then you can get a trusted type object which you can assign to the DOM sync. And the very interesting part here is all the security critical code will be concentrated in the policy instead of like across all of your application, which dramatically minimizes the trusted code base and heavily simplifies the security reviews for the application. And yes, so you can also can have compile time checks for trusted types. You can also add linter support, pre-submit checks. But what you can do also is like the runtime checks by setting the trusted types content security policy. You basically instruct the browser to enforce this behavior also in the client side. And that's a very strong security validation. So if your policies are secure and access restricted, you will be able to get rid of probably all the DOM access vulnerabilities in your code base, which is really nice. Currently trusted types are available as Chrome origin trails, but they can also be poly-filled. The poly-filled is available on the trusted types GitHub page. The same page also has a very good explainer, couple of instructions, and I really recommend checking that out. So if you're not tired after IO when you get home today, you can try a default policy, log all the insecure DOM assignments in your application, and yeah, give trusted types a try. So wrapping up the injection defense section, you know how to defend against stored and reflected XSS with a non-space CSV. You now also know how to defend against DOM-based XSS with a trusted types policy. And the nice thing is these two technologies fit very well together. They can even be set through a single content security policy. And together, they really prevent and mitigate the vast majority of XSS vulnerabilities in the application. And yeah, I hope this was useful for you. And with that, I hand back to Artro who will tell you more about upcoming new isolation features in the web platform. Thank you, Lukas. Thank you. So the security mechanisms that Lukas just excellently presented to you will go a long way towards protecting your application from the most common high-risk bug cross-site scripting. But there is a little more to web security. And let me cover some remaining features that will give you isolation for your application to cut down on some of the other high-risk bugs that we've seen on the web. Before we start talking about the defenses, I want to quickly show you the two different types of isolation bypasses on the web. The first one, and this is something that you've already seen at the beginning of the talk, is with the CSRF example, the first class of attacks is based on the fact that the attacker can make requests to your application, to resources of your application that will be sent with your user's cookies, but will be embedded into the attacker-controlled page. And when that happens, an attacker can usually leak some information about the resource or about the response. For example, depending on the amount of time that your server takes to respond to the request, the attacker might infer something about the data. The second kind of attacks is a little different. It relies on the attacker opening a new window to your application. And obviously, after another website opens a window to your application, it cannot directly read the contents of your site for a logged in user. But there are other things, and I'll show you in just a moment, there are other ways in which it can interact with your application. So those are the two main attacks and the two security mechanisms that we'll talk about address each of them individually. The one thing that we have to cover before getting into the defensive mechanisms is the concept of origins and site. So can I get a show of hands from you guys just to wake you up? Who here knows what an origin is and what a site is? If you know, please raise your hand. Wow, great. You guys are awesome developers. For those of you that might not know this, security researchers talk a lot about origins. When there are two different documents or two different URLs, they are considered to be same origin if they have the same scheme, host name, and port. And for all intents and purposes, same origin URLs are considered to be the same security context. So essentially, this is a shorthand for saying this is the same application or applications with the same set of security privileges. Two URLs that are not same origin but share the same scheme and registrable domain. And the registrable domain is just something that you would pay your registrar $10 per year to maintain. If you have two documents from the same registrable domain, they're usually considered to be same site. So that doesn't give them the same security context, but it implies that there is some trust between them because usually they belong to the same organization, for example. And finally, if URLs are neither same origin nor same site, they're cross-site. And that implies that there is no trust between them by default. All right. So now we know all of this, so let's talk about the defenses. The first one, the first defense I want to tell you about is called fetch metadata request headers. It sounds super complicated when you read the name, but the mechanism is actually very simple. The main idea of fetch metadata request headers is to add a little bit more context to HTTP request that the browser is sending to allow the server to make security decisions. So in practice, what this means is that browsers are starting to send HTTP request headers that provide some of this security-relevant information that the server can act upon. So for example, the sec fetch site header tells you which site made the request that ends up in your application. If it was your own application, the value of that header will be same origin. If it was some other completely unrelated website that made the request to you, it will be cross-site. The sec fetch mode header roughly corresponds to the type of the request. And that mainly lets us differentiate from between sub-resource requests and navigations. There's also a header that tells you if there was an explicit user interaction that led to a navigation, which is also useful in some security context. So in practice, what this means is the servers will start getting these new headers and will be able to differentiate between same origin requests. So on the top, you have a request made via the fetch API from your own application to your own application server. And then the header will say same origin, which means that your application server can easily allow it, consider the request to be trusted. If an evil web page that's cross-site to you makes the same request, the request to the same URL, the browser will tell the server, oh, this is actually a cross-site request, which means that the server will have a chance to reject it. In the past, without fetch metadata request headers, these two requests would have been identical, meaning that the server will have no way of distinguishing between them. And that lets you, this is very simple, but it lets you write very powerful security logic that protects against most of the cross-origin attacks on sub-resources that we have seen in the past. So what you have here is a very simple module that allows requests that are same origin and same site, but bounds cross-site ones. And what the module does is, first of all, it allows all requests without the headers for backwards compatibility. Then it checks if the request comes from a trusted site. And if it does, then it allows it. Finally, the one last condition it has is for requests that are cross-site that could potentially be dangerous. It checks if that request is a navigation, because we still want to allow cross-site links for users to be able to end up in our application by following a link from another site. And this is essentially it. You can adopt these protections based on fetch metadata headers by implementing logic like the module you saw on the last slide. First, you would probably implement it in reporting mode without enforcing the restrictions. And then after you fix any parts of your site that might not be compatible, you can start enforcing the logic. This feature is already implemented in Chrome. It's shipping in the next stable version. Right now, you can already use it behind the flag. One other thing that is related to fetch metadata request headers is same-site cookies. You might have seen an announcement a couple of days ago about some changes to how Chrome will handle cookies in the future. The gist is that fetch metadata headers help you test the deployment of same-site cookies in your application. The problem with same-site cookies is that while they are very powerful and very useful for security, it's a little difficult to know what the consequences are of setting the same-site attribute in your cookies. And with fetch metadata, you get the information that lets you see if your application is fully compatible. So use these headers to test your same-site cookie deployments. Finally, we've arrived at the last security mechanism that we want to tell you about today. And that is something that protects our windows from being referenced by cross-site pages. And the reason we need to do this, and this is what I promised you a couple of slides ago, is that when an evil web page opens a window to your application, it holds a reference to your window. And with a reference to your window, it can do several things that you might not expect and that were always surprising to me as a security engineer. So for example, the evil web page can send messages to you. It can navigate you to an attacker-controlled site. It can also look at the number of your frames and iterate over these frames. So if your application, for example, adds an iframe if a user is logged in, an evil page can immediately know if a user is logged into your site, which is not great. And the way to protect against this is to set the cross-origin opener policy response header to either same origin or same site. And that will cause windows that are not same origin or same site to lose the reference. Even if they open a window to your application, they will no longer be able to do these things that were shown in the previous slide, which is a very simple but also a very powerful security primitive. And if you adopt the cross-origin opener policy, another thing that browsers can do is, even if they don't have full site isolation, they can put you in a separate process also protecting from some speculative execution attacks, which is very useful. All right, so this is my final slide. If you have been watching the live stream for the Google X talk over the past 40 minutes, just take a picture of this slide. This will tell you everything you need to know. We have very powerful security mechanisms in the web platform that are arriving this year. And it's the first time that the web platform has these security capabilities that let you protect against the large number, the majority of the vulnerabilities that we've seen on the web before. There are protection against injections, CSP entrusted types, and there are isolation mechanisms, such as fetch metadata request headers, and the cross-origin opener policy. And if you adopt them on your sites, your users will be much safer against most of the vulnerabilities that might affect web applications. Thank you very much. Lucas and I will stick around after the talk if you have any questions about these mechanisms or other security issues. Thank you. Thank you. Thank you.