731

February 16th, 2024 × #security#javascript#css

Client side security, XSS attacks & CSP with Stripe’s Alex Sexton

Alex Sexton from Stripe discusses CSP (Content Security Policy) and client side security best practices, drawing on 11 years of experience at Stripe.

or
Topic 0 00:00

Transcript

Scott Tolinski

Never talked about CSP. It's not in any of our show Node.

Topic 1 00:26

This is the first time CSP has been covered on Syntax

Scott Tolinski

So that's really good. And, you know, you know what else we don't have, Wes? What? We don't have any bugs in our code because we use Sentry.

Scott Tolinski

And, Sentry is logging all of our bugs and errors, and we get to find them and fix them. That's actually not true. We do have some Sentry errors, and we are actively working on fixing them. So, hey, if, that sounds good to you, head on over to century.ioforward/ syntax and give it a try. You can get 2 months for free. Alright.

Scott Tolinski

Let's get going.

Scott Tolinski

Alex.

Guest 1

you wanna give yourself a bit of introduction about what you do and who you are? Sure. Yeah. I I live in Austin, Texas, and I mostly do, you know, front end web development.

Guest 1

But yeah. Yeah. Moece and I met, in in the jQuery IRC channel doing support, and and we got pulled into this, like, you know, cabal back Channel, of people who answered the questions instead of asked them.

Guest 1

And I think we used to conference together and stuff like that. None of that came Yaykuri with Paul Irish and Rebecca Murphy and and Adam Sontag.

Guest 1

And that was really fun, but it was a lot of work to do a video podcast. And so, you know, we stopped.

Guest 1

We all got we all got jobs. Eventually, you know, you know, that was from J. Crew. We we then worked on Modernizr together, and then I started working at Stripe in 2013. So, I've been there about 11 years Node, a little under 11 years. I I think I Wes, like, I was pretty relevant, but they they had already launched and, and, you know, we're we're a product by then, so I I can't take credit for, super early stuff. But at this point, I am the 4th most tenured person, at Stripe, remember Wow. Which is which is fun. Who's above you? The the founders? Yeah. Yeah. The founders and then 1 one person.

Topic 2 02:30

Alex has been at Stripe for almost 11 years, fourth most tenured

Guest 1

It's mostly, like Like, I'll clean the dishes, after they have lunch and then, and then the the, I guess in the beginning, they hired, kind of only kind of full stack. Everyone does everything.

Guest 1

But very quickly, I found myself in in front end, roles there. I I wrote Stripe JS v 2,

Guest 1

You know, that's one of the things.

Guest 1

Well Well, I I scooped over some details. There are, I think, 3 versions of Stripe JS v 2. I wrote the one that is still out there. But one of the purposes of writing Stripe GS v two. A 3rd time was new PCI rules. And so we essentially had to make a library that worked exactly the same before 2 users, but worked very differently under the covers, and that was kind of, what I did. Now there's Stripe Wes v three and elements and stuff like that.

Guest 1

So I wouldn't necessarily Suggest running my my current code.

Guest 1

Yeah. And then I worked a lot on the dashboard over the years, so dashboard's probably been my my primary work. Of the bigger projects I've had on the dashboard warp been, internationalization. I don't know if y'all have done I'm sure you've done some internationalization episodes, but that was kind of my project from start to finish. Like, to internationally, most of internationalizing most of Stripe was my my personal kind of singular project. Eventually, I got, 1 1 manager. And then once it was kinda live and successful, they're like, alright. We'll we'll put a team around this. And so that was actually it took a lot of advocacy and stuff like that, to to get that across ESLint. So I'm proud of that, but also, like, now there JS a team of people who do great work, and I can't take any credit for then I worked, across, our design systems team for the last probably 6 years up until about a few weeks ago. And so, Stripe is an internal design system we use across, let's say, most of our products.

Guest 1

And, me and a a band of heroes, worked on that, for a long time, and and that's been the primary work I've done since then. That's cool. That so many of those things are

Scott Tolinski

very impressive projects on their own.

Scott Tolinski

I the the Stripe dashboard has long been, like, one of my big inspiration seeking places. Like, if I wanna see, oh, how do I do a dashboard? I almost always go to Stripe And look at the Stripe dashboard before proceeding because there's so many nice lessons there.

Scott Tolinski

So yeah. Bravo to everything that You and the entire team have been doing because it works really Wes. It's it's fantastic.

Topic 3 05:09

Stripe design system started from Bootstrap, became React and CSS based

Guest 1

Yeah. So I'd say the design system started out of a project that I that I was writing in 2015 maybe, called Bootstrap.

Guest 1

And for a long time, the company JS Bootstrap, I have a talk on it from JSConf Argentina, I think, in 2015 or so, where I talk about what Bootstrap is. But it's mostly CSS classes. Right? It's, it's like a BEM style. I think you used suit CSS at the time. Right? It it proliferated to the point where they're like, man. We better, like, build a team around this, and maybe add some JavaScript bindings and things like that as things were moving to React. But at the time, like, when I wrote it, we weren't on React. We were it was still, you know, backbone CoffeeScript days. As Bos days. As the team went around, it became a React based library. And, over the years, the CSS kinda fell out. And so, the the project called ESLint internally.

Guest 1

Theoretically, there was a, like, a backronym added, but let let's just say, that it's just named SAIL. A few years into writing SAIL, the SAIL JS kind of very much, extra and and if anyone's like I don't know. I I don't I don't have a lot of advocacy on which way you do it, but I've now done it both ways. And there are pros and cons to, like, Pulling a design system out of an existing product, versus, like, building 1 from the ground up. And there are a lot of, like I mean, if I could do both at the same time, that'd probably be the best thing. But so SAIL, was very much, like, extracted from the dashboard and then, you know, turned into these more generic, you know, better tested, independent, components and things like that. And, you know, designers were added into the process, so the the components are actually good. And then maybe, like, 3 years ago, we set on a project To rewrite kind of from the ground up. So we have Vercel classic and SAIL next. And so when you go to the dashboard, you're getting both. It's kind of like actually still getting Bootstrap too. So in the in the dashboard, you'll hit some pages that are very old that are using CoffeeScript backbone and Bootstrap, and then you'll get some pages that use So classic and some and, you know, even, like, the same Node might have a cancel button that's so classic and one that's Cellnex.

Guest 1

But one of the the key things that we wanted to do in Cellnex is It's add, like, the ability to do, like, theming, even especially, like, even local to subsections of the tree really efficiently, Because we wanted to use it across more products. You know, we want, our link product to be able to use it. We want, connect products to be able to use it. We want, like, branded customers to be able to come to us and then, you know, generate a checkout form that looks like their brand, but it's using our design system under the covers. So we can run it in, like, IBM carbon Node, or we can run it in all these different modes we don't spend a lot of time on. This is kind of the a fun demo. And because of that, like, we can make it look a lot like Sail classic. So we kinda have, that one's like CSS and JS React.

Guest 1

It's React ARIA Bos. It's maybe, the the big thing you wanted to know there that we didn't Awesome. Yeah. Waited till the end too. I'd say that, like, if you're I don't know. This is gonna I'm probably gonna get some comments, but, like, I have tried all the, like, headless and the I don't know. They're all amazing and all that kind of stuff, but, like, nothing is as, like, mature as Reactaria Yep. Or even close.

Guest 1

The other ones are super fun and super useful, and probably it's all you Node. And I'm not saying anything bad about them. I use them I use Radix in projects and and, like, Pedro and those guys are, like, absolutely fantastic, developers, etcetera, etcetera.

Guest 1

But, like Mhmm. I think that Reactaria has a level of maturity because it has probably Adobe money, behind it. And Devan Gavitt JS a is a hero.

Guest 1

That's Node I'd say I'd say we're very happy with that choice even though, like, you know, there's downsides To, like, not controlling a 100% of your own destiny sometimes.

Guest 1

Scott. Totally. And what about the Bos app? Is that a a totally separate thing? So we don't have Bos engineers on our, design systems team, and it is a native iOS app that we have. But we do work closely with the iOS team for them to pull in, like, assets and tokens. So, like, they very much use the design system from a systems sense, especially like, like, they use, like, Figma assets and all sorts of things. And they have, you know, custom overrides for for Wes. There's not a lot of code sharing. There is some code generation. Like, some of our code gets generated into stuff they can directly use, especially when it comes to, like, colors and tokens and things like that. And so That's cool. So there is a crossover, but it's pretty light. And it's, you know, pretty manual each time they wanna use a new part of the system. It's like with the there's a lot of, like, you know, talking needed to happen. That's super interesting. Thanks for sharing that. Yeah. Super interesting.

Topic 4 09:50

Overview of what Content Security Policy (CSP) is

Guest 1

Sure. Well, I think Pnpm and ESLint Stripe is, I'm not even sure necessarily. There's, like, Stripe Node and and Stripe bindings to our API, and then there's Stripe. Wes. And I have worked on all those at Some point, but not a lot of them recently. So, there are rules. Anything I tell you about PCI rules, I'll be Slightly wrong, and some will be mad. But, yeah, roughly, like, you don't wanna be hosting the thing that facilitates credit card tokenization directly on your own server. Part of the rules is that it needs to be served from, you know, something that has, blah blah blah. You you know, PCI controls.

Guest 1

And so there's that Yarn. But, you know, CSP CSP is one part of client side security, and maybe we should branch out into into talking about more than just CSP Sure. As part of this.

Guest 1

But, like, there are there are parts of, like, security specifications that I think could solve for this, but, like, the PCI organization isn't necessarily known for being, like, on the cutting edge of, like, what browsers can do and stuff. So you could see something like subresource integrity, which, like, verifies that TypeScript that you downloaded from any server anywhere matches the hash of the content that you expect.

Guest 1

And so theoretically, like, you could imagine Stripe providing the hash of Stripe Wes in the Node JS package and saying, feel free to host this yourself. And and I think that'd probably be about Secure and and that could work out, but I don't think that that's part of the rules.

Guest 1

Actually, there are some PCI 4 stuff with sub resource integrity. Like, they're they're trying. It was just like, you know, that's been out for 5 or 6 years now. Oh, yeah. Yeah. That because

Guest 1

Yeah. I think you might have to import them in, like, the The tag way instead of the Mhmm. Instead of in in syntax.

Guest 1

But I don't Node. I don't know the support for that. Yeah. There are there are CSP ways on top of this. Right? So, maybe we should I don't know. Maybe we should back up. Do do people should we talk about what content security Policy is? Should what what's the level of knowledge we're going for here? Yeah. What does it protect you against, and and what does it stop? Yeah. Assume the audience knows nothing, probably. Okay. Okay. Yeah. I love that sense Sense of trust that you have in them. The the, content security policy or CSP, and and, this is a fun one if you if if you're a pedantic Vercel, unlike myself, a lot of people will talk about CSP policies, and that's like saying ATM machine. You're you're the the p is is is duplicate.

Guest 1

And that's fine. I'm fine with that. So, content security policy is a specification that most browsers implement. I don't know. I think it's, like, IE 8, maybe IE 7. There are versions. There's content security policy 1 through 3, and there's, you know, like, additional specifications, and modifications that are that are being added to it in the working group still.

Topic 5 13:44

CSP closes vectors for attack by blocking everything by default then selectively allowing

Guest 1

So it's it's it's a evolving specification for increasing the amount of, vectors of attack on your website, especially cross origin, but also, like, cross format. If you if you ever went to a a Douglas Crockford talk way back in the day, he used to have this thing at at Yahoo. I'm gonna remember it after this, but they had this whole thing. It was like a sandbox.

Guest 1

You'd, like, run your run your stuff through the the Crockford sandbox, and it would, like, make everything safe Node not allowed to reach. I think it's sort of the c or something. But, anyways, there's, he has a bunch of talks where he talks about, like you know, there is this fundamental security mistake we made, when we made the web where you could just load a script from anywhere. That shouldn't be allowed. Like, that's fundamentally the wrong choice. It's very bad for security. The web is insecure, and, we shouldn't have done it. And this thing I made at Yahoo fixes it or whatever is the idea.

Guest 1

And, I think that's roughly true, but I think and I think he also acknowledges this in the talks. Like, that's also what made it work. Right? Like, we had to start with the insecure thing. Everyone just mashed everything together. It was fun, and it was chaos. And now we're like, we've grown up, and we put our bank stuff on it. And we're like, okay. Let's let's lock it down.

Guest 1

This isn't just WebRings anymore. Yeah. Exactly. And so, like, I am happy. Like, my career comes out of the Fact that we were able to do that, and now my career comes to the fact that people need to to stop that from happening maybe by default.

Guest 1

And so the idea, in in most security circles on how to, like, make something secure is to not think of all the ways That someone could hack you and then block them. It's block everything and then think of what you need, and only open up a little bit For that. And and then you still have to worry about how people could tag you, but that surface is now much smaller.

Guest 1

And so with content security policy, you can create a policy. I think it's a little more complicated than this, but, like, I I think it's fine. That roughly says my default source JS just self. And that means that for all the various rules that we can set, we can talk about that. They're like apply to script tags or style tags or Pnpm or places that you can request things from. Or Images too. Interesting. Yeah. Yeah. All that kind of stuff. And it says, like, the the default source is the kind of the fallback. Right? So you're saying my fallback is the only place an image can load from is my own self.

Guest 1

And then immediately, you're in that world, Wes, like, if you try to load an image, if you try to, like, make a request, if you try to do any of these various different things, if you try to, like, even XHR to another server.

Guest 1

It'll get blocked by content security policy. And that's, like, a nice starting point. And so you write your warp, and then you hit a point, at which I need to make an SHR to api., you know, my website Scott. And, at that point, you say, okay. I'm gonna say my default source is still self, and then my script or Node. My connect source, which JS, like, what an XHR, would would go through, is gonna be self and api.mywebsite.com.

Guest 1

And so now you have 2 places that requests can happen, and you've closed a lot of vectors, for someone to, like, exfiltrate data or inject scripts. It also, by default, and this is different than what we were Talking about with all this Crocs stuff, will disallow inline styles and inline JavaScript.

Guest 1

And, technically, that's that's not part of what we were talking about before. But since that's such a wrong vector for things to go wrong.

Guest 1

And it will also, it it actually disallows things it calls unsafe JavaScript, or unsafe styles. And those also include things like new function with a string or eval.

Guest 1

And so it kind of closes off all the loops to run arbitrary code, from not a script tag that you loaded from your own website.

Guest 1

And so

Scott Tolinski

I love this idea of permissions by By enabling what you actually need. Yeah. The opt in, and and we've seen this with, you know, Deno compared to Node. Js Wes it's locked down by default. You have to explicitly enable things. But if you've ever worked on anything like Bos or whatever, you know, you're constantly telling it what you need.

Scott Tolinski

That makes a lot of sense to me, and I don't think I've ever heard it expressed that way about content security policy.

Guest 1

in in an important way, which is that there's no rule to say none. That's the default rule. There is a rule to say allow unsafe, inline JavaScript.

Guest 1

And you can also only allow, unsafe inline or unsafe eval separately from each other so you don't have to, like, do one with the other. But a lot of times, you actually run into these problems where you want a little bit of something, but not a Scott. And then we can talk about the strategies for that. But but you you get these buckets. But, yeah, by default, you cannot, you know, with just, like, everything locked down, you can't do it, and you can decide to allow unsafe, ESLint or unsafe, eval.

Scott Tolinski

Wes and and just to be clear, how are we setting these things? You you're a developer. Yep. This is a header,

Guest 1

that comes with every request, or every response in the HTTP request. So you request, you know, pnpm. Html from your website, and it comes back with headers that are like, here's the cookies for the website. And then one of them is called content security policy, and it has this format that has these, like, I I think they're called directives. Right? Like, the the default source or the connect source or the script source or the style source. Those are these, like, little lists of, allowed places you can connect to. And it's probably much better if I had a slide to show everyone, but but I think you have a lot of audio listeners.

Guest 1

Yeah. Alternatively, you can do a meta tag, but there are more problems with meta tags, and I think they're, like, generally discourage unless you must.

Guest 1

I find that they're useful for, like, hacking sometimes.

Guest 1

You know, I'll, like, figure out how to do headers on some closed system. But, like, They're more hack like, if someone can inject before your CSP meta tag, then then they'll they'll win. Because they can just inject a meta tag that's like, everything's fine. And then Yeah. Then the other ones get ignored. Right? And there are also some rules that you can't inject via a meta tag. And so, like, the it's limited support. I'd say, like, unless you must Use it. Don't don't use the meta tags. But, yeah, typically a header, and it it's gotta come with every single response. It can be custom per response. So if you have certain like, Some people are worried about the, like, byte overhead of this because it can get quite large. And then we can also talk about strategies with that. Maybe I should write down what I'm gonna say we can talk about strategies for. But, the directives apply to any time like, like, if you have a full page reload, those directives need to work for everything that might happen from then on out. So if you do any full page reloads, like, you can't update the content security policy along the way. And so for most, like, spas, you are you pretty much just send everything.

Guest 1

So it's not anything can happen after that, but that page is locked into that content security policy because it only got the header once. Right? You only made the request for the HTML. The body, you know, it was loaded in via HTML, only the once.

Guest 1

Oh, okay. Then it was modified all sorts of times, and it looks totally different. But those rules need to continue to apply. And that's actually not too hard on for most apps. But when you get to Stripe Size and there are, you know, hundreds of people needing, like, custom rules for their one thing and it's a big, SPA or whatever, then, single page application. Sorry. Acronyms are bad.

Guest 1

Then then you end up with just, like you go to the Stripe page, and it needs 7,000 rules because, like, there are there are so many different, like, little, you know, use cases where someone had a thing, and then it becomes pretty insecure. Right? Like, you're almost back to where you started. And so it takes, at an organization our size, a few different strategies to, like, mitigate that from happening. That's the default outcome after a few years of, like, turning on CSP JS that it becomes huge and, like, someone gets a PR approved that's just like, yeah, allow all unsafe things because I need that for this graph library.

Guest 1

And then you don't realize that, like, all the other work did, has now been invalidated.

Guest 1

download something, or what other types of things do you often see? Yeah. Those are pretty easy to mitigate. You just, like, mirror it yourself or you say that this 1 URL is fine. Now whenever we're talking about directives, you can let's say script tag is a good Sample. Right? I wanna be able to load jQuery from the Google CDN JS something you might say. Like, I think that's an important performance thing for my website, might be wrong these days, but let let's say you believe that.

Guest 1

And you could do that. You could add, you know, google.com as something that is allowed to load. But now if someone hosts, like, you know, hosted .google.com/myhackerwebsite, you can load JavaScript from there too. So you can actually qualify the URLs as much as you want. And so if you wanna load jQuery from the from the Google CDN, you can fully qualify that only this full URL Can load from the jQuery CDN, not just the domain. And so that's the first thing like, the first line of defense is like, alright. You have this script that we are pretty sure is fine. We're not To like, if Google attacked, we're probably screwed anyways. And so, yeah, load it from the Google CDN. This isn't my actual opinion, but, but load it and then just add the full URL is what what I do. That's probably the first line. It's like, better than all scripts are allowed. Right? Like, this is so much Yeah.

Guest 1

And so that's the first line there. But then, you know, you could self host.

Guest 1

But there are other ways things break.

Guest 1

Like, there are a lot of libraries that will include inline styles, and those inline styles will be injected in a certain way, or a lot of libraries will do eval under the covers for some, like, perf reason. Charting libraries are often, like, the worst offenders here, for whatever reason.

Guest 1

I'm sure it's Mike Bostock's fault, somewhere down the line.

Guest 1

But the mitigations are rarely that easy for those. You had to, like, reach in and fix them. You'll see, like go to any open source library that you use and search for content Security policy or CSP in the issues or especially the closed issues. And you'll see people being like, hey. I can't get this to run. And often the advice is turn on unsafe eval. Right? It's just like oh, yeah. It's like yeah. I actually am a little bit sad because there there JS this, like, wave of, like, adding CSP policies to create react app and things like that. And then just, like, so fast, the advice becomes, here's how to functionally disable it. It's like it's both on and off at the same time. The worst of both worlds. It's both going to annoy you and not protect you.

Guest 1

I think that, like, an interesting conversation that that maybe you wanna bookmark, but, I think you you said something about, even, unsafe ESLint styles. And I think that's a really fun when I give a talk about, this topic in general, that's the one that I like to give, because people don't realize how much damage I could do or not sorry. It's not me. I could do once I read the the reports of people who can do damage with just, like, ESLint style tags.

Scott Tolinski

There's some deeper with that? Yeah. Tell us. What can we do? Let's hear about it. So so, like, the the fundamental,

Topic 6 26:27

CSS attacks possible by defacing site or exfiltrating data

Guest 1

like, piece is a request.

Guest 1

Well, I remember there's 2 there's 2 ways that you can herd a website with with JavaScript or with style. Sorry. The first way is to deface The website. Right? Or cause the user to click something they didn't intend to. And those don't necessarily require you reaching out anywhere.

Guest 1

And so you can imagine there are, like clickjacking is maybe, like, the earliest form of this. You make some invisible link that covers the entire page. And no matter where you click, you're gonna get redirected to my website. So I can't inject a meta tag redirect, and I can't inject script that redirects you to my website. And I could inject something that's like, hey. Click here to win $1,000, but that seems sketchy.

Guest 1

But I could make it to where the entire website has an absolutely positioned, fixed, whatever fully clear link, over the top of it. And then anything you click from that point on, will actually just go to where I want you. And I could even, you know, guess what you're gonna click and then make that page look like you intended on being there. Right? And so it'd be great for phishing.

Guest 1

Another old one that's, like, roughly fixed is, CSS link knocking JS what it's called. So if you, know about, you know, the visited selector.

Guest 1

The visited, links, turn purple and get a different treatment. Right? And so back in the day, people were just on the side of the page where you couldn't see you know, off the the corner of the page, they just, you know, render all the most embarrassing websites and see which ones match the visited selector and be like, hey. Looks like you go to embarrassing website Scott, you know, pay pay me money, or I'll send it to, your friends or whatever. A lot of people were also afraid about, like, you know, you go to your insurance website and they, you know, do a quick visited link knocking check against you to see if you're searching for symptoms of certain disease. You know, like, there are ways in which that could also be, bad, from a, you know, state actor standpoint. That's that's why if you if you use get computed style and CSS on Tolinski. It will not give you It'll last of the styles except for the

Guest 1

or just a couple others. It's just a cat and mouse game. So, really, all we did was make that trick slower.

Guest 1

Imagine if you I really like this too.

Guest 1

Imagine if you made a really hard to render style for your visited link. So it will render it, and then if you do get computed style, it'll tell you it's just a a non visited link. But, like, what if it takes, you know, a 100 milliseconds to render that much, like, box shadow? Like, you just put 800 shadows or something Overlaying each other. My god. So now you render the links, and you'd see which ones take a really long time to render or you like, you do that kind of stuff. Right? And there are versions of that.

Guest 1

Could you do the height on a visited link and check the height of a parent? Sorts of ways To, yeah, like, essentially compute whether links were but it all becomes slower because you have to, like, guess and check, guess and check. Yeah. And so, really, all they did was, like, slow that down and make it Less hard, but, or less easy. But yeah. I don't know. One of my favorite things about talking about security is I repeat A report I read. And pnpm everyone's like, wow. That's so smart. And then it gets, like, credited to me.

Guest 1

And so I should say that I've I've hacked exactly one thing, I think ever in my entire life. And the rest, I just read the report of someone else who's much smarter than me. So hats off to the real hackers. And to be clear, my bug bounty that I got was on Yahoo Sports.

Guest 1

I think, well, I was playing fantasy football with other Stripe employees, and the the winning, Like, person would win, like, $200.

Guest 1

And, like, on on week 1, I hacked the the, like, bio section of my thing to script inject, and so I could, Like, delete anyone's team or change their players. And all I did was change their team names, to, like, something that was good about me and bad about it. It was, you know, like, Alex rules, Wes rules would be their team name.

Guest 1

And so anytime anyone played me, their team name would change to something like that. And then at the end of the season, I reported the bug, and I think I got, like, paid out, like, 15 I got last place in the league, obviously. Yeah. And and then, Scott paid out, Like, $1500 or something like that. That's pretty sweet.

Guest 1

Yeah. So that's the second half. So I was talking about kind of 2 ways. You can kinda deface or trick, with the defacement.

Guest 1

The other half is, you have a combination of being able to make requests so you can exfiltrate data. You can at least, like, write an analytics. Right? You know, like, I know exactly which IPs are on this website exactly when. And maybe that's useful to you, and that's enough. But often, people are like, yeah. That's fine. Like, someone injected, they can, like, reach for this transparent dot PNG that gets requested as a background image. And, and, you know, that's not great, but, like, what else can you do? And that's where things get Really, really interesting.

Guest 1

So the first thing you gotta add is the power to write, like, selectors that match fuzzy things. Right? So you can have, like, a starts with selector, or you can have a selector that matches. So imagine you had, like, a 4 digit PIN, that you need to put into your bank website Or a 2FA code that you need to put into a website, and you need to, like, exfiltrate that data. Now you could just write, you know, what is it? 16 you Node, it's not 16. Whatever. You could write 10,000 selectors and inject them into the page, and each one matches an image called 1234 Scott Pnpm, 1235 dot PNG.

Guest 1

And then whichever Node matches, ends up being the one that's Wes, and you've now exfiltrated the thing that the user typed into the page. Wow. And you might say, like, alright. Well, what we really don't want to happen is for people to exfiltrate a CSRF token, for instance, because then they could make requests that are essentially authenticated against the API for this user.

Guest 1

And you'd say, like, alright. Cool. My CSRF token is so complex that it would Takes so many, you know, trillions of selectors.

Guest 1

And that was the that's this is the most mind blowing one to me.

Guest 1

Another thing that you can learn about, CSS is that you can import external CSS files at the top of a CSS file, you can say import food dot CSS.

Guest 1

And that request gets kicked off but is blocking, of the rest of the styles, rendering. And so you can do this this is a little bit hard to explain over audio, but you can essentially make a loop. So you have a request that gets kicked off. And then in the body, all you search for is let's say it's just an alphanumeric CSRF token. And so you just search for a through z. Right. It's all lowercase. And all you did is you wrote 24 selectors, a through z.

Guest 1

The script up top is non blocking, or you can make it non blocking, in certain browsers or something. Blah blah blah. Who cares? One of those selectors will match, and it'll ping the server. And that server will match, you know, a Scott JPEG.

Guest 1

You know, it it'll load it in the background somewhere you won't see it. But you haven't replied yet with the import from above.

Guest 1

So now that import above is also on that same server, and it knows that a hit.

Guest 1

And so you can respond with a CSS that all the prefixes already start with a because you know a is true.

Guest 1

And then you can say, alright. Here's 24 more, where it's a a, a b, a c.

Guest 1

And you can essentially, in however long your CSP request is and however, like, much entropy there is, like, in 30 requests fully, you know, extract someone's CSP, or CSRF token and blah blah blah. And and this JS, like, happened in the wild, and someone reported it. And it like, for the most part, JS mitigated, but, like, not entirely.

Guest 1

It's such a cool hack. Like, I have nothing to do. I I believe the the person who found its the handle on Twitter is like Sanity something, but Donut found this. So I I we can I'll I can share it with you in the show notes. Yeah. They're an incredible, security engineer, so I do not take credit for that

Guest 1

Yeah.

Guest 1

You'll you'll see that a lot.

Guest 1

You'll even see that internally at companies.

Guest 1

You'll see companies who could host their CDN.

Guest 1

Like, there's no reason, like, Stripe's files can't be served from the same domain, as other stuff. But you'll see, like, CDNs get split up, and you'll see, like, different products get split up into different URLs to even just, like, mitigate, internal URLs versus external URLs. So internally at at Stripe, we have URLs for, like, more lockdown services and then URLs for less lockdown services and, like, different types of data can exist, on on different different ones. So, like, our, like, internal Node pen type thing.

Guest 1

Like, it's you know, you write arbitrary code and host it. Right? Like, that's gonna be the least least secure one. I can't, like, reach out, and hit hit, you know, user data or something, of course.

Guest 1

I'm not I I don't speak in no way on behalf of Stripe for for this, by the way.

Guest 1

Nothing I'm saying is official. It's all a joke. Okay, good. Good. Good to know that.

Guest 1

worm happened as well. Sammy worm. Do you know Node you mean? You're admitting to running a worm, and then knowing about the Sammy worm, the the Sammy is my my friend. You know, the outcome.

Scott Tolinski

Outcome of that whole thing? Yeah. Didn't didn't he get a knock on the door by the,

Guest 1

the FBI or something? Okay. So so that story JS pretty good. It's, it's. There was originally someone who did some stuff with Facebook, and they very famously, like, turned Facebook to look like Myspace. And then Facebook was like, hey. You should just work for us. Like, that was incredible. And then he did, and he made a ton of money, and and he's super rich now. And Sammy is, like, a great hacker. Sammy also wrote, like, Evercookie and a and a bunch of stuff. It's one of the best security engineers for front end stuff ever, for sure. And, Sammy did that. Sammy is my Sammy is my best friend. Sammy is my I can't remember.

Guest 1

Samy's I don't know. Samy's my hero, I think it was.

Guest 1

Whatever. Who cares? S a m y. And, Myspace is like, hey. That's amazing. You should come work with us. Thanks so much. And flew out to LA. And, like, when he gets off the plane in LA, like, gets arrested by, you know, the FBI and then, told he can't use a computer for 5 years under, like, the the Patriot Act.

Guest 1

So his outcome was was quite different with the same thing. Because, like, I would totally do that.

Scott Tolinski

You should Teach people a lesson about running arbitrary code. Yeah. But, yeah, that's crazy. So yeah. You hear that, Wes? You hack my code.

Guest 1

Okay. A plug for Sentry, and also a plug for Sentry to, help me even more with this pro I I actually don't have to deal with this, but, if you run a content security policy that has reporting on so one thing we haven't talked about is that, you can add a report to directive. And anytime there's a, violation of the content security policy, then that, violation will be sent as a report, then you can, like, log it. Right? So we have logs of all the things. And Sentry handles, content security policy reports, and it's Much better than what I had to use before that, and that was great. But the web is is is crazy. You know? Like, like, there are things like in app browsers that are trying to eject stuff all the time. And so the amount of noise that exists in your reports Makes it almost impossible to find, like, the single hacker that's hacking. Right? Mhmm. I don't know if anyone tries. I'm sure there's someone at Stripe who's thinking about that. I'm not again, this is all a joke.

Guest 1

The, like, it's a very difficult job at a company to go through all the violations that are happening because of things like in app browsers, but they will fail. So, essentially, the in app browsers will try to inject stuff onto your page. But in general, they write that in a way that if the page rejects it, then, then, like, things still work. And so for the most part, that's what it that's exactly what content security policy 4, if you don't want Facebook to inject their tracking cookies onto your page in the Instagram in app browser, then use content security policy and and don't put They're you know, like, the the allowances on there. And and that will work for the most part. JS possible.

Guest 1

I don't know the details here. My Yeah. Or the exact details. My if I had to just make a wild guess JS that because of the lockdown browsers on Ios, it's Yeah. It's pretty difficult to do, and I bet in Android, it's a little more all over the place.

Guest 1

My I think my assumption is that, for the most part, the the majority of people are using in app browsers that probably are not, like, hacked in such a way that they're ignoring content security policy.

Guest 1

But I mean, you could you could counteract that. Right? You could write something that, like, destroys your web page if CSS, you know, a script that's in line that says hide everything, and it that would be blocked by content security policy. And if it's not, then things go away. So I don't know. You could you could you know, it's a game. It's all cat and mouse. That's cool. Yeah. And and what about the self XSS?

Guest 1

Not really.

Guest 1

A, your audience is different than most people's audience. I don't think that like, it's a huge a huge problem.

Guest 1

I think that's more of a browser level thing.

Guest 1

Now if you go I can't remember for sure, but I'm pretty sure Facebook. If you open, facebook.com.

Guest 1

It's like, hey. Did someone give you something to paste here? Don't do that. Like, that's not gonna give you more friends or whatever. I think there, you know, there have been various things that browsers have done to make bookmarklets more safe for people because that's used to be the way that that, you know hey. Save this. Copy this into your bookmark and then click it. It would be a way that people would do script injections. And then it kinda still works, but it's like, it's a little bit more locked down. I don't know. There's not not self access is tough.

Guest 1

Yeah. It's hard to, like, Safari's approach is they don't you have to turn on developer mode in order to even get access to the console, for instance. And I think that's, like, why they do that. Mhmm. Yeah. Makes sense. To save people from themselves, which is

Scott Tolinski

always the hardest part if somebody's, like, really, really trying to install something on their system.

Scott Tolinski

You can only do so much to stop them. Yeah.

Scott Tolinski

Hey, we are at 48 minutes. Oh, yeah. FYI.

Guest 1

Oh, immense.

Guest 1

Parts. Yeah.

Guest 1

either on CSP or just on client side security in general? Yeah. Maybe we maybe, like I think it's been fun to talk about, like, why Like, the the mental models and, like, why it's scarier than you think, but maybe, like, the most practical

Guest 1

Yeah.

Topic 7 44:06

Best practice is to use CSP from the beginning for new sites

Guest 1

It it definitely changes your mental model a little bit for some things.

Guest 1

I'd say that, like, my number Node advice if you don't already have an application is do CSP from the get go.

Guest 1

Much like something like internationalization and often, like, accessibility I'd put in this bucket are things that are not that hard to do right if you do them from the beginning. It's like turn on CSP every time you need something, put it in there. If you're not a huge company, that's probably fine. You're good.

Guest 1

It's hard to add CSP to a website, you know, 2 years after you launch because there's so you've been paying you you have to, like, push back the all the aggregated work that you've pushed off until that ESLint, and that can, like, be disruptive.

Guest 1

And there can be hard edges. So my first advice is is probably if you're starting a website, start with a pretty locked down, CSP and just, like, add things that seem reasonable to you along the way, and you're probably gonna be okay. And and maybe another tip I'll give along the way, it can can talk about what a reasonably locked down CSP is. But, second JS if you already have a website, the first thing I do is start writing a policy that you think is good and start with, what's called content security policy report only. So you can send a header called content security policy report only, and it works exactly like content security policy except for that. It only reports. Oh,

Guest 1

also, like, this isn't I wouldn't say this is the definition of incremental incrementally shipping. But as a as someone who's a fan of incrementally shipping, you can do it in 2 increments, because you can serve both a content security policy and a content security policy report only. So you can just lock down stuff that you know you're not using. Right? Mhmm. And then have pretty you know, you I have to keep unsafe ESLint or unsafe eval because I know these libraries are in use, and I can't lock that down yet. But I think that I might be close, and so you can also do the report only, and kind of, like, work towards the more lockdown content security policy.

Guest 1

At the same time, like, you don't have to wait until it's perfect, I guess, is is really, what I mean there. But the increments are 50%, and Deno Node are the only increments that you can do JS 50%.

Guest 1

Yeah. So, then there are strategies. Right? Different people have different opinions on the best strategies for rolling out CSP.

Guest 1

I'd say that, like, for most, you know, smallish things, like, I wouldn't worry too much about a strategy. Just, like, lock the default source to self at first. And when you need to make an ACR request, add the most qualified URL that you can, not the you Node, if it's the domain, then so be it or whatever. And then, like, just go go on with your life. If you need to add so, like, just literally do everything exactly the same you would, but whenever it breaks, add it to the CSP policy. And then you're gonna be so much safer even though you're doing everything Exactly the same. Now you may need to make some decisions on, like, how you load in jobs. Sometimes someone's like, alright. Paste in this you know, put this in your body, and it'll asynchronously load this other thing in. And you need to make just some decisions, like, you sometimes you have to trade off performance and Sanity, sometimes you don't.

Guest 1

There are some things we haven't talked about, and those are you can put hashed scripts, or this isn't a great word in all cultures, but a nonce, is is something you can add to your header, in order to allow, like, specific inline scripts. So you can say, the hashed contents of this script that I wanna load is this in shot 256.

Guest 1

And you can put that in your header, and then it will allow that single ESLint script. And so if someone's like, hey. Put this, you know, inline script in your body so it can load. You'll need to add both The thing they end up loading JS well as the hash for the script to run it. Or you can just choose to, you know, like, use an async, script tag or something instead.

Guest 1

I I don't Node. The the specifics matter there. Yeah. Yeah.

Guest 1

And so there are, you know, different ways to make things okay for your website. Some people are, like, really adamant that, like, hashing is okay, but, so a nonce is just like, here's this randomly generated string, and I'm going to also add that nonce attribute to the script tag. So you can say script nonce equals foobar baz. And as long as the header also had that, this kind of this double check. Like, the header knew that this would exist, and now it exists.

Guest 1

And so that's what Anance is. But, those often get caught in, like, a cache. And so you'll see websites serving Anance, like, in their header, and it's the same for every user every time. And now you're in a bad zone where you've given an attacker away.

Guest 1

So if you're, like, considering doing it, I'd I'd optimize for, hashes long before you did nonsense, but it's not always an easy thing to do. Because sometimes you have a dynamic TypeScript or something. Like, I don't know. It's tough. Yeah.

Guest 1

Another thing you can do is, like a lot of times, the reason people need inline JavaScript to begin with is to, like, load their initial state. You Node, it's like,

Scott Tolinski

reduct state equals Yeah. The Foo. Hydration.

Guest 1

For that, like, load it as JSON and then have a non inline script tag parse the JSON out of the out of the DOM. Right? You can still do it. It's just not a live Scott. And that's a much better practice anyways because now that block isn't script injectable. For years, the default, explanation on how to do that on the React website was injectable. If you just added a closing script tag To, like, the name. Now you close the script tag that, like, your initial state was in. Oh, yeah. On on my website, I just throw it in a, like, a hidden text area or input or something like that, and then just pick it up from Yeah. That A common thing is for people to still put it in a script tag, but give the script tag the, Like, type, application JSON, which won't execute. And so it's, like, it's cute. I don't think it I don't it at least doesn't, like, try to, like, render it. Like, you don't have to pay the cost of the the DOM caring about the inside of it. So maybe there's a benefit there, but there are probably other ways to do that.

Guest 1

So, yeah, there are some gotchas there. And then, maybe, practically speaking, I think the next thing I would advise is for people to think through a concept that isn't an ALEC specific concept that you can, like, Google about, like, and and that is a concept of threat models.

Topic 8 50:40

Important to think through threat models when applying security

Guest 1

And so one of the fun parts about thinking in a security mindset at first JS you're thinking like a hacker. You're like, someone could chain this to this to that to the other thing. And then you start building web applications That are really complex, that are blocking against legitimate, like, exploits that are just really unlikely.

Guest 1

You're you're focusing your energy on a on a on a on a small part even though the attack vectors are large. Right? Shout out to an old, coworker, Rich O'Healy, who who who really, advocated for this in my life. But the first thing I do is, like, figure out the threat models for the thing that you're doing. Like, what bad things can happen? What are the what are the worst outcomes? And then, like, work backwards from there. And if, like, something isn't work out, like, it doesn't mean, like, don't solve for that thing. Of course, like, whenever you're going kind of from a permission based instead of, like, allow list versus Deno list, kind of mindset. Like, things are gonna be locked down by default. There's, like, always a way through. Right? It's about, like, layers of security and making sure that you're focusing on the right spot. And then maybe my last tip is that, there's 1 thing that we haven't talked about, and and it kind of is a new idea that I haven't got to play around with a lot.

Guest 1

And it's kind of the I I don't know. I'm speaking for people who I haven't talked to about this, but, like, it feels like the goal of a new directive called, strict dynamic is to mitigate some of the problems that organizations like Stripe or, like, any kind of, like, more complex than a few developers might might see. And that's where you get, like, so many rules and so many things that it's, like, too hard to understand what anything is doing. And because there's only a single that you end up in this world where you trust this initial set of things and then anything can happen after that. And so they've essentially kind of codified that in a in a nice way. They're saying there's this new directive called strict dynamic. And so you can say TypeScript source is strict dynamic.

Guest 1

And that means that make sure this script is allowed, and it follows all these rules, and it's in part of my analysis and from there. But anything that that script does is fine. So it can load from any domain. It can do and it'd be like Uh-huh. I already trust that script to do anything on my web page.

Guest 1

So, like, if it gets hacked, I'm already, like, probably screwed. Like, probably if someone could hack that script, they could hack the CSP header itself, like, is the idea. It's not necessarily true, but it's like a nice place to give ground. It's it's actually a good, kind of, like, application of the idea of of threat models, it's just like in the threat model where someone can hack your server and change your script file, they can probably also change your header.

Guest 1

And so let's just make an easier use case, and we'll actually end up with better security because people won't have to accidentally add an unsafe ESLint, which is much worse than strict dynamic, Because the strict dynamic can now update, you know, stuff and inject inline scripts and do all that kind of stuff, because it was originally trusted.

Guest 1

And maybe one thing because I'm worried that this whole time I I haven't explained that is ESLint styles or CSS and JS from React uses the style the style APIs under the covers. So if you update the CSS, or the style attribute of an element in your React, you not actually doing inline styles. The that they compile that to a call to, element dot, style dot value equals. Right? Yeah. It's it's using the DOM API, not Just the DOM API. Style tag that needs to be parsed. CSS and JS is totally fine to React. It does not break the unsafe inline rules because it does the compile down to JavaScript that that calls that.

Guest 1

It's Scott, like, arbitrary text for the most part. And so yeah.

Guest 1

That last thing is, like, consider strict dynamic if you're if you're making a new website or if you have an existing website. That's Often a good first pass is, like, doing the strict dynamic. Like, yeah, I can lock down this from here, but then it gets really murky from, like, the long tail. That's often a way to, like, do that. So you can mix that with the report only stuff and and really get, like, a lot more confidence That, like, data exfiltration and, and XSS and stuff like that are impossible. But, again, it's layers. Still sanitize stuff. And, like, don't rely solely on this stuff. There's, you know, there's ways around everything you want,

Scott Tolinski

a a multilayered defense for sure. And and I had a a just a quick question on development and staging environments. Obviously, these are going to be totally different URLs, different area. Like, what's the process for making sure that this is all good in all of your environments,

Guest 1

works smoothly? Yeah. Two layers here on simple things.

Guest 1

Some of the directives that they have built in Yarn, like, self, and that's really nice because it it just whatever you are, if it's staging, Scott syntax Dot FM versus, prod dot syntax. It just works because that's the self. It it implies, like, whatever whatever served this header.

Guest 1

But, yeah, for sure, like, we might be hitting different API endpoints, and you don't necessarily wanna open up staging Yeah. Dot syntax f f m API syntax f m, on the prod one. Right? And so there's not, like, CSP level tooling for that, but many frameworks have toolings for this. At Stripe, I found that the thing we've landed on that I like the most is getting those directives that are related to each other as close to each other as Possible. I'd say the 1st pass that anyone do is, like, here's our staging CSP, and here's our prod CSP, and there these 2 files or these 2 sections of file. And updating 1 often means you forget to update the other and you break or whatever.

Guest 1

And so, I think the current one looks more like you can add something, and then you can give it the QA mode or whatever. Like, anytime you're adding something, it's, like, here's the bundle of things I might need to use, and here's how it works in the various Different environments.

Guest 1

Yeah. It is a static file, but it has, essentially, like, PR approval rules that security must sign off, on the Okay. On the change. So it does need to get signed off on by security.

Guest 1

And there's a ton of documentation, like automation that happens. Like, if you add a rule, it's like, hey. This this JS not gonna work. Here's why. Here's the docs. So, like, there's a lot of automation around trying to, like, not require you to, like, Do that, like, slow loop. But, like, Stripe is pretty advanced here, I think, compared to to other

Guest 1

Sure. I I think I remind me to tell, the my original CSP story. I meant to tell it at the very beginning of the episode. I think it's good. But, yeah. So it it wasn't it was Thanksgiving Day, the day before Black Friday.

Guest 1

Apparently, Stripe and Vercel, apparently, I think we're we're like, oh, we should do Black Friday. Like, how much, volume has gone through Stripe on, like, the Black Friday Cyber Monday? Maybe you saw, what is it? Bfcm.stripe.dev, maybe? I think we took it off the main Scott domain for for the reasons, specified earlier.

Guest 1

And it, you you know, JS a Stripe website that we had to host. And, you know, some people were building it, and then they're like, alright. We wanna deploy it on Stripe's infrastructure, and then it hit all these checks. And so it Wes kinda being developed outside of the things. It was this quick project people wanted to do. And then just like, yeah, you're, like, pulling into the infrastructure so we can serve it on Stripe dot dev, and and, like, it it failed all those checks. And security was like, nah. We were not gonna, like, approve a bypass of of all the security checks. And so, yeah, I I spent the morning on Thanksgiving, like, essentially doing what we were talking about. It's like, Alright. Let's go through everything. Let's put up a report only. Let's, start breaking it and then, like, working through the thing. And so yeah. I mean, first thing you can do is is, in that situation is what I did here is, you know, put up a default source self, and then the page doesn't load at all. One failure happens, and then you, like, add a directive or you fix the Node, and then you get a new error, and you just keep doing that until it happens. It was a a few hours, I think, and we got something out the other end that was, like, pretty agreeable to security, and and went out the other way, but, you know, it's always fun to be the person who knows about that in an emergency because there's, like, a high a high reward for, like, how quickly you you can kinda Vercel that stuff compared to, like, the team that, like, didn't know that that was a requirement. So maybe the lesson there is, like, As early as possible, try to, like, surface these errors to to people. Yeah.

Guest 1

yeah, we got time for the the story. Yeah. Go for it. The other story I wanted to tell yeah. It's it's quick. I I was at JSConf EU, I don't know when, 2012, maybe, 2011.

Guest 1

And I had just started, like, getting ESLint, security and content security policy and stuff like that.

Guest 1

And I think I was speaking I can't remember. Maybe I was just doing video. I mean, I think I was just doing video. And so I was out there, like, speakers dinner or something like that. And, and I was talking to someone. He's like, I was like, oh, I'm Alex. I work at Stripe. Cool. I, you know, I I work on jQuery. I I feel important. And he's like, oh, my name is, Mike Wes, and I, like, I do some security stuff at Google. And, like, oh, really cool. I was like, I've been learning a lot about something called, content security policy. Do you know anything about it? I think he'd, like, very nicely informed me that he was the author of the pnpm.

Guest 1

And And and love it. And, since then, I've, been very, very fond of Mike West who, I it I don't think he's, like, solely the the spec author these days. I don't know the extent to which he's involved in new stuff or is also but, like, he is a huge massive, maybe, majority fingerprint on a lot of the CSP stuff and is extraordinarily good at describing, you know, the problems. And and I can credit a lot of of, the things I've learned to him. But, like, fundamentally, I think have straight changed the security landscape.

Guest 1

But I really put my foot in my mouth, at that point like, I don't know. I really felt like I was coming across, like, someone who knew what he was talking about, and, you know, it was the spec author. It's the guy who yeah. Yeah. Who actually made it.

Guest 1

There JS exactly 1 person who I couldn't essentially come across like that to, and I just talked to him Yeah. Of the entire world. Mhmm.

Guest 1

I did I Wes? Sick pick and same as what for I I guess a plug could be for anything. But what's Yeah. A a sick pick is you pick something that is 6. So it could be a

Guest 1

Yeah. I guess I'll plug, I have a my sick pick and my plug are gonna be the same. Okay. There's this, there's this guy on Instagram who does, like, home improvement stories that I really like. I think his name is Wes Bos.

Guest 1

You should.

Guest 1

You should follow his, like, basement reconstruction.

Guest 1

Yeah. I I really appreciate all of his organizing tips. Also, like, subgenre of, like, thrifting.

Guest 1

So some of my best best content.

Guest 1

I think, mostly just Twitter these days, under SlexAxton, which is kinda like my name, but the first letters, swapped, and and you lose an e somewhere in there as well. It's like Saxton.

Guest 1

Yeah.

Guest 1

Yeah. For sure. Thanks for having me. Yeah. Thanks so much.

Share

Play / pause the audio
Minimize / expand the player
Mute / unmute the audio
Seek backward 30 seconds
Seek forward 30 seconds
Increase playback rate
Decrease playback rate
Show / hide this window