Welcome to the React Show, brought to you from occupied Miwok territory by me, your host Thomas,
and Broken Assumptions. Is your new web app secure? How easily can someone get into it,
take over accounts, or even steal user data? Can someone- take over the site or even use
it to distribute malware? How do you even know? Don't worry, today we're going to cover the
basics of web application security so you'll know where to start, what to focus on, and
how to progressively harden your web application. Thank you so much for joining us. I'm fresh
off a bicycle tour in the San Gabriel and Santa Monica mountains down near Los Angeles. And
I'm really excited to jump into the basics of web application security, maybe more. Maybe
I'm too excited. I just, you know, it was a great time. I had a great time and I'm just
feeling refreshed, you know, so we'll see what happens. But I did want to begin with a couple
quick
super excited to present the general availability of the Reactors. It's the silly name I'm giving
to my new initiative to expand the community around the podcast and programming and React
in general and just try to help all of us get better at programming and React as well as
provide a place to just hang out. First, you can sign up for a premium version of the podcast
that includes all of the regular episodes ad free, as well as bonus episodes. And not only
do you get more, but it also really helps to support this show. And you can actually sign
up for as low as $1 US dollar per year, trying to make it as accessible as possible as well.
But whatever you're able to do, if you're interested, it makes a huge difference in my ability to
produce high quality educational podcasts. I just super appreciate any support anyone is
able to give. Second, you can join for free on our new Discord server or channel. I'm still
learning Discord, whatever it's called. But yeah, you can come hang out with us. ask questions
about programming or react or even tell me how I was completely wrong about something. I'd
love to hear that too. Yeah, so if you want a little break from programming too, I also
post sea slug pictures or other fun adventure things. I want it to be a fun space to hang
out so we can learn but we can also just be friends and hang out with each other, right?
But yeah, no matter what the reason, we'd love to have you come join us on Discord. Links
for Discord and the premium podcast feed will be in the description or summary or whatever,
wherever you find this podcast, as well as on our website, of course, thereactshow.com.
So speaking of Discord, that's actually where the idea for this episode started. So I just
want to say thanks to this MC Kraken on Discord for the initial questions about web application
security and feedback on my early outlines of this episode. Right before we jump into the
main show though, I do want to give a real quick disclaimer. You are responsible for security
on the things you work on, not me. This episode is just meant to be a- primer on web application
security and is absolutely not an exhaustive resource and is not a replacement for doing
your own security analysis on your own projects. My goal is to provide a quick overview so you
can know where to start and so you can dive in deeper on your own projects and also just
to outline methods and approaches I take to in general keep web applications more secure.
And Of course, just like my coverage of this topic is not entirely exhaustive, security
will also never be exhaustive either. Security will never be 100%. Every system that we build
or work on will have holes. The goal of security is not to make a perfectly secure system that
is unreasonable and essentially impossible. The goal is to make the cost of attacking a
system vastly outweigh the rewards. If someone, like for example, if someone breaks into your
system to steal user data but finds out you don't store any user data, Well, you win there.
You've made the reward essentially nothing, in that context at least. There might be other
reasons they might break in. But yeah, the whole point of security, and I think this is something
that if you're not super familiar with when it comes to security, might be a little bit
surprising. But the entire point is not to build a perfectly secure system. It's not to come
up with perfect security solutions. The point is just to make it so it's too costly for people
to break in relative to what they'll get if they succeed. So that's where my thinking when
it comes to security is sort of comes from, which is that I call it sort of scaled security.
So trust in a system should be proportional to the hardening effort. So for example, when
you're first launching your web application, let's say you're just going to share it with
a couple friends and it's not carrying out financial transactions on people's behalf. Maybe you're
using a service for that, but whatever. The point being, you're launching a new application.
It doesn't have a ton of users. The users it does have are your friends, and they understand
this is the first version, and it's gonna have bugs, and it's not gonna work well. In that
case, it might be perfectly fine to spend very little time on security, because the trust
that people are going to have in this system are gonna be very low. So if people are approaching
your system and being like, oh, this thing is probably gonna leak all my data and it's not
secure at all, I'm not gonna put anything that I care about into it. And it's not maybe inherently
creating data that you don't want to share or something. There's a lot of cases where I think
when you're first starting, I wouldn't say all cases, there are cases where you need to care
right from the beginning. But I think for a lot of people, like, Maybe you're creating
a site to track some recipes or something and you want your friends to try it out a little
bit before you make it a larger launch or something. It's still good to be aware of security things,
but it's not as important to have a super hardened system at that point. That's where I think
of scaled security.
you know, how much trust people have in the system. And, you know, a lot of cases people
don't know how much to trust a system, but as the software engineer, we can better evaluate
how much trust they should have in a system as well, and also evaluate how much trust people
think they have in a system. And so we can scale our security based on that. So if we understand
that our software is creating valuable data, We need to scale our security to match how
valuable that data is. So that's kind of my preface to all of this is that you're never
going to build a completely secure system, but the level of security you provide should proportionally
match the amount of trust that people have in your system or should have in your system if
they understood everything it did. and proportional to the rewards somebody's going to get for
breaking into your system. I mean, if you don't have anything valuable, security doesn't matter
as much. There's still other aspects to security. Maybe somebody can take over your server and
use it to infect your users with malware. But that's still a reward somebody gets for the
security vulnerability. But the entire point of this is just that it's going to sound like
a lot probably if you're not familiar with this topic. A lot of the stuff I'm going to talk
about, it might sound kind of overwhelming. But my point is just that think of it in terms
of a scale. We're not trying to have perfect security. Not every application needs the same
level of security. We need to develop an ability to tell how much security a system should have
and engineer solutions for that. That's part of being a software engineer is understanding
these tradeoffs. I'll try to help guide us along that journey throughout the podcast, but I
wanted to start with that so that people are able to frame all of this information in a
more useful manner. One other thing I wanted to... talk about before we get into the main
meat of this episode is that I'm primarily going to be talking about technical security. I'm
going to presume you have a React-based web application, but I think a lot of this will
apply to any web application. I'm specifically going to speak to React. What I'm going to
talk about applies to everything from a technical perspective. But the more important thing in
any web application or any system is going to be your operational security, not your technical
security. Technical security is important. Operational security, though, is where you should start.
Most hacks into systems are not via breaking in, like hacking into the system. using some
vulnerability. Most systems are broken via social engineering. So our focus in this podcast is
going to be more about technical security, like how do we secure applications from a technical
perspective and vulnerabilities. But I just wanted to make it very clear that the place
you should always start is operational security. And what I mean by that is like phishing attacks
or giving people access to an admin account that shouldn't have access or that you don't
trust or those types of things. The people in your systems are always the weak point. Even
very insecure technical systems, usually operational security is the bigger deal. You need to take
care of that first. I just want to mention that and I'll make some other references to it because
it's so important, but the main point of this will be on technical security. All right, so
let's get into it. The best way that I think to start is that we need to look at where people
are coming from when they're trying to crack your system, when they're trying to hack your
system because We need to understand that before we can understand how to make secure systems.
And so I call this thinking like a cracker. Hackers, they're different than the way they
look at your systems is different than the way you will look at your system as an engineer.
I found this from, I think it was mouser.com, like a blog post. And I thought it was really
great. So they say follow paths. They will continue to follow a path until it fails to progress
them forward on their mission. We need to start getting our head into thinking in that term,
those terms. When you're looking at building a system and being that engineer, you're like,
okay, I've structured my API this way, my client this way, and you're thinking of it in those
terms. The way that a hacker thinks of things is like, oh, is there a vulnerability in the
login system? I'm going to try a whole bunch of different things to try to get into this
login system. Is there something I can learn about the way the sessions are organized or
the session tokens, or is there a weakness in the SSL certificates? Can I intercept communications?
they're going to, you know, the way they think of it is, okay, how do I break into the login
system? And they might try a bunch of things they can't break into the login system. Then
they might move on to something else. Oh, can I, you know, break into this other part of
the system? Can I directly break into the server, you know, using some other program that the,
you know, is running on the server, not the main web application? Is it not firewalled
off? I'm going to port scan. And so that's the way that hackers... are really looking at your
system. And so it's important for us to put on our hacker hat, so to speak, and think about
systems that way as well. And just a note throughout this episode, I'm going to sort of talk like
the hacker is a real person sitting at a keyboard, typing this stuff in, trying to break into
your system. And that could be a thing. Generally though, all of this stuff is encoded in bots.
that try to do it automatically. It's not, it's kind of not relevant because the people that
create the bots, you know, create them to do the same thing that they would do if they were
sitting there in front of, you know, at their keyboard, right? I just want to note that.
It can matter in terms of like how quickly somebody is able to do something. You can make a bot
that like tries to break into your system by guessing random passwords, right? And if it
can submit 10,000 requests per minute or whatever, that's going to be, open up vulnerabilities
that wouldn't be there if somebody had to manually type in a password every time. But that's not
so important. Like once you get into this and start thinking about it, it doesn't matter
as much. So I'm just going to talk like it's a real person, but you can imagine it's actually
a bot sometimes, right? Anyway, so going back to our like, hackers follow paths. Another
part of this is that it's really important to try to get assumptions out of your head and
just assume that all of your assumptions will be wrong. In fact, this is generally the place
I start. So if somebody says, hey, can you perform a security audit of this system? I'll look
at the code and be like, okay, what are the programmers assuming? One great example of
this will be a lot of times on the server side, they will assume that the client they're talking
to, the web application they're talking to is trusted. When you're programming the server
side, you're not usually being like, okay, what happens if somebody creates a duplicate of
this website that's malicious? and connects to my server and starts sending me requests.
You might, in general, think like, okay, let's make a secure API, right? But usually what
I find happens is people will be like, oh, okay, we'll take these couple security measures to
make our API secure. And then from then on, assume that the web application they're talking
to is the one that they wrote.
Okay, they're assuming requests are going to come in this order. What happens if I don't
send in requests in that order? Can I just jump ahead in the login process, for example, and
send a request that the server thinks is going to come after three other requests? And so,
yeah, I think when we're looking at how to think like a hacker, this is by far the best place
to start. all of your assumptions are wrong. And even if your assumptions happen to be right,
it's a really great practice. For example, one way you could look at a system is be like,
let's assume that our HTTPS layer, our TLS layer, our security certificate system doesn't work.
Let's assume it's broken. Normally you assume that works fine, right? And maybe it will work
fine. Maybe there's nothing wrong with it. But part of this learning process is to be like,
okay, let's assume we broke something. Let's assume we implemented it wrong or there's a
bug in whoever wrote the library that we're using to provide that. What happens then? If
somebody's able to get past this, what do they get? Where can they go next? And so thinking
like a hacker, this is the way that I usually like to... think about it. It's like, oh, let
me look at this system and just start making a bunch of assumptions that things aren't the
way they seem to be. And so that could be like, let's assume we have a bug here. Let's assume
somebody can view people's bank account numbers. Let's assume somehow there's a vulnerability
we didn't know about and people can view this data in our database. What happens then? What
can they do with this data? That's the way I like to frame it. That's the place I like to
start when we're getting into all of this. So it's like some other examples might be, don't
assume that user data will only be used in the original code path. And this is another part
of security and that is, don't assume that future engineers at the company will make the same
assumptions that you do and think in the same way that you do. going back to this user data,
you might be like, oh, I don't need to sanitize this data for cross-site scripting because
it only ever gets used in this specific way, which never gets sent back to the client, well,
maybe one year later, a new engineer implements a feature that sends the data back to the user,
not realizing it wasn't sanitized. They made the assumption it was. And so that's where
I just try to get in this mindset whenever I'm... doing a security audit or writing code like
this where I'm like, okay, let's assume this fails even though I think it won't. What happens?
That will really help you just start learning. We don't necessarily need to do anything with
that yet, that information, but it's important to just spend some time practicing that. One
of the best things you can do when you're doing this is even spend looking at code, looking
at your own sites, and just trying to get in that mindset of how can I think like a hacker
to hack into my own systems? This will absolutely pay off. It will really help start retraining
your brain to think and view your code in a different way that will help you create more
hardened systems. Another aspect to mention when it comes to assumptions is that I find
a lot of engineers assume for some reason that people hacking into a system, they just exploit
one thing. That's almost never the case. Many attacks involve multiple attack vectors. For
example, maybe the hacker's looking at your system and they say something like, oh, look
at this. It doesn't verify phone, like the phone number matches the account. It only verifies
the email address. I can use that to change my account to someone else's phone number.
I don't know what that will let me do yet, but I'm going to find out. And so, you know, maybe
they make that change, they get somebody else's phone number, and then maybe that allows them
to bypass the two-factor authentication or something like that. So when you were initially programming
the, you know, form to update somebody's profile and set the phone number, you were just assuming
that, you know, somebody setting this number an authenticated user for this account, not
realizing that your API just lets anyone send a request in here and update the phone number,
right? And so that's kind of what I'm getting at. And usually, it'll be like usually people
break into a system using something that seems really minor. You're like, I don't need to
secure that because it's not that important. But they get to that point, which allows them
to exploit something else that is more major and more major and more major and they can
take over more and more of your system. So it's important to also when you're thinking about
this path-based hacker approach to keep that in mind. It's not just one vulnerability. It's
multiple. Another example like the session token is stored in the cookie. Is there a way I can
get access to cookies? Oh, look. I put data. I put data into this text field.
into a form and it shows up on other people's feeds, right? And it isn't escaped. They didn't
escape this code in some way. So I could add some JavaScript on maybe my profile and then
it will show up on everybody else's feed, which means it's getting run in everybody else's
browser, and that JavaScript will let me read their session token cookie. That's an example
again of how you can sort of go through this path. You have a vulnerability essentially
in two different places here. So always remember, defense in depth, never rely on one piece being
secure. Try to secure all the pieces. Try to make the assumption that somebody broke through
the earlier layers. Attacks are usually a chain. They might start with something that seems
hardly worth securing, but once you get past that, you get to progressively higher levels
of trust within the system. All right, so I like to think of things in a sort of methods-based
approach, right? If you've listened to this podcast, that's the way I like to do it. So
that's what I want to talk about next is how can you do this in sort of a methodical way?
So we've talked about how to think like a hacker, but how do we actually apply this? So this
is what is called the threat modeling process. If you really want to get into this, you can
look up way better information. This is just an overview. But this is kind of generally
the method I would use if I'm trying to do some sort of security audit. So to do a threat model,
the place that you generally start with, there's other sort of ways to do it, but I like to
focus on what are called the assets. Identifying assets. Assets. So assets are things a cracker.
might be interested in. Maybe this is personal data, session tokens, bank account numbers,
anything that has value to someone else in your system. These are your assets. So you can just
list them out. That's what I have to do. Just write a big list. The next thing you'll want
to do is identify and rank threats. So then you can go through that list and be like, Okay,
what are the actual threats to this data? So let's say it's personal data. Somebody could
steal that data, disseminate it on the internet at large. And part of this is like ranking
and sort of writing how big this threat is. So let's say you have very minimal personal
data, a first and last name for a site that is just for storing recipes. Well, if this
data gets leaked to the internet, I mean, it's not great to leak anything, but what value
does that really have? I mean, maybe not a lot if that's all it is. But maybe you're Ashley
Madison and you run a website for people to cheat on their partners. Leaking their first
and last name could be absolutely devastating. So this data might be worth a ton. You might
be like, okay, this is a really, really valuable asset. We're going to give this a high rating.
So yeah, I usually will go through that list of assets and just sort of it doesn't You know
depends on how thorough the security audit you're doing if you're in a new web application You
know it can just be pretty quick like oh, yeah, this is really important. This is not as important
Whatever so that can be yeah Many different things too. It could be like oh, what if somebody
can run their own code on our server? What if somebody gets admin access? You know definitely
if you're gonna do this you can look up like lists that people have created that will give
you more ideas. But yeah, you can have this asset list, you can identify and rank threats.
Another example might be a session token could be used to impersonate another user on the
system. What does that give them? What does that let somebody do? How much of a risk is
that? Or a stolen bank account number could be used to steal someone else's money. I mean
that seems like you want to do it in a sort of relative way. Maybe there's a bigger risk
in your system and that's number three on your list. The whole point here is just to sort
of give us a way to prioritize our efforts. So the next thing is a threat analysis, which
is basically the probability that a threat occurs times the cost to the organization or the users
of your system. So this is, you know, being like, okay. We've got our personal data and
maybe it's worth a lot. How likely is that to actually be disseminated, broken into, stolen?
Maybe you encrypt the data and you take all sorts of other safeguards against it or something
so the probability you feel like is lower that it might get stolen. Well, that might move
it down on the list of your priorities. Basically at this point, this is where we start also
exploring attack paths. So we'll look at our system and be like, okay, if somebody breaks
into the login system, that gives them access to this. If they're then able to elevate their,
you know, login to an admin account, that gives them access to these things. Um, but to get
there, they need to break in three systems maybe. Um, and so you're like, well, that seems the
last likely. So that's basically just this process. You can look it up if you really want to know
it, but the general idea is just like get an idea of the paths somebody would have to take
through your system to get to these assets and rate how likely that is times the cost of them
actually succeeding at it. Another thing I like to do at this stage is just to sort of note...
how each of these paths is mitigated or not mitigated. Maybe part of your system is you
tell users, hey, we don't protect against this. That might be valid. But yeah, so that's what
I would do at this stage. We have our threat analysis. And so that's sort of a very abbreviated
version of threat modeling. But this is sort of where I start. I look at a system. I identify
the assets. I rank. the threats against those assets, and then I do the threat analysis where
we figure out sort of the cost priority and the paths a hacker might take through your
system. Once we have this data, we can prioritize our security efforts. We can look at the top
of our list and be like, okay, this is the most important thing for us to secure. These are
the paths we can imagine somebody taking to get to this asset. What can we do to further
mitigate this? Maybe we're like, OK, we need to do better. You can look at each of those
paths and be like, OK, what can we do to stop somebody getting through this layer? What can
we do to stop somebody getting through this layer? So this provides a nice method of doing
a security audit and making your system more secure in a methodical way. All right, so next
I want to talk about basic attack vectors. And this is definitely not an exhaustive list,
but when you're writing a web application, there are very common attack vectors that you should
be aware of, at least. And first, I'm going to talk about operational security and social
engineering. Then we'll get into the technical things. So something that I want to note that
a lot of people forget is it's super important to secure your domain registrar, your host,
things like that. If somebody can break into whoever you've registered your domain with
and take over your domain, boy can they really do a lot of harm at that point. And so that's
not code you've written, but keeping that secure is super, super important. So remember to keep
those types of things in mind as well. One thing that I highly recommend you do is use a hardware
security key. I think, I honestly think most engineers should be using them. So a hardware
security key is basically a physical device that's got cryptographic stuff on it. I'm not
going to go into all the details, but it essentially means that if you use it for two-factor authentication,
things like that. It basically means somebody can't get into the system unless they physically
have that hardware key. And that eliminates a massive host of vulnerabilities when it comes
to things like securing your email, your domain registrar, things like that. So I really recommend
all engineers, software engineers that work basically on anything should spend some time
understanding how to use a hardware security key or at least like a two-factor authentication
app on their phone which is also good. The reason why I really recommend a hardware security
key though is because then you can use WebAuthn, Fido, you can look these things up but they
essentially provide phishing protection as well if you use them correctly. Like when I plug
my hardware key into my computer and I log into my domain registrar, I do my username and password,
then it says, okay, verify with your security key. And you can't get into the system unless
I enter a code that does cryptographic stuff on this hardware security key, and I physically
touch it, you know, hit a button on it. And using public key cryptography, that process
verifies the site the request is going to is actually the site I think it is. It's the site
I initially set up this hardware security key on. And so this is super valuable because it
eliminates a lot of the phishing attacks that normally take over people's accounts where
they think they're on their domain registrar's website, they enter their username and password,
and then maybe even... They enter in the two-factor authentication code from the authenticator
app on their phone. But turns out it's a completely fake site. And this fake site in the background
is entering all these details into the real site and giving that hacker access to these
things. If you use a security key correctly, you eliminate a lot of these things. So I definitely
recommend that. It's kind of a pain. It's extra work, but in my opinion, very worth it. Anyways,
you can do what you want, model your own risk and all this, right? I'm not going to tell
you what to do, just a recommendation. One other thing I want to mention is secure your email
and anyone else at the company that has an important email account. Don't use SMS for multi-factor
authentication, two-factor authentication. It sucks. A lot of people though don't remember
to secure their email. And I think... even more important, secure that this one is, this one
is hard. But if you can make sure that people with important emails in your company also
have them secured like the CEO because a great way that people social engineer their way into
systems is The CEO is like, I'm not super technical. I can't handle all this extra security measures.
And so they don't really secure their email account. Somebody hacks into their email because
it's easy to hack into. And then they send out emails to other people at the company telling
them to do things, impersonating the CEO or CTO or somebody high up in the company. And
A lot of people, like engineers, they're probably not going to question that. They get this email
from the CEO being like, oh, this is really important. Can you take care of this by XYZ
time and basically let a hacker into the system? So just a word of warning, anyone with like
authority in a company, they need to secure email and chat programs, anything like that.
Anyways, all right, done with operational security. Let's get into basic technical attack vectors.
So these are pretty classic, I'm not going to go into anything wild at this point. There's
a million ways you can hack into a system, but we're going to talk about just a few major
ones. So database injection. If you have a database of any kind in your system, you run the risk
of injection. So what is injection? Basically think of it this way. Let's say you have an
SQL-based system. And you... put user data into the SQL queries. Let's say you just made the
queries up with a string, you interpolate or append user data into these queries. Well,
what if a user of your system puts SQL into their data? Then you will be running the attacker's
SQL code against your database. That's horrible. It can let them... getting access to basically
everything. This is super classic. Not gonna go into all the details again, but database
injection is one of them. How do you avoid that? Don't create your SQL just using plain strings.
Basically, most libraries today will provide a API and it'll be the main API where you pass
in user data separate from the query itself. It's parameterized is what it's called. Like
if you use an ORM, Object Relational Management System, that type of thing, generally it provides
us for it. But take like five seconds and look into your system and look at how to do this
right. Don't make the mistakes that people have made in the past and do it wrong. This one
should be pretty good these days. Just take five seconds to understand how your system
works. The next one is cross-site scripting. And when it comes to React, This is one definitely
to be aware of. So cross-site scripting is basically an attack vector where an attacker is able
to run their code on your website, within your web application. This is something I mentioned
earlier. So somehow an attacker, maybe they put JavaScript into a form field on your site,
and this JavaScript gets served up to other users in some way. and runs on their system
in their browsers. This is very bad. Again, this can basically give somebody access to
everything in your system eventually or do lots of other nefarious things. Very bad. Cross-site
scripting. It's a very classic attack vector. But specifically, when you're using React,
there are a couple things to be aware of. Never ever put... data that users have entered into
your system inside dangerously set inner HTML, I think that's the property name. Unless you
really know what you're doing and you really actually understand cross site scripting and
you know how to sanitize that data, just don't do that. And the other thing is don't put user
data into attributes of components in React. Things that could... be passed through as like
a style property or something like that. This one's a little bit more nuanced in understanding
it, but from a blanket perspective, if you're using React, you can put user data in like
the body of a HTML tag. Like let's say something eventually gets rendered out as a div and in
the body of this div, you put some data the user has entered, their name. whatever it happens
to be, that React will always automatically escape for you. So you don't need to worry
about cross-site scripting if you put... In fact, not only do you not need to worry about
it, you just need to be aware that React will escape this for you. So you don't need to escape
this data when it goes into your system necessarily. In fact, I'm going to talk about that a little
bit more in a second, but... The thing to be aware of is if you just generally use React
in a normal way where you just render components and include user data in that output, you'll
be fine. Just don't put it in attributes. Don't be like, style equals dollar sign user data
or whatever. Don't put it inside attributes. Don't put it inside dangerously set inner HTML
and you'll generally be fine. React is great. It takes care of this for us. Originally using
things before React, this was not always the case and boy, it was much easier to have cross-site
scripting. But yeah, if you're using React, it's pretty straightforward. I have though,
definitely run into cases where people have put user data into attributes and even one
time I had somebody putting it into dangerously set innerHTML and they thought somehow that
was more secure. No, don't do that. But yeah, so that's cross-site scripting. Another one
to be aware of is request forgery. So this is like,
one example would be server side. So don't use user data to access local resources. So let's
say you had this brilliant idea where you were going to take someone's name and use that as
the file name on your file system to store some data about that user. I mean, I don't know
why some of you do this, but just to give you an example,
what you're going to do then at some point in your system is open up that file. Well, if
somebody can put any data they want for their name into that field, you're basically going
to be running a hacker's code locally on your system. Again, you can. look into how this
works and maybe you can escape it. So, or create a whitelist or do this in some way securely,
but by default, you need to be careful whenever you're opening a network address, a file on
your system, a database, credentials, anything local to your system or to your infrastructure.
Don't put user data into it because then somebody can use this to forge, you know, to local I.O.
within your system. So that's another one to be aware of. This can also be clients forging
requests to the server, clients, your client forging requests to somebody else's server.
Just another vulnerability to be aware of. Alright, so those are the main web app based threats
that I'm going to cover. There's a million more but that's always basically where I start with
for historical reasons, those things have been exploited more than basically anything else.
So definitely make sure you get those things right. Luckily, libraries often do a much better
job today than they used to in terms of protecting us against those things, but at least spend
a little bit of time to understand what your library does to protect against it and how
to use it effectively. Next, I'm gonna mention a couple other areas that I... find people
often make mistakes on or don't know about when they're getting into this. So another one would
be log security. What are you putting in your logs? This is kind of the same as like don't
put user data into logs because,
or at least you got to be careful because a lot of times logs aren't treated very securely.
And so this could be an easy way to leak user information or even let hackers run code on
your system. A great example of this, which it wouldn't be running on your system, but
something people don't realize is that in JavaScript, console.log and console.error essentially are
the same as eval. So anything that, you know, if you put user data into a console.log or
a console.error, that essentially allows somebody to run arbitrary JavaScript on your client.
Maybe this doesn't matter, depends on your system, but it's just something to be aware of. And
same on the server side. Be really careful what you put into logs and be careful what you do
with logs. I think people forget about this a lot. They're just like, Hey, let me log this
data. I want all the data. Log it all out. Um, that could be a threat. Be careful what you
do with logs. Um, And the next thing is a big one, I think, for people getting started, which
is authentication and authorization. You got to make a login for your system, right? Most
systems have a login. This is where so many mistakes have gotten made. I have found so
many vulnerabilities in login systems. A lot of times, people get sort of the basics right,
like username and password or. They're using some library to do this for them. But oftentimes
where they go wrong is identity. And identifying who a user is or updating data in the login
process, there's just a million ways you can do this wrong. And so my advice is always keep
this as simple as you possibly can. Even if you're using some library to handle this stuff
for you, just keep the implementation of that library, that code, just keep it super simple.
Just make it only about one thing, logging in. Only one section is authorizing the user to
get into the system. The next section might be identifying the user and setting up their
session token, but just keep it really simple. What I have found is these login systems just
balloon. It's really bad. I think what happens is people build the initial one and then somebody's
like, hey, can we add this feature to automatically do x, y, z after a person logs in? And so people
will start putting other code into the login routines. And this is extraordinarily dangerous.
Just don't do it. Make that a separate thing, completely separate. Don't update data on login
automatically unless you're super, super careful. I see this all the time too. People will be
like, oh, our first set of users didn't have this data set. We're going to set it for all
users when they log in or update a phone. Maybe somebody puts in their email and their phone
when they log in and somebody like as a second factor or something. And or I've seen like
they log in with both, I don't know, multiple pieces of data. And people are like, oh, we're
gonna save the user a step. We're gonna update this data for them when they log in. Don't
do that, never do that. It's like the worst thing ever. Cause without realizing it, you
often open up vulnerabilities where it allows a hacker to. inject their own information in
those paths and essentially log in as somebody else. So just something to keep in mind, make
your login systems, your authorizing users and identifying users as simple as you can. This
one is, I don't think I can stress this enough. People, this is by far the place that I've
seen the most issues. Like generally at this point, those other things like database injection,
cross site scripting, that kind of stuff is I don't usually see glaring issues here. People
are generally aware of it, and libraries do an OK job of handling it. But when it comes
to actually implementing this stuff, especially for authentication or authorization, giving
somebody admin access, people overcomplicate this. They add features to it. Just don't do
it. It is a recipe for disaster. All right, so enough of that soapbox. Let's talk about
data validation. So I think this is the user on Discord, this is initially what they were
asking about is how do I validate data or filter data or escape data so it's safe to be in my
system. And this is where it seems like, I think I started here too where I was like, okay,
I get user data, I need to escape it right away and make sure it can never do anything dangerous.
And this can actually be dangerous in and of itself. Let me give you an example. Let's say
you have a system that stores data using SQL or something, right? Then later on, you send
this data back to the user, you render it using React or something, right? The way that you
escape data for putting it in an SQL query is different than the way
in React on the web. And in this case, maybe there's no conflicts, but maybe you have a
markdown parser, whatever. There's many different systems, right? And the thing is they all need
data to be escaped differently. So the approach that I think is safest is only escape and like
you should escape and then unescape data. when it goes in and out of systems. So when you're
putting data into your database, at the database library level or whatever, that should be automatically
escaped for you in the specific way the database needs it to be escaped for. And then when you
take that data out of the database, you generally want to un-escape it back to its original form.
So that at every layer, you're working with the data in its original form. And the reason
why this is important, like initially when I got into this, I was like, oh, okay, I just
want to escape it once right away when the user enters it, right? And then I'll be good forever
and I can just rest easy being like, my data is validated and it's good. I don't need to
worry about it. But what can happen is the way it gets escaped for one system could create
vulnerabilities in the next system's escaping thing, right? So let's say one system escapes,
you escape data by adding a double slash in front of it or something, or adding a slash
in front of slashes. Well maybe the next system sees that and thinks, oh, that's how you indicate
safe code to run, you know? So you just need to be super careful to not end up in those
situations. And that's where I tell people escaping and invalidation. should occur at the layer
that it's relevant for. Don't try to anticipate everything at the top layer. Don't be like,
okay, I'm going to escape it for SQL here, I'm going to also escape it for JavaScript here,
and whatever, right? Don't try to do that all up front. Do it at each layer and do it automatically.
And another reason for this is... You don't know how the data is going to be used in the
future. Somebody comes in later and might add a layer that does something that needs escaping
or on escaping, and they might not realize what you've already done to the data. Generally
things are done right. You shouldn't really need to worry about this. But I think we're
still at a state where you absolutely do. I have absolutely seen this many times over.
People are like, okay. the user enter data into the form, I'm going to escape it for rendering
out in React at this point. That doesn't make any sense, don't do it that way. So yeah, that's
my spiel on data validation. Now that's in my mind separate from, I guess I would call that
data sanitization. There's also what I would call data validation, like. Maybe when somebody
puts in their first name, we don't want it to be a bunch of random symbols, or we don't want
it to include certain things. In general, just because maybe it makes it confusing. Maybe
you're like, oh, people should only be able to enter their username as ASCII characters,
that way they can't use a Unicode character that looks like another character to try to
spoof names or something. That would be the way I would think of data validation. And that's
something you can do immediately when the data goes into the, you know, when you get it from
the form, from the client, you know, whatever. The other part is, and I forgot to mention
this, should have mentioned this, data validation should always happen on code that you control.
So validate and sanitize data only like on the server if you have a web application infrastructure.
Never, ever, ever rely on client-side validation or security. You can have client-side validation
where on the client you reject things and you tell the user, hey, this isn't valid. You need
a password that's longer. You need a password that includes this or whatever. But you never
trust that. You can't trust that because somebody could create their own clients, send their
own requests, You always have to do those things on the server or code that you control. Alright,
the next thing is libraries and external services. So this is a big one that people often don't
treat the same as the rest of their code, but if it's involved in your program, I consider
it from the security perspective to be the same. So keep your libraries up to date from security
vulnerabilities, which is a- pain in the JavaScript world, but that's how it is. That's an obvious
one. Keep things up to date. The next one is check security before adding things as dependencies
to your project. This one is kind of a pain. A lot of engineers I find, especially frontend
engineers, don't do this and don't want to do it. If you care about security, you should.
So you're like, hey, I want this library to do this thing for me. Take a few minutes at
a minimum and do a quick threat analysis. Look at the documentation, et cetera. I'll tell
you what I look for. So if I'm going to add a library or a service to my project, when
it comes to security, I look for a number of things. The first is, does this library have
documentation on how to implement the library securely. Most security vulnerabilities actually
come not necessarily from a vulnerability in the library itself, but people implementing
it wrong. Having really good documentation there that says, okay, this is how you do it securely
and correctly, don't do these things. I look for that in the docs. If it doesn't have that,
that makes me a lot more cautious Did the programmers think about security at all? How do I do this
securely? Are there things I should be aware of? If it doesn't say, I mean, that's a big
red flag to me. So good documentation will also include the types of attacks that the authors
are purposefully not addressing. And this is always gonna be the case. They might be like,
hey, this is something we don't feel it's our responsibility. You need to handle this on
your end or something. But basically at this stage, I'm just looking, does the documentation
include this information? If it doesn't, again, sort of a red flag where I'm like, do these
people know anything about security? Are they handling it, right? It's just sort of one of
those things that makes me spend longer researching it, you know? Let's see, the next one would
be, do they have a public policy in place for how they handle vulnerabilities? This is another
good indicator that they're paying attention to security. Kind of the last general thing
I look for is do they have good documentation on a high level approach they take towards
security and does it make sense? So basically everything should have this in some form or
another, even if it's just, hey, we don't really need to do anything for X, Y, Z reasons. It
just needs, there needs to be something. And like for say a library that has to do with
authentication, it should be like, okay, this is how we store data. This is how we manipulate
data. This is how we defend against these common attacks. That type of documentation needs to
and should exist in a high quality library or service that you might be integrating. As a
quick example on Discord, it was asked about the auth library and identity service called
Clert. like C-L-E-R-K. So I did this analysis real quick and my results were there were not
good documentation on how to securely implement it. It was pretty, almost nothing. And so that's
a huge red flag to me. There should be extremely explicit data on an authentication library
on how to securely implement it. That is like the first thing it should have is. To do this
securely, do things in this way. Follow these things, don't do these things. It did not have
good documentation on it, or if it did, I couldn't find it, which is just as bad. That needs to
be front and center. So I didn't like that. I also looked into sort of the high level approach
they took towards security, and that gave me some sort of, maybe not red flags, but like
orange flags, like sort of warnings too, because. This is something that you might not know if
you're not as familiar with security, but that's fine. They use some relaxed security policies
around session token storage that require extra work from the people implementing the library.
That in and of itself might be fine, but I don't feel like they highlighted this very well.
service and the company themselves. Since they're handling everything including session tokens,
identification, that type of stuff, but they really didn't provide hardly any details on
how they internally secure this information and manage it. They might do a fantastic job,
but I couldn't figure out if they do or don't or if they care or don't care. So this was
a pretty big red flag to me as well. And My overall conclusion was I would be pretty hesitant
to use a service like Clark for these reasons. I think there are better libraries and systems
available within the JavaScript ecosystem. Maybe it's fine. Maybe for your system you're like,
okay, all those things are fine. I don't really care. It's not a big deal. But personally I
was like, okay, there's a lot of red flags here. Anyway, so that's my approach for. how I look
at integrating libraries into my project. And I'll briefly touch on what I call external
services too. So this might be like Google Analytics or something where like they just give you
some code to put in your project or a library or whatever, but they're sort of like clerk
where they are handling everything for you. Your app might be the most secure thing in
the world, but if you're... giving other services access to run code on your system or whatever
that might be, they need to be just as secure as your thing. So you need to perform a threat
analysis with them or in some way keep that in mind. Let's say a few other things I'm going
to go over real quick are keep backups, but also do it securely. If. something goes wrong,
it's really nice to be like, oh, okay, maybe we don't understand everything yet, but we
can restore from a backup. Or you can use the backup to see what, maybe some data got changed
by a hacker, you can use the backup to look at that. So in general, backups are always
good to keep, it's also important for security. Another thing is keeping logs and intrusion
detection. Can you... find out if somebody did hack into your system. How do you know? What
did they do when they were in your system? So this is another aspect of security that often
gets overlooked. It's important to have mechanisms in place to detect when somebody has done something
unusual. Not gonna go into all that, you know, right now what that means, you can research
it yourself, but something to be aware of. Another great tip. piece of advice is don't store data
that you can't secure or that you don't want to put the effort into securing. This is an
easy one that I think a lot of people forget. When you get a feature request to capture some
new data, be like, hey, is it worth it from a security perspective? Maybe this data is
super valuable to a hacker. Are we going to invest in actually keeping it secure? And then
the last thing that I want to talk about is my approach to writing secure and safe code.
It's hard to keep all of this in mind all the time when you're writing code, right? But I
think there are a lot of things that we can do as engineers to just in general, write better,
more secure code. So what I always advocate for are systemic solutions. The first thing
is don't rely on programmers remembering to do things. If there's some part in your application
where you're getting data from the user and a programmer needs to remember to call some
method to sanitize that data, that's horrible. Don't do that. Figure out a way to architect
your system so it gets handled automatically at that layer or whatever it happens to be.
A lot of people, again... don't do this and it really bugs me because it's just too easy
to get it wrong. And, you know, whether it's in refactoring or some other engineer that
doesn't know about it, or you just forget. Don't rely on manual methods. Create systemic solutions.
Or create a library that just guarantees it always gets sanitized correctly at that level
without the programmer having to do anything special. That's the... absolutely critical
to just in general writing robust secure code. Another approach I use when writing code is
that things that seem like you can trust them but shouldn't be trusted should be explicitly
called out in that way. So an example would be a user supplied crypto address that money
can be sent to. where the expectation is that whenever that crypto transaction is executed,
the address is verified at that point. It's not necessarily, like you might verify it when
the user enters it into the system, but the security of your system relies on it being
verified each time a transaction actually occurs.
label that field in the database, like untrusted underscore crypto address or something. Just
so that, you know, anybody that doesn't really know how it works sees that and they're like,
oh, untrusted. What does that mean? You know, and maybe that you have a code comments and
other people on the team that knows what that means. But I just take this really defensive
approach where anything that seems like you can just grab it and use it, but you really
can't is explicit. everywhere in the code base. This helps a ton. It just makes it so it's
a lot harder to make silly mistakes where somebody's like, oh yeah, I just plopped the address in
here and created the transaction and sent the money over. And then somebody else is like,
what? You can't do that. You were supposed to do this other thing first and whatever. And
maybe you created this library to handle it for you, but somebody comes in to refactor
it and they don't know about that, you know? They should, but hey, the CEO wants this done
yesterday, right? We all know how it goes. So I try to like always do this in cases where
it's surprising, where you're like, oh, this is sort of unusual or surprising. I try to
call that out in any way I can. Another thing I do is assume the worst case scenario. So
like, I'll When I'm working on some level of the code, I'll just assume that all the security
before it failed and somebody is able to access the data. I look at it and go, what are the
consequences? Is there anything we can do to store less data or separate some of the data
into a different system or encrypt it per user account or something like that? So I just try
to have this general approach of, oh yeah, are other security mechanisms failed? What can
we do to make this specific layer more secure? And of course, like I mentioned before, complexity
is the enemy. Do not make things vastly more complex to try to make it more secure. Instead,
try to design better code. Complexity just creates more paths to secure and more code to understand.
And I call this out because the tendency when you find a potential vulnerability is to be
like, oh, we can plug this by adding this additional mechanism. But now you have to secure that
additional mechanism, which increases the surface area. And it's not like a linear thing either.
It's kind of like an exponential increase, the more code you add. So in my opinion, I always
push for like, you know, taking a deeper look at how you can re-architect the code to eliminate
the vulnerability altogether or secure against the vulnerability in a more systemic fashion.
This is really important. A lot of times, you'll find a vulnerability, and there'll be pressure
to just solve it right away. And the initial instinct will be, oh, let's add this extra
layer. Let's add this extra stuff. And maybe. That's what you do temporarily to just get
things moving and sort of more secure. But it's a really bad idea long term. Just try to resist
that urge. Try to be like, OK, is there some way we can do this where we don't need to make
it more complex? Complexity around security code is always a big red flag to me. If I get
into some code and I'm like, wow, they made this so complicated trying to make it secure,
it's like, I'm really unsure this is going to be very secure at all. There's just too much
to keep track of. And another just sort of final reminder is don't write or implement your own
cryptography. Unless you're crypto, I was going to say a crypto expert. That means something
different these days. Yeah, don't write this stuff on your own unless you know what you're
doing. Ideally, always use well-tested, well-documented libraries that handle security and authentication,
login, things like that for you. This is just, yeah. I don't find a lot of people end up trying
to do this because it seems overwhelming anyways, but I have. So yeah, do your best to not write,
just don't write your own crypto. Let, let. People that are experts at it do it. If you're
an expert, you'll know it, I guess, and you're fine, but for the rest of us, like just don't
do it. Don't be like, oh, this is too slow. I need to reimplement it in my language. That's
faster, whatever. Just don't do it. It's a bad idea. You're only gonna create headaches for
yourself. Use stuff that is well-documented, well-tested, has a good track record. Don't
do it yourself. All right, well that was a massive dump of information. Hopefully it's useful
to you and it gives you at least a starting point. Yeah, if you wanna know more about a
specific part of this, feel free to send me a message from the reactshow.com or come join
the Discord server. I'd love to hear more or if something wasn't clear, definitely let me
know. Yeah. I just want to thank you all once again for joining us. And if you've made it
this far and you want, definitely check out the premium feed, like I mentioned at the beginning,
or joining us on Discord. We'd love to have you there. Anything that we can do to support
each other, something I'm all for. Anyways, thank you so much for making it this far, joining
us. And and I hope you have a fantastic rest of your day. Bye.