20. Backend Security: Everything You Need to Know
Sriniously
2:50:03
20. Backend Security: Everything You Need to Know
Today I want to talk a little about security. I don't have to mention this but from the name you can derive that security of an application and in our
context security of a backend application is one of the most important things that you as an engineer should be aware of. I'm not saying that all the other topics that I covered in this
playlist are not important, but security is something which if you don't pay a lot of attention to has a lot of destructive effects on your application.
Destructive financially and when it comes to your business, when it comes to your resources and in turn everything boils down to money and finance. But security itself is a huge domain. You
can talk about different aspects of security depending on which context you're talking about. We can talk about browser security which will talk a lot about HTML and your cookies, local
storage, all these different components where vulnerabilities can arise. We can also talk about your network security which means the transition layer with
the HTTP, HTTPS and your encryption, compression all these components come. We can also talk about server security. I'm not talking about backend security
where we are only concerned about our app our particular code base and our particular process which will be running inside an operating system. When I say server security I mean the operating
system in itself when we deploy a backend code in a server we deploy our particular application which runs in a process inside an operating system. So
that operating system itself has a security context. You can talk about that also. And obviously we can also talk about our back end which means all
the vulnerabilities that can arise because of our app because of the code that we have written in our back end. And if you start to tackle and if you
start to discuss security in all these context, it quickly goes out of the scope. You don't need to know all these things about security. Of course, it is
always better if you are aware of these things. If you are interested in these things, there are a lot of reading materials that you can find with just a simple Google search. But for this video
where we mostly talk about practical things where we mostly talk about things that are in the context of backend engineering. In this context we have to
limit ourselves to a particular set of vulnerabilities a particular set of rules that we should follow to ensure security of our application. And the last thing that I want to mention before
we start this video is the aim the purpose of this video is not to teach you all the different fancy security techniques in one video so that you can
just go ahead and implement all of them in your application starting from tomorrow. That is obviously not going to happen. As I have mentioned security is a vast domain and you have to do a lot
of reading yourself. You have to do a lot of research. We have to build the mindset to build a truly secure application. That sentence itself does not make any sense because no
application can ever be truly secure. As technology evolves, as our libraries evolve, as our programming languages evolve, there will always be security vulnerabilities. But anyway, we have to
try our best. So the purpose of this video is not to teach you fancy security techniques. As I have already mentioned in my authentication and authorization video, the goal of this video is to make
you paranoid about your application, about the code that you write and give you a mindset to always keep security in the back of your head whenever you are
creating any kind of application. So that is the purpose that is the goal of this video. Of course, we are going to talk about a lot of security threats, a lot of security based techniques to
prevent all these threats. We'll also look at a few demos to understand how it works on code level etc etc. But again if I miss some major component when it
comes to security if I miss talking about a major threat or some best practice then as I've said security is huge and as long as you are able to get
this mindset that always keep security at the back of your head whenever you are writing code then I will consider this video as successful. So with that we can start. One of the things that I
have thought a lot about structuring this video is I don't want to list and I don't want to talk about a fixed list of very abstract vulnerabilities because
that never works. Instead what I want to do is I want to give you a glimpse of how to think like an attacker. I don't want to use the word hacker because
there is a lot of cringe already associated with the word. So we are going to use the word attacker for this. And the fact is these attackers, people
who wish harm upon your application in any way possible, they don't really care about the framework, the library, the programming language that you have
written. They only care about this one question. The question is where did the developer make an assumption? That is the most important question and we are
going to pay a lot of attention to this question to find out why this question is so important especially in the context of security because every single vulnerability that we are going to talk
about in this video it all comes down to that single thing. A developer assumed that the input coming from the user coming from the front end would be
clean. Or they assumed that the user was who they said they were. Or they assumed that the request that they are receiving on their server on their back end was
coming from their front end the front end that they are supposed to serve. And they assume that no one would open the network tab like this. and no one will
just take a look at the network calls and no one will try to modify the parameters. Of course, these assumptions they feel reasonable when you are
building under deadline, right? Especially in startups, there is always a deadline. You want to ship features as fast as possible. You want to meet the client's requirements as fast as
possible. In that environment, it is perfectly reasonable to make all these assumptions because you are always thinking in the happy path. The user will always fill the form correctly, the
way that you are expecting them to. They will always click the right buttons and they will always navigate your app the way they are meant to. But unfortunately
attackers again people who wish harm upon your application they don't use your app the way you want them to. They try to poke at every single boundary.
They try to modify. They try to break every single input and they try to guess every one of your assumptions. So by the end of this video, you'll have a mental
model for thinking about security that will stick with you regardless of what language or framework that you're using. It can be Go, it can be Python, it can
be NodeJS, it can be Rust, does not matter. You'll always start questioning this question which is what could go wrong here in terms of security every
time you write code. With that, let's jump into our first kind of attacks that I want to talk about, which are say injection attacks. Now, injection
attacks, I'll come back to why we are calling them injection attacks, but they have been around for decades now. And and they're still very common and they all share the same root cause. And once
you start looking at the root cause, once you start understanding the root cause, you'll start understanding an entire category of vulnerabilities. So let's start with a very fundamental
truth, a very fundamental fact which is your application speaks multiple languages. So if let's say this is our app and let's say here we have database
and this side we have browser and in this side let's say we have operating system. We have our app let's say this is our backend application and at all
times we have to imagine that our application speaks multiple languages for multiple contexts. When our app, our back end tries to query some data, tries
to insert some data, tries to delete some data, any kind of interaction with data, it tries to speak to a database, right? It is a kind of interaction and
for that it uses the language SQL. Same way assuming that our back end also deals with sending static assets like JavaScript or HTML or CSS, then it also
speaks these languages, right? HTML and CSS and whenever our back end needs to perform some task in the context of operating system let's say it wants to
open some kind of file which deals with the system calls which are governed by the operating system then it speaks the language of shell shell script if you're
aware of so of course there will be other categories here depending on how big your back end is but the point is at all times Your back end speaks multiple
languages in multiple contexts and each one of these languages. They have their own grammar and their own set of special characters, their own way of separating
commands from data. And this is where most of the vulnerabilities come from. Let's say we are as a user, we are here because we interact with our back end
through browsers, right? Here is a user. Most of the time the vulnerabilities arise because a user speaks one language. Let's say this is HTML, JavaScript and CSS because we mostly
talk to browsers, right? Whenever a user speaking one language tries to cross the boundary of another language from starting from here, we interacted
through browsers and the browser interacted with our back end. And when this language tries to cross the boundary and starts entering this boundary or this boundary in those cases
mostly vulnerabilities appear and we'll talk about what we call them but skill injection excss attacks these are one of the examples of when vulnerabilities arise because of one user crossing the
boundary from one language to another because the input from the user in one particular language they can mean some kind of special command some kind of special character in the other language
and that is the root cause of most of these injection based attacks. So let's talk about one of the most dangerous and also common injection attacks which is
SQL injection. Okay, for this let's imagine a simple login page. We have a login page here. We have the email field. We have the password field and
this is running in our browser. This is governed by our browser. This is our front end and this is going to talk to our server which in turn is going to
talk to our database DB. This is our setup. The user enters the email and the password. Your server receives these two fields email and password. It makes a
request to your database to find if the user exists for this and it checks whether the password is matching or not. If it does then it returns a response
right very straightforward. Now what we want to do we want to zoom in on what exactly is the database query here. So let's say this is the database query
that got executed. This is the database query. If you are familiar with SQL that is great. It is a very straightforward SQL query. We want to fetch all the
users whose email is matching this particular user input. whatever we are receiving from the user. But if you are not familiar with SQL, this basically means that our server is asking our
database that give me the user with this particular email address, whatever email address that the user has sent us through our front end. If the developer who is building this query by
concatenating strings here the way that is done here by concatenating strings taking the query template and just dropping the user input here inside the
query then we have a huge problem. Now to understand that problem let's first discuss the happy path. As I have already mentioned, most of the times we
assume the happy path. The users are going to act like the way we are expecting them to. So let's first do that. The user comes, let's say the user's email address is Alice
atgmail.com. They type their email and they type their password or whatever the password is and they send it to our server. Now
given this particular SQL query, we paste it here. When our server receives this particular email address, what we do? We take this email and we inject it
here. Now this will become Alice whatever@gmail.com. The server constructs this particular SQL query. It sends to our database and the database is able to find a user with
this particular email. It returns it and the server finds the user and allows the user to login. Right? That is our happy path. Everything works perfect. We don't have to worry about anything. Now let's
talk about not so happy path which is the attempt of our attacker. So instead of typing a very valid email, what the
attacker does? They do something like this. They type a single quote. Then they type the word or. Then a space. Then another single quote. Inside there
is one. Another single quote. Equals to another single quote. There is one. Another single quote. Then hyphen hyphen. This is the input that the
attacker types in the email field and they send it to our server. Now what happens? As usual, the server takes this input and inserts it into this whatever
template that it is constructing for the database query. In order to understand this, let's go to a DB explorer which will be much clearer. This is our
template for the first case when we are talking about our happy path. This is what it becomes. Let's add a colon here and here select star from users where
email this becomes let's comment this out just for our reference and this becomes after constructing the query becomes alisgmail.com
this was our happy path same way let's copy template and paste it here now let's talk about what the attacker did after constructing the query this is
what the query will become. We already had a pair of single quotes here which was added by the server. Now what happens inside that we concatenate whatever the input that we're receiving
for the email. Now what the attacker did first they typed a single quote single quote then space let's type space it was or then single quote 1 equals to single
code 1 then space hyphen hyphen. Right? This becomes our final query. When we take this this whole thing and we insert
inside our template where we fetch the user information. In order to test this out, let's go ahead and create a table called users. Users, it will have two
columns. Let's call the first one name with the data type text and the second one will be email. Again, text. Let's end the statement here and let's go ahead and run it. This table is created.
Now let's insert some data into this table. Insert to users name email. The values will be first set is going to be
Alice with the email alice atgmail.com. The second set will be the name Bob with the email Bobgmail.com.
Okay. End this with a semicolon. And let's run this. We have inserted two rows and if we do a refresh here and check in our users table. Let me try to
increase the font for this data table. Let's make this yeah should be fine. Padding also sidebar also. Let's increase it. And a
little bit of padding. This is too much. Anyway, this should do it. Let's make this a little bigger. Great. Now we have a users table with two users named Alice
and Bob with the email alice@gmail.com and bobgmail.com. Now going back to our SQL query. If we run this query, this particular query, let's go ahead and run
this. We are getting this particular row as a result. Since we are saying give us all the users from the users table where email matches this exact string which is
alice@gmail.com. That's why we are getting this row with all the columns as a result. Now this one is our malicious query which if you go back to our board. This was our template and what it does
it takes the user input and inserts it in between a pair of single quotes. And since the user passed this particular statement which is single quote or 1
equals to 1 and hyphen hyphen we take that and insert it into our template. This is what the query becomes. And what happens if we run this? Let's go ahead
and run this. We got all the rows in our users table. Both Alice and Bob, even though the user did not pass any email, but they were able to get a list of all
the users in our database including their email address. Now let's understand what exactly happened here. Starting from our template, our query template. This is what it is in the
first part which is select star from users where email equals to single quote. Okay. And opening single quote. After this point, this is the user's
input. This is what gets dropped in. And if we take a look at this input, the first character is a single quote. And since the first character is a single
quote, what it does? It closes this particular pair. Since we already had a single set of single quote and combined with the single quote coming from the
malicious input. Now this pair is complete. Now what this statement becomes? This statement becomes select star from users where email equals to an empty string. This is the first part of
the statement which is a complete logic. We want to fetch all the emails from our database where email equals to an empty string. Of course, email will not be an empty string in our database, but that's
what leads us to our second part of the statement. Then we have an or statement. And as the name suggests, or simply means either this or this, right? This
first part is the left side of or and this one is the right side of or. So let's take a look at the right side of or. We have one equals to one. not one as a number one as a character since
they are inside single quotes which basically says that is the string one equals to the string one and the answer is obviously yes so this part of the
statement becomes true and then there is another clever part which is two hyphens and we use two hyphens in SQL to comment out whatever comes after that in that
line and since we have a pair of hyphens here whatever comes after this which is a single quote that gets commented out. If they did not provide this pair of
hyphens, then this will throw an error because there's an extra single quote here which obviously creates a syntax error in SQL. Now what happens to our final query? This part was the left part
of the or and this is the right part of the or. Now if you translate it into English what it means what it is trying to say is return me all the users from
our database whose email is empty string which is obviously false or when 1 equals to 1 which is always true since one part of the or statement is true.
This whole statement becomes true. That is the clever part of this injection. And since this whole statement becomes true this part executes. Now essentially what it becomes if we come here let's
say we copy this and we paste it here. Let's say comment it out. If we take this statement this whole statement and make this true and we close it and we
run this then we get all the rows in our database from the users table. And if you want to shorten it even a little more since this is only a true condition
here which is a constant rule, we can take this also and paste it here. Let's comment this out. And we can just go ahead and remove this part. And if you run this select star from users which
returns all the users from our database. So the idea behind writing this malicious input is tricking our string concatenation logic into taking a set of
characters which when combined with our string combined with our SQL template which is selecting a set of users from our database that creates a constant true condition which in turn makes the
whole statement exclude the where part and it essentially becomes select star from users. That's why the attacker is able to get all the users from our
system with this input with just a single set of malicious characters. The characters themselves are not malicious. When combined with our SQL template,
they become malicious with just this set of characters. They were able to fetch all the users from our databases. And since in our case, the users table only
has name and email. But in a production database, users table usually has a lot of fields. Fields like hashed, passwords, addresses, phone numbers, and
lot of sensitive user informations. But that is still not as harmful as the next one. So imagining the same statement, the same SQL template. Let's get rid of
this and let's remove the comment here. Instead of passing this, what if they changed their command and made it more harmful? What they can do? So let's
remove this part. This is our essentially the template. Whatever the user passes, we are embedding it inside this. So starting from here, let's say they again did the same trick which is
passing a single quote. Now this part is complete. So whatever they pass in the next part that becomes their primary attack command and after this let's say
they passed a semicolon then they passed this drop table users and then another semicolon then again hyphen hyphen so
that this part gets commented out and it does not throw a syntax error or there are no other conditions after this statement which is messing with their attack command. Now tracing this part
what it essentially becomes if you can guess which is very straightforward. This part of the query is obviously select star from users where email uh is an empty string. That part is not going
to become true because email is not going to be an empty string because usually we have a not null constraint on our email fields. But this part, this statement when it's executed, this
completely deletes the users table from our database. We'll talk about how to mitigate this. Of course, the first obvious thing is parameterized queries which we'll talk about just after this.
But the second obvious prevention for this is usually in our application code in our backend code when we establish a connection to our database to interact with our database to perform operations
like create update delete and insert etc. The credential that we use the user and password with which we connect to our database that user should only be
given permissions for DML queries which means data manipulation queries. data manipulation queries as in insert queries, update queries, delete queries.
These statements which are called DDL queries which is data definition language which can alter or delete actual tables from our database. Of course, when we run this, this will
return a successful response which is an empty set of rows. But this part will never run which means our users table is not going to get deleted. And the reason
as you can see the query one returns okay but our users table even if you do a refresh it's still there and the data is still present and the reason it did not run was because we pasted this
inside our DB explorer which is table plus and it was not from an actual program table plus whatever driver since it's proprietary we do not know exactly
what database driver they use for postris but most of the modern drivers they have all these safety mechanisms so that they by default block these
backtoback SQL statements. So only the first one runs. This one gets disabled because SQL injection has been a classic vulnerability since decades. A lot of
tools, a lot of drivers and database ecosystem tools. They have a lot of safety mechanisms to disable behaviors like that. But if you're using some
older version of a database driver or a database driver which does not have all these security mechanisms then this statement will actually work and your users table will get deleted. And
judging by this pattern you can already imagine an attacker with the correct set of skills and intentions. They can do a lot more creative attacks lot more
damage to your wagon and your database. uh they can do things like use union statements to extract data from other tables, other sensitive tables like
payment information. They can also use database specific functions to read files from your server's file system. And in some configurations, they can
also execute operating system commands to the database. So there is a lot of attack surface here. That's the reason SQL injection has been one of the most
destructive vulnerability since decades. Now going back to our initial explanation of what exactly is an injection attack mentioned that
injection happens when languages cross other languages boundaries inside a ecosystem ecosystem of browsers, servers, databases and other tools. Now
looking at our SQL query, if we apply that theory here, SQL or SQL, it is a language where certain characters have
special meaning. The single quote here we use to represent a string. Whatever comes inside a single quote, a pair of single quote, that is treated as a string. The semicolon here that is used
to separate two different statements. If a semicolon comes inside an SQL statement, that means that statement is over and the next statement starts executing. The double dash here, this
means whatever comes after that will be treated as a comment and SQL will not be executing whatever command is there after the double dash. Same way keywords
like or, drop, where, all these things they have a specific meaning. And when we concatenate our SQL string or SQL template with whatever coming directly
from user input there what we're saying that we are trusting that the input does not contain any of these special characters which is of course asking for a lot and it is an impossible situation.
Even if the user input is not malicious, we cannot guarantee that a benign user, a user who is clueless, they will not type all these special characters inside
the input box. We are just assuming that it's all data. And that is the essence of all injection attacks. This part that we treated as data and this part which
was supposed to be code but due to the overlapping meanings of special characters this thing also became a part of our code which was supposed to be our
data. So in a way if you want to explain the meaning of injection attacks it all boils down to this confusion between code or data treating data is code or
code as data that's where a lot of injection based attacks are born from. So how do we actually fix this? The answer to that is parameterized queries
and they're also called prepared statements. And this is how they look. If we take this statement and copy it here, instead of doing this string concatenation where we are taking two
single quotes and we are just pushing whatever comes from the user inside this pair of single quotes, instead of doing this, what if we remove this whole thing
and we wrote something like dollar one and while we are executing this whole thing, what we do usually, so let's say we store this whole thing inside a
variable on statement equals to this whole thing and we also add a pair of quotes here whether single quote or double quote that's on you and your
language and while executing what we do we have a DB instance or a pool instance whatever do we have let's call it something like DB and we call an execute
method whatever your uh database driver offers or a query method a debuted query a pass the statement in the first parameter. In the second parameter, we
pass the slots. Whatever slots that are supposed to go inside this query. I'll explain that in a bit. Let's say here we pass the user input. Okay. Now, let's
make this a little bit bigger. Let me change the color of this and also the color of this so that we can differentiate what is the actual query and what is the code. Let me write.
Okay. Now the whole idea of parameterized query is to separate the query from the user data. The user data in the sense whatever data that we are
receiving dynamically during runtime during the execution of our program the data that we do not have the knowledge of while we are writing the code or while our code is compiling. So instead
of building one string with everything mashed together, we send two things to our database. Here if you take a look this is our DB instance pool instance
whatever you want to call it. This part will differ from language to language from FMA to framework. So this part we will ignore the syntax we'll ignore. Let's focus on this part. This practice
is usually common between all kinds of drivers and all kinds of ORMS. So you'll be able to relate this statement. Okay. Now focusing on our actual query
template or our actual query statement. the first part from starting from here to here. Previously where we were concatenating this part with the user input what we did we wrote our first
part as it is as it was before select star from users where email equals to dash. Okay this part contains no user data. This usually contains the tables that we are selecting from whatever
conditions that we are checking and whatever operations that we are performing things like update etc. And in this case this is a select statement. no user data whatsoever. The second part
comes where we have a slot or the actual values or the place the slot or the empty box where the user data will go eventually and we call these parameters
because parameters in the programming language jargon means a slot where eventually data will be pushed in. Eventually data will be filled in. Now that we have our statement ready, we
have a slot reserved here. When we actually execute this statement, we pass these two things to our database. The first part, we pass this whole
statement. The statement that our database is supposed to execute. In the second parameter, we pass the actual value. So this user input, this might be coming from a query parameter from our
HTTP endpoint. Now we take that and we directly inject it here. And this in turn will be injected in this parameter. this query parameter or this empty slot
and if we take this statement let's paste somewhere here with the color this so eventually this statement becomes
this thing whatever data whatever user data whatever data that we pass into a parameter now this in turn becomes this which is let's say the user typed alice
atgmail.com right this is what the query becomes in At the end, the database takes this part, this statement, and it knows that
since it encountered a phrase where there is a dollar sign in front of the phrase, it is treating this part as a slot. And what it did, it took the
second argument that we passed into the query function. It took that and it injected that into this into this slot. Now if you ask what is the difference between this method and the previous
method where we were concatenating string which was more intuitive and which was more easy in fact the difference is when the database construct this query by taking our
statement and all the arguments this part whatever it inserts into this slot this is purely treated as data. There is no confusion between data and code which
is usually the source of all kinds of injection attacks. Now that possibility is gone because if we take the initial malicious statement this whole thing let's say we take this whole thing. So
now let's take our malicious example for our user input. Let's say the attacker came and passed this whole thing which is supposed to be a malicious input and
we took that particular user input and this time we are using parameterized queries instead of doing string concatenation. We took that input and we passed our database the query template
which is this part and the user input which is this part and our database in turn constructs this whole statement. Now what is the difference? Unlike before when the database encounters this
single code it does not try to confuse between code and data it does not treat it as a actual single quote which is going to merge with the existing single
code and it will complete one part of the statement. That will not happen. This will not be a complete statement because whatever comes inside the slot it is treated purely as data or in
simple language it is treated purely as a string. No part of this data will be confused as a command. And that is why this whole thing is now just a garbage
string. Since this is clearly not an email, this will not return any kind of row from our database. And also since we are using parent queries, it will not be
able to harm our database in any way possible. Of course, you can argue that this particular string this should not have reached up to this point where we are executing a statement for an email
with this particular structure. Usually in our validation layer, we should catch these kind of strings which means if a string does not have a structure of an email then the validation layer should
throw an error saying that this does not look like an email. This just a random garbage string. So it will not reach up to this point. But even if for some reason your validation layer is weak or
is not as strict, if it did reach up to this point, then the our database driver will take care of that. Nowadays, every single database driver in every language
supports these parameterized queries. In fact, every OM also uses them by default. The only way to be vulnerable is to deliberately if you intentionally
bypass all this safety by building raw strings by yourself which does not even make sense or is convenient in any way possible. That is only when you'll be subjected to all these different kinds
of attacks and vulnerabilities. So that is your advice for the first kind of injection attacks which is SQL injection. Always use parameterized queries. Even though this has become such a common practice that I did not
have to cover this anyway most people use OMS and OMS by default use parameterized queries and even if you use SQL raw strings the database drivers
also encourage you and make it as easy as possible to use parameterized queries. This has become such a common vulnerability that this did not need covering. But still if you wanted to
understand why this particular type of injection attack has been so destructive for so many decades then this part would be helpful to you to understand what exactly your database driver does by
using these parameterized queries. Now some of you might be thinking I use MongoDB or any other kind of NoSQL or document database and since I don't
write SQL I am safe and I don't have to worry about user inputs which is obviously again not true in MongoDB also queries are represented as JSON like
objects and when you want to find a user by email in a similar situation like this you create an object with an email field and the value that you're
searching for. And the problem is that MongoDB query objects can also contain operators. Things like not equal operator, the greater than operator, the
exist operator, and many other kinds of operators. And these operators are specified as nested objects with special keys starting with the dollar. So a normal query might say find where email
equals to this string value. But if the attacker can control not just the value but the structure also they can pass an object with the not equal operator. So
if your application takes a JSON input from the user and passes it directly to the database query even though that is an obviously stupid thing to do and no one in their same mind will do it but
still if you were to do it an attacker can inject these operators. So that's also something you have to be wary of. Even if you use NoSQL as your database. Of course, if you use MongoDB or NoSQL
database in the first place, then you have bigger concerns than worrying about security. On the same note, there is a different type of attack, a different type of injection attack which is called
command injection attack which works pretty much the same as an SQL injection. But the attack target is instead of your database systems or your
backend system, it is your operating system. In an SQL injection attack, what happens? You have an SQL query template and you take user input without proper
caution and without proper techniques. You just add or concatenate the user input with your SQL query and pass that as code instead of treating it as data.
The same kind of thing happens in command injection where instead of an SQL string you have a operating system command can be a system command or it
can be some CLI program that you call for an example let's say you have a web service have a web app where you ask users to upload images and that image in
turn goes to your web server where you have to process that image process ing can be of any type. Let's say you can let's say the request may be to compress
that image or maybe the request is to resize the image. It can be of any type depending on what you're providing. So your web server takes that image but in
order to resize that image or compress that image etc. What do you do? You use a CLI application or a CLI tool. Something like FFmp is a very popular
tool for any kind of image or video processing. So what you do in your backend code after you get the user's file in your handler you execute a OS
function which in turn calls this CLI tool which is fmmppg and there are some arguments here something like what is the height what is the etc etc whatever
arguments usually fmmppg takes and in the end there is a slot or there is an option to specify the output image file name something like output dot jpg. Now
for this what you're doing you are taking users input for the name of the output file. So the user has the capability to define what is going to be the name of the output file or the input
image. So what you do you just take the user's input and pass it into your whole command. So which in turn becomes the above command which is fine as long as
it is a genuine input. But let's say you are invoking the same command mpg height 120 width 220 something like this and in the end
you're let's say hyphen o and you specify the file name here what you're doing you're passing the user's input as it is and in this case if the user inputs something like this semicolon rm
- rf/ this whole thing becomes the command now so this thing executes But when it encounters a semicolon, this is a complete command. And starting from
this, this is going to be executed. And of course, we all know the destructive effects of RMRF/ which is deleting your whole root folder. The attackers can also get
creative. They might use pipes to redirect the output to a different or more destructive command. They might also use amperands to run commands in
the background which might work as a spyware in your system in your server system because each shell whether you are using ZSH or fish or bash whatever
shell that you use each shell has its own set of special characters and escape sequences and someone who has the skill they can do proper research and come up
with more creative exploits for your system. And the fix for this is similar to the SQL injection fix which is just separate the command from its arguments.
Most of these languages the system languages or languages that enable you to interact with your operating system whether it is NodeJS or Go or Python whatever language that you're using.
They all have these functions that take the command this part as one parameter and the arguments in this case the output file name that in a different
parameter and these functions these functions that are provided by your programming languages or your framework they pass these arguments the arguments that are coming from the user directly
to the process without them going through your shell interpreter when the user's input is passed directly to the shell interpreter that is treated as
code. As we have already mentioned, all these injection attacks happen when our workflow, our system confuses data with code or code with data. So in order to
prevent that situation, when we use proper functions provided by our programming languages, these command injections can be avoided because these functions they inject whatever users
data directly into the process when the program is already running. Instead of interpreting the programmatic meaning of the user's input, they treat it directly as string. So that was a different type
of injection attack which is called command injection. Now let's try to summarize the injection attacks. This is the mental model that applies to pretty much all the injection attacks. Whenever
you are passing user input, whatever coming from your user that they type in their keyboard or paste it, whatever user input that you are getting in your
server, which is another system, the system where the user types is the browser and that travels to your system, which is the operating system of the server or your backend system, whatever
programming language or framework that you're using. When that happens, whether that is SQL or NoSQL or shell commands, HTML, LDAP, XML or anything else,
whatever you can imagine, you have to ensure that the user input is purely as data. It should never be treated as something that can be executed,
something that can be interpreted. And the safest way to do that is to use APIs, use functions, use methodologies that enable you to separate structure
from the data, separate code from the data, which in our case when we talked about SQL injection, they are parameterized queries. And in case of command injection, they are argument
arrays which are provided by your programming languages or your frameworks. Something that you should never do is doing all these kinds of dangerous or sensitive operations using
string concatenations or using template strings by mixing user input with the control syntax. So the thing to be paranoid of that I mentioned in the
first part of this video is whenever you're building a string that will be interpreted by another system and that string also includes any kind of user
input you have to stop and think more about it and always try to find a parameterized alternative to that and most of the time it almost always exists. Next let's talk about
authentication. We have already covered authentication and authorization and a lot of aspects of that in the authentication authorization video. But in this part in this section of this
video we are going to look at authentication and authorization by keeping vulnerabilities in our mind by thinking about where the weaknesses might be where people usually mess up.
So to give a brief authentication is basically the process of verifying the identity of your user to finding out which row exactly in your database in
the users table associates with this user who is trying to currently access your system. That is basically the question that we are trying to answer. Every time we are talking about
authentication, if you get authentication wrong, the malicious user or the attacker, they can impersonate your users. They can access their private data. They can take actions on
their behalf and they can also steal their money. If you are dealing with any kind of payment related operations and these kinds of vulnerabilities, the account takeover kind of vulnerabilities
we see every day in real world. So there are a couple of obvious thumb rules that we can follow where typical vulnerabilities arise so that we can
avoid as back end engineers these kinds of issues in our systems. But before we dive into the technical details, I have an advice. uh since we are talking about
authentication and authorization if you can avoid implementing authentication yourself and here I'm not talking about avoiding implementing your own
cryptography algorithms etc. I mean in the general sense if you can avoid implementing the whole authentication flow at least in your production systems then you should definitely go for it. If
you have the budget, if you have the approval of your company for the budget of taking an authentication and authorization provider, then that is
always a good move because the obvious benefits even if you think beyond security. The first thing it saves you a lot of time. Of course, when your
service finally scales to thousands of users or millions of users, you are going to see your authentication provider sending you invoice bills of
thousands of dollars. But by the time you reach millions of users, you most probably have a revenue source. So you can anyway handle the ollated bills. But
as I was saying, the first benefit of taking an O provider is the amount of time saved and in the amount of money that you can save on your developer
salary. Because to get the whole authentication and authorization flow, right? And when I say the whole authentication and authorization flow, I don't mean a very simple JWT verify and
storing the token in your cookie and verifying it. That simple flow is obviously something that you can implement. There are LLMs that can definitely implement them for you and
there is not a lot of difficulties or weaknesses when it comes to security in that flow. But when we are talking about production systems, it is much more
complicated than that and the scope is not as clear as that. Most of the time when we are talking about the whole authentication and authorization flow in production systems we generally prefer
stateful authentications by stateful authentication I mean we can keep track of all the devices where the user has currently logged in and if the user wants they can immediately revoke the
sessions of all these devices at once or if we want we as the provider of the service as the provider of the system he can revoke all the sessions of the user
from all the devices and log them out. We have the capability for that. So that kind of capability, that kind of setup is called a stateful authentication. If you want to learn more about stateful
authentications, stateless authentication, you can revisit the authentication and authorization video. But if we want to provide a stateful authentication workflow to our users,
the things start getting more complicated because the setup is not as straightforward as signing a JWT, sending the JWT and the front end storing it in cookie and your server
verifying it with each request. When we talk about stateful authentication, there is databases involved. There are caches involved like radius. Then you have to think about what is the best
place to store users permission. Then you have to think about timeouts. Then as I talked about you also have to think about all these revoking mechanisms. Revoking from your cache, revoking from
your database, right? That is just one part of the equation which is stateful authentication. Then comes the social authentication like sign in with Google that we see in pretty much all the
professional SAS services or your service consers. Then sign in with GitHub. Mostly all these services, SAS services, a production SAS service. It generally provides two three social
providers something like Google or sign in with GitHub, sign in with Twitter whatever. So for that you have to implement oath flows yourself oathflow
not just in your back end also in your front end with the redirect mechanism for a very good and very smooth user experience. Once that is out of
question, now you have to also implement the system so that your email and password based authentication which when we are talking about stateful authentication which is directly
concerned with this this is directly connected to your social provider authentication which is your oathflow which means let's say I sign in to your
service with my email which is 1mail.com some sample email and my password something like password. It's ironic
since we are talking about security in this video but anyway for the sake of this example let's say your password is password. Now you as a user sign up to my service using this credential and you
frequently sign into the service using these credentials. So one day what you do you sign in instead of your credentials which is email and password you try to sign in with Google with the
sign in with Google button. When that happens, your oath system should be capable enough that it can connect your email credential flow with your oath
flow which is sign in with Google flow so that it does not treat this user even though they have the same email as a different user just because they used a different method of sign in. Right? That is another problem. And I can go on and
on about a lot of not problems but inconveniences. Inconveniences which your developers are going to spend weeks on or you are going to spend weeks on if
you are a solo developer or you are building your own project. Of course you can go ahead and cut corners in a lot of these things that is anyway going to compromise with your security or b with
your user experience. You have to make a trade-off on either of these. every time you cut corners. So with all these things, the argument always comes down to this. Use a O provider. They take
care of all these things. The modern providers like clerk, they also provide arbback permissions, a lot of other stuff like advanced security stuff they take care of. Of course, these services
they might also have user experience issues. They might also have vulnerabilities in future. But they are a security company. Their whole service is surrounded around authentication and
security. So they have a dedicated security team which thinks about security 24/7. They handle edge cases that you will not think of and they will
respond to new kinds of attacks that come in the market very quickly since they are spending 24/7 on finding out and fixing different different bugs or
vulnerabilities and all these things that I talked about the different inconveniences that you have to face as a developer they have already fixed this and with just a few buttons and with an
API key you can provide worldclass user experience to your users for authentication. and also worldcl class security. So in my opinion, it's always worth it to make that tradeoff of paying
a few bucks for an o provider instead of going ahead and implementing your own o flow and dealing with all these things. Of course, let's say when you have thousands or millions of users and your
authentication provider bill is crossing, let's say $10,000 or $20,000 bill, then you can definitely consider migrating to your own authentication
setup. By then, you'll have the revenue, you'll have your developer bandwidth and priority state. So, then you can definitely go ahead and implement your own o workflow, which definitely makes
sense. But when you are starting out, it is always recommended to go with an O provider. But even if you do listen to this advice and you get an O provider,
you still need to understand how authentication works. You need to know about configure, what can go wrong and how to integrate it securely. That is the reason we'll talk about
authentication, different different issues in authentication right now. Starting with the first kind under authentication is the password storage. So since we are talking about authentication, let's start with
passwords. Since most of you will deal with passwords even if you are using an oservice. So during the time of sign up, a user comes and they enter an email and
a password. They hit enter and that goes to your server. Now the question is what do you do with it on the back end? Of course when we talking about server we mean the back end and you as a back end
engineer. What exactly do you do with it? How do you store it so that you can verify it later when they log? And for some of the advanced users, this might sound very silly and a very beginner
developer targeted, but we have to talk about this so that we can understand the reasoning behind the advanced password storage practices that we have today
which are very common in the developer ecosystem. But as a beginner, you might not be aware of those. So the knive approach of storing password is of course having a database column with
text type and you just store whatever the user has sent you in the plain text format right so let's say the user has something like 1 2 3 4 5 you take that
this is your server and you store it in your database right so that the next time user wants to sign in he passes the email and 1 2 3 4 5 you which gets to
your server the server fetch matches the user's password using the email from the database which is 1 2 3 4 5 m matches and the server lets the user login right as simple as it can get. But the problem
with that is databases get breached every single day. If you follow the tech news or cyber security news, every other day you'll see a headline saying that
this company's this database got breached and a lot of users credentials uh got leaked. And it happens to companies of all sizes, not just big companies and not just small companies.
Every kind of company has the same kind of probability to go through a breach. And breaches happen for a lot of reasons. It it can be because of some insider employee trying to take revenge
on you or it may be because you are using a third party service and they got breached and in turn your database got breached. There can be a lot of reasons but the truth is they do happen and when
your database gets breached and you are storing your password in plain text format all those passwords are leaked. At that point you can argue that okay the moment you know you have a breach
you go ahead and uh reset all the credentials of all the users log them out and ask them to create a new password. But the problem is the moment a user's password is breached their
presence in any kind of internet website is also the reason being more than 70% of the users they use their same passwords in multiple sites right they
use the same kind of email and password combination in pretty much all the sites. A very less number of users use password managers which randomizes the password for each site. But still a lot
of internet traffic, a lot of internet users where you use the password. So the moment your database gets breached and you expose a certain user's email and password combination, the attacker can
pick that up and they can run that on a lot of sites. can be payment related sites, it can be e-commerce sites, it can be social media sites, a lot of sites and there is a huge probability
they will be able to match one of those sites and they will be able to take over that user's account. Usually users are not aware when database breaches happen
unless they follow tech news very closely. So that is a huge security risk storing passwords in plain text in your databases. Even if there is no breach
happening when you're storing passwords in plain text format that means that your developers the developers who are working on your system the developers who are building the back end they can
see those passwords and your database administrators they can see those passwords anyone with the database access in your system they can see any user's password in your system so you're
basically trusting every employee every contractor freelancer everyone who might access your systems with all your users credentials and their online presence. So by now we know that storing passwords
plain text in our database is a wrong way to do it. So what is the solution? That's why people have come up with hashing. Now of course hashing was not
necessarily invented for storing passwords in databases but hashing is a very good use case for password storage. Uh if you are from a computer science background, you will already be aware of
this. Hash is typically a function which is called the hashing function which is one way that is the most important tool of any kind of hashing function. And the
goal of this hashing function is it can take any kind of string 1 2 3 A B C at the rate any kind of strings as an input
of any length also. The strings can be of any length starting from length of one to length of hundreds of characters. They can take any kind of input of any kind of length but in the output the
condition is they always have to return a string or any kind of output of fixed length. Depending on what kind of hash that you use the length will differ but
the condition is it always has to be fixed length. If the hashing function says that it will return the output of 27 characters, then the goal is it does
not matter what is the length of our input. The output will always be of 27 characters. And the second condition is for the same input does not matter how
many times we call the hashing function. It always has to return the same output of the same length. Right? It cannot be a random function which gives us different output for the same input
every time we call it. The output will always have to be same and the output will always have to be of fixed number of characters which summarizes what is a hashing function. And the last condition
was it is always one way which means we can get the same output for the same input of the fixed number of characters. It does not matter how many times we run
the hashing function but we should not be able to get the input from the output right it should be mathematically impossible to get the input from the
output using the computing capability that we have at the moment given all these features of a hashing function this is a very good candidate for us to
store our password in a database so what we do let's say the password is 1 2 3 4 5 instead of storing this particular a string in our database. We take the hash
of it. Let's write now let's say h password hasher the input is 1 2 3 4 5 there is a cost factor which we'll ignore and we say generate hash gives us
a set of characters let's say paste it here. Okay. So instead of storing 1 2 3 5 we pass it to a hashing function. In this case we pass it through bcrypt
which is a very popular hashing library. and we store this hash in our database. So the next time the user logs into our database, what happens? We fetch the
hash of the password for that user which we have stored in the users table or the accounts table or wherever. We fetch the stored password, the hashed password of the user. Then what we do, we take the
user's input for the sign-in attempt. We take that password and we hash it and we try to match whether the current hashed password matches our store hashed. If it
does then the user has typed the right password and we let them we let the sign in successfully. Now in this case think about the situation of our database
getting breached. What happens? The attacker gets access to all the password hashes of all our users. the hashes of the passwords, not the actual password. And since one of the properties of the
hashing function is you cannot under no circumstance with the computing power of our current systems get the input from
the output of a hash. The user's identity is safe in the internet. The password is never exposed. The attacker under no circumstance can get the access to the actual password. Now that we have
discovered hashing, the use of hashing to store our password securely in our database, we should be secure safe, right? Unfortunately, that is not the
case. Since hashing has become the industry standard for storing passwords in databases, of course, attackers also know this fact. And what they have done
is they have built a huge storage of preomputed hashes. Let's say there is a big table which can be used in scripts
like Python or any other exploiting scripts. And this table is basically a one to1 map. For example, for the string something like 1 2 3 4 5 which is a
common password that people use in a lot of sites. For that they will have a hash something like dollar like mapped to this. Same way for the string password
they'll have some other hash. If you do a simple Google search, you can find an image like this. So where you can see there are common passwords on the left side and hashes of those on the right.
So train table is basically a table with common passwords and their hashes. And of course not every hash returns the same string the same output string for a
particular input. But since usually there are industry standards of which hash is currently the most popular. So attackers can use that particular hash
to build this particular rainbow table of common passwords and their hashes. Now using this what they can do is let's say your database gets breached but this
time instead of storing your passwords in plain text you have stored them using hashes and you are all confident that you are super safe the attackers can never get access to your users
credentials but again what happens once the database is breached and attackers get access to all those hashed passwords one of the techniques that they can
apply is taking a rainbow table like this which has the mapping of all the common passwords and their hashes. they can run a lookup and when they do that there is a huge probability that some
percentage of your users are using commonly used passwords like password or 1 2 3 4 5 or password at 1 2 3 all these common passwords and those attackers
they can get a couple of matches where the hash is exactly the same as one of the hashes in the rainbow table and since a hash function will always return
the same output put string with the same number of characters for a particular input. They can exactly reverse map what is the password of the user for that particular hash and using that they can
find out the passwords of at least some percentage of your users and that is why you're still not safe even if you just use hashing to store your passwords in
your database. So what we can do for this situation is something called salting. Now what is salting? So far we have discussed that for each input of a
commonly used password something like 1 2 3 4 5 will always produce the same hash like z 7 something of some number of characters as long as you are using
the same hashing function and since they always produce the same hash for the same input they are prone to rainbow table attacks in order to prevent that
sameness what we have come up with is salting. Now what does salting mean? It is a very simple trick/ technique to avoid this sameness problem of same
input generating the same output which is universally. Now what we do a user signs up say with the name a at the ratemail.com
and the password 1 2 3 4 5. In the first method what we used to do we used to store in plain text. Second, we started using a hashing algorithm. By the way,
for a long time, BCrypt has been the default hashing algorithm for pretty much all the web apps on the internet. But recently, Argon 2 ID has been the
industry standard for the most secure and most performant hashing algorithm. That is something that you should know of and you can do more reading on that on your own. One of the good resources
for reading about authentication is is Lucia O. Lucia has been one of the good authentication libraries with good primitives and a very well-ritten
documentation but Lucia is no longer a authentication library. Instead it is more of a guidance towards implementing
your own authentication using the best industry practices etc. So it is a very good resource to understand the mechanics of secure authentication and industry standards is something that you
can explore on your own to implement secure authentication. Now coming back that is the second method where we implemented hashing. Now in the third method what we are saying is just
hashing is not enough. We also have to integrate salulting here. Now what does salulting mean? Let's say the user then signs up with these credentials which is a zmail.com and 1 2 3 4 5 as password.
Previously, previously what we used to do, we used to take the password 1 2 3 4 5 and run it through a hashing function in case of argon 2 ID and that or brypt
and that returned us some kind of hash of some number of characters. Now in order to prevent the sameness problem what we'll do we will add a new element to this which is a random number. So
instead of just passing the password string, we'll also add a salt and salt in this case is just a string which is randomly generated for that particular
user. Let's say this is the user 14 in our platform. So for user 14, we have a database row in our database where we store the user's name, we store the
user's email, we store the hashed password. Now we will also store the user's salt. We'll generate a salt. Now this salt has to be generated from a cryptographically sudo random generated
function. You'll easily find all these different kinds of functions from very popular libraries. OpenSSL is one of those. And we have to generate this salt which will be something like SP3
some string and we'll also store that in our database. And while hashing what we do we take the password we concatenate with the salt is a randomly generated
string that is unique to this particular user in our platform and then we hash the whole thing and after that whatever we get we store that in our hashed password column. Now in this new
scenario what happens? Let's say our database gets breached. The attacker gets access to all the hashed passwords and the salt and users email everything.
But in order to get access to the user's actual password, not the hashed password. Previously they used rainbow tables which had a mapping of users common passwords and the hashed version
of it. Now when they run the rainbow table against this leaked database, they will find zero results because each of these hashed passwords have been hashed
with a randomly generated salt. So this hashed string will never match any other string in the world. It does not matter even though the common password is 1 2 3
4 5 and the user's password is 1 2 3 4 5 but the attacker will never get to know this because this 1 2 3 4 5 which is the common password and the hashed version
of that which is in the rainbow table of the attacker and this version which is the 1 2 3 4 5 is the actual password of the user in our platform but the hashed
version of that has been hashed using a salt which we have generated randomly. So there is no way these two are ever going to match and that is how we prevent rainbow table attacks by using
salts and not just a hashing function for storing our password. Now with the introduction of hashing with the introduction of salting we should be completely safe from any kind of
password based attacks. Unfortunately that is still not true. Now comes another threat which is the brute force attacks. These days especially with the
advent of all these LLMs and all GPUs have become very popular and there are companies there are manufacturers there are providers also cloud providers which
provide GPU instances for some fixed amount of price. Now modern graphic cards or GPUs they are very good at
computing hashes. A single GPU can compute billions of SSHA 256 hashes per second. Billions. So even with salting
an attacker with your database can do what's called an offline brute forcing for each user. They can take the salt and they can try common passwords one by one. They can hash that common password
combined with the salt and they can check the hashed password with the breach hash password. And with their capability of billions of attempts per
second, they can even try every password up to eight characters, every common password pattern, every word with common substitutions within a matter of days.
And in order [clears throat] to prevent this problem, we do not use common hash functions like SHA 256 or MD5. These are also hash functions commonly used for
file-based hashing to match the integrity of files when we are transferring files over the internet. But we do not use them for password hashing exactly for this reason because
our graphics card computing is so fast at these hashes that they can do billions of hashing functions attempts per second. This is the exact reason we
use slow hashing functions which I have already mentioned. It's called decrypt or script or the current industry standard which is argu ID. These are
called slow hashing functions and they have been specifically made for password hashing. The other hashing functions the MD5 SHA 256 these are general purpose
hashing functions which have a lot of use cases but these ones the slow hashing functions which are deliberately made slower because of the use case of password hashing they have what we call
as a cost factor cost factor or also called as work factor which controls how slow we want them to be depending on our systems bandwidth depending on our
systems computing capability we can decide how slow we want these algorithms to be. So we can decide while we are hashing our passwords for a new user, we
can decide whether we want this particular hashing to take 100 millconds or we want it to take 5 millconds that is on us depending on how much latency
how much computing power we can afford in our system. Now if you think about it for a genuine user who is trying to log into our platform with their email which
was a at the ratemail.com and 1 2 3 4 5 and we are using argon do id or bcrypt for hashing this and we have decided
that we want this particular hashing function to take let's say 400 millconds right so for a genuine user logging in through browser the login taking
somewhere around 400 to 600 milliseconds is not a big deal. In fact, this is almost imperceptible to the user. But for the same scenario for an attacker
for a malicious user who is trying to brute force his way into our system by taking a breached database and trying out billions of guesses offline. For
them, this changes the whole equation because previously when they could try a billion attempts per second, now it comes down to just four or five attempts
per second. Which means instead of cracking being able to crack users passwords in the matter of days, now it will take them decades or even centuries. The next topic to discuss
under authentication is sessions. Your user has logged in and they have provided their email address. They have sent it to your server. You have checked
with hashing and whatever authentication mechanism that you have and you have made sure that your user has proven their identity. Now what is the next step? We cannot possibly make our users
enter their password every time they want to access our platform or every time they want to make a new request. that would be a very bad user experience. So for that we need to
remember that they have already authenticated with our system with our platform and this is exactly where we talk about what we call as sessions.
When a user successfully signs in or successfully logs in the server does three things. First, it generates a random session identifier. I 1 2 3 A Z
whatever. This is a very long random string. Ideally somewhere between 128 to 256 characters and again it must be
generated by a cryptographically secure pseudo random number generator function which are provided by popular libraries. Why do we need to make this random? Because if an attacker can guess your
session ID, if they can guess this particular session identifier, they can potentially hijack any session of any of your users. And by making sure that our
session identifier, this whole session ID is somewhere between 128 characters to 256. Even if it is just 128 characters or 128 bits, we are making
sure that there are more possible session ids than the number of atoms in our universe. Which means that guessing this number is almost practically impossible. The second thing that our
server does is storing this session identifier in our database. Now this could be radius which provides a very fast setting and accessing time which
can provide a better user experience or it can be your primary database also something like posgress or MySQL. With this session identifier we also store
some kind of metadata in our database again in radius or our own database. things like who does this session belongs to the users information when it
was created so that we can do operations like timing it out revoking etc when it will expire or default expiry time 7 days or whatever the IP address of the
user so that in the user's dashboard we can show from which location this sign-in attempt was made or which are all the locations where the user has
signed in and also the user agent the user agent which is basically a request header in our API calls. This tells our server from what kind of device or what
kind of browser we are trying to access the platform. So using the user agent our back end our platform can atom can know that the user is trying to access
from a Chrome browser or a Firefox browser from an Android phone from an iPhone whatever the device type or the browser type is. And the third thing, the last thing, the server sends this
session identifier, this cryptographically safe session identifier to the browser and asks the browser to store it inside a cookie. Now since this is stored inside a cookie,
all the subsequent request that is made to our platform or our back end, this particular session identifier is always sent back to us to our platform. And for
each of those requests, the server extracts the session ID, the 128 character random number or random string from the cookie. It looks up in the
persistent store whether it is radius or posgress depending on your implementation. Finds the associated user who has initiated this session who is the user of this session and finally
it knows that this is the particular user who is currently making the request. Now even if you are using JWDs which have a slightly different format a
slightly different workflow because they are stateless and we don't maintain the session in our persistent store but the philosophy of identifying an
authenticated user works pretty much the same way as a stateful workflow which we just discussed. Now when you are using cookies there are some flags there are
some configuration of a cookie that we should be aware of and that is always recommended to be used in a production system. The first one being HTTP
only. This means that when we set HTTP only flag to true for this cookie which has our session identifier. This means that if your site has an XSS
vulnerability, we'll talk about XSS in a bit, but it basically means that if an attacker is able to run JavaScript in your user's browser through your
platform through some vulnerability in your site, then your site or your platform is said to have an XSS vulnerability. If [clears throat] you do have that, then some potential
JavaScript can read your local storage. So storing your session identifier or storing your JWT tokens in your local storage is always discouraged. That is
why setting HTTP only to a cookie. Make sure that this malicious JavaScript snippet it does not have access to this particular cookie. Of course, JavaScript
can also read cookies only if HTTP only is not set to true. And that is why it is important if you're storing any kind of sensitive information related to
authentication. This is usually a session identifier in case of stateful authentication or AJ token in case of stateless authentication. It is important that you make this flag only
to true so that JavaScript cannot access your session ID or your JWT token. The second flag is secure. Then this flag is set to true. Your browser makes sure
that this cookie is only transferred back to your [clears throat] platform, back to your back end only if the connection is HTTPS. It will not try to send the cookie over
HTTP or HTTP protocol. if TLS or SSL is not enabled for that connection. Now it is important because HTTP traffic can be
intercepted on public networks on public Wi-Fi or cafes by malicious ISPs or if the Wi-Fi router itself has some kind of spyware configured which can read all
the traffic flowing through that endpoint flowing through that hop. So if your sensitive cookie which has the session identifier or the JWT token, it
gets sent over an HTTP connection can be stolen by anyone who has access to that endpoint who has set up that spyware or someone who is observing the traffic
with a tool like Wireshark. So the secure flag make sure that your cookie is only transferred over HTTPS connections which are by default
encrypted. Even if someone is watching your traffic, they cannot make sense of it because everything is encrypted. Third important flag is same site. This flag controls whether this particular
key can be sent during cross origin requests. Now we'll talk about what is cross origin. But this can have three kind of values. One is strict, second is
lax and third is none. Okay. What does strict mean? It is exactly what it sounds like. The cookie where we have stored our session identifier or our JWT
token. It will only get sent to the domain or the platform or the site where the request is originating from which is essentially our own back end. So for
example, if someone clicks on a link which is a link to your site but it is hosted on some other site. In that case the cookie will not be sent because this
is essentially not your originating request. It will only get sent if it is a request is starting from your own front end from your own site. with the same site flag set to lax. What happens?
The cookie will be sent for top level requests which means a direct link for your site but it will not get triggered for other types of elements like images
or iframes. We'll cover why we are talking about images or iframes when we are talking about links and duplicates in a attack class which is called CSR
which is called cross-sight request forgery which involves making use of images and iframes to trick our server into believing that the request is a
genuine request originating from our platform or our site or our front end. So with the same site configured to lags, we are only allowing top level links and not any kind of triggers
started from images or iframes. When it is set to none, it is the most relaxed. It is sent for all kinds of request. But it requires that the secure flag has to
be at least set to for some kind of security. So in our case for storing a session ID cookie you either want strict that is the best case scenario or you
never want to make it as none. This prevents this whole class of attack which is called CSRF or site request forgery which we'll cover in a later part of this video. Now with that
talking about cookies we have covered talking about stateful authentication and the kind of vulnerabilities and the security mechanisms that you have to
keep in mind when you are dealing with stateful authentications. [clears throat] Now coming to stateless authentication where we are primarily dealing with JWT. There is an alternative to stateful authentication
or stateful sessions or serverside sessions which are called JWTs. Now the difference is with sessions what happens your server your database it stores the
session data the users information the IP address the user agent whatever information that particular session has it stores it in the database and it
gives the client just the ID of the session. This is just one column of that session row and the client stores that ID and in every subsequent request our
server our database is able to take out that particular session row by using this ID is the session ID. The difference in JWT is the whole session
data. This as you can see the decoded payload that is shown here in the official JWT site. This is called JWT.io. Here the actual session data is
sent to the user or sent to the front end sent to the browser. So that for every subsequent request the user the client is going to tell us who the user
is. One natural question that comes to mind in this case is if the user is going to tell us if the front end or browser is going to tell us who the user
is, isn't it prone to all kinds of attacks like the access is within the user's hand? Now if they want they can imperson it as any other user in the
platform and tell us that I am not user A and I am user B. So I should have access to user V's account and a lot of different kinds of attacks like that.
Fortunately that does not happen because of how JWTs itself are constructed and all the different pieces that come together to make a JWT based
authentication workflow possible. Now if we take a look at this particular JWT example which is shown here, it has three parts. The header which is the first part which is shown in green. The
payload the middle part or the second part which is shown in white and the last part which is also called the signature is shown in purple here. Now the header it describes the token. It
describes what kind of algorithm was used for this particular JWT. The payload contains what we call as claims in the JWT jargon. It is basically some
pieces of information that we manually store inside the JWT for all the back and forth transferring that happens between the client and the server. Now if you take a look inside the claims
table, we have a couple of common fields. One is the sub field as per the JWT standard. This is the subject of the JWT and in other words the actual user
which can be the ID of the user as per the platform as per our platform. It can be UU ID or any kind of ID that you generate for your platform for your
database. It also includes another field called IAT which is the actual timestamp when the JWT was created. And using this
particular field, our server logic, our server code can decide whether it is timed this JWT is expired or not depending on when it was issued and for
how long we are letting JWTs to work. Apart from these ones, the default ones, you can also add your own claims. Claims are just a fancy way of saying key value
pairs, pieces of information that you can store inside AJW. So apart from the default ones, you can also store your own custom claims. In this case, the
name of the user is stored and also the information that whether this particular user is an admin or not that is also stored as a custom claim. Now the last part which is in the purple here inside
the JWT token. The last part we call as JWT signature which involves three parts. One is the header. Second, the payload and taking these two and signing
them with a secret token. Again, [clears throat] a randomly generated cryptographically secure string and we sign these two header and payload together. And whatever we get that is
the signature of this the last part. This is how it typically works in case of JWT based workflow which is the stateless workflow. user comes and provides their email and their password,
sends it to our server. Our server verifies it and if the password is successful and everything. Now in this flow also we use the same kind of password storage and password
interaction mechanism which is hashing the password salting the password and everything that does not change. What changes is the actual session storing and session not storing part. So here
after the password matching and everything is successful the server creates a JWT which looks something like this with the user's ID and other relevant information like if if they
want to store the user's name the admin and the issued at field is automatically created by whatever library that we are using for interacting with JWTs then the server signs the payload using a string
a random string which is stored inside the environment variable of the server using that string it signs the payload and whatever JWT is generated after the
signing this JWT is sent to the client for storing and for sending this back to the server for every subsequent request. Now client take this stores it ideally
in memory or in local storage in some cases or in cookies and for every other subsequent request the client sends this back in an authorization header. Now
server gets the request. It tries to extract this particular JWT from the authorization header. It verifies the signature. Usually libraries provide a
function called verify which takes secret key which is stored again inside our environment variable and it tells us whether this JWT is successfully
verified or not. If the signature is valid then the verify function will return true and the server will know that this payload has not been modified or has not been subjected to any kind of
tampering. So the whatever information inside this is completely true. Now looking at this whole flow, one big advantage is the server does not need to
store anything inside the database as compared to the previous stateful workflow where we had to store the session information either in radius or our database. In this case, everything
happens on the fly during runtime by checking just the JWT claims. And because of this region using JWTs scaling is a lot easier because there is
no overhead of interacting with our database or caching layer using radius. But even with JWTs there are of course some of the less favorable properties of JWTs that we have to talk about since we
are talking about security. The first one which is the very obvious and very important one is revocation is hard. Now, if you think back, if you look back
at our sessions example, with sessions, if you wanted to log a user out, what you had to do was simply go into your database and delete that particular
session from the database immediate and the next time the user refreshes or sends any kind of request, the front-end platform should ideally log the user out. But if we talk about JWTs, this
functionality, this revoking functionality, at least the immediate revoking functionality does not work as smoothly. You cannot just delete a token
from the client. Since JWTs are created and sent to the client for storing it and sending it back to us for every subsequent request, we cannot just ask
the client to delete the token from the browser. And in turn as a result of this one primary security vulnerability that arises here is if the user's account is
compromised if somehow some other malicious user got access to the user's account and the user sends us as a platform a support query that their
account has been compromised and they want us to revoke all their logged in sessions [snorts] from all devices. Now we are in a difficult spot since we have
implemented stateless authentication. We cannot help the user in any way because we don't have the capability to revoke any kind of sessions from any devices. We had implemented stateful
authentication then we could help the user by logging out of all the devices in a moment's notice. Of course over the time after realizing the limitations of
JWTs the community has come up with some workarounds. One of the popular ones is blacklisted tokens which means that for that particular user we take that
particular user the next time the request comes and we add it as a blacklist to our uh database or to our caching layer with radius and we call that a blacklisted token which means we
will not serve successful responses for any requests that has this particular token attached to it. The second one, the second workaround is using short
expiration expiration time with refresh tokens. Now this is a very popular workflow. What happens with this is instead of issuing just one token to the
user after a successful login what we do after a successful login we issue two kinds of token. One is access token and the second one is a refresh token. Now
the general practice is keeping the expiry of access token to something very short 5 minutes or 10 minutes and the expiry of refresh token to something
longer let's say 1 day or 7 days depending on how paranoid you are how secure you want your systems to be and for every subsequent request the user will attach the access token but once
the 5 minute window is gone once the access token is expired hired. Technically what happens the server will obviously throw 401 unauthorized
response code back to the client and in the client when it sees that our server has responded with 401. What it will do? It will use the refresh token and the
refresh token is supposed to be saved by the client upon successful login completion which it will receive from the server. The client will save this refresh token somewhere safe in local
storage. And when the access token is expired, the client will send the refresh token back to the server saying that uh the access token is expired. I need a new access token. The server will
check the validity of this refresh token and it will check whether the refresh token is expired or not. If it is not, then it will again issue two sets of token again a new access token and a new
refresh token and the cycle continues. After that what happens with this flow is even if the account gets compromised in some way and the attacker is able to
get a hold of the access token then the session will only last for 5 minutes after that they cannot refresh the token since they do not have access to the refresh token. Even if they do have
access to the refresh token again that will also expire in one day. So these are not perfect workarounds but these are the workarounds that the community and the engineering standards have come
up with to mitigate the revocation challenge of JWTs. And another confusion that usually happens is this particular string which is the payload part of the
JWT. This is just a B 64 encoded string. So if we take this and let's paste 64 we paste here and we say decode then we can
see the full JSON here ID of the user the name whether the user is admin or not when was this issued etc. Of course we cannot really go ahead and make any
change here. Let's try to do that. Let's say instead of John do we remove the E part. So now it's just John do and we take this and we let's say we want to
encode this. We paste this new JSON and we encode it in the new B 64 and let's paste let's copy this new B 64 string. Go back to RGWT and let's try to change
it here. Now the moment you try to change the payload in the signature part which is in turn the verification part it says that the signature verification has failed which means that the server
will immediately know someone has tampered with the payload and this JWT should not be trusted and the server will not allow the request to go through. But the fact that this string
is just a base 64 encoded string means means that we should not store any kind of sensitive information inside this only store those kinds of information
which when exposed to external users or malicious users. There is nothing much they can do with that information. It is only helpful to our server. And the last
problem and the most discussed problem with are the storage. Where should you keep the JWT token when you send it back
to the client? Where is the ideal spot for the client to keep the JWT token? Now, local storage is still the most used solution even though local storage
is vulnerable to a lot of different kinds of attacks since it is accessible by any JavaScript snippet if your site is vulnerable to XSS based attacks. So,
local storage is obviously not a good choice since XSS can steal it. The second option is cookies and for cookies again in order to prevent JavaScript
from accessing that cookie you have to make it a HTTP only cookie which we just discussed. Now with all this you are anyway back to our cookie based solution the HTTP only cookie based session
solution. So my general advice when it comes to authentication, when you're choosing between stateless and stateful authentication is unless you have
specific scaling requirements like horizontal scaling, multiple servers being able to authenticate a particular user request, unless you are in a situation like that, always prefer
stateful authentication, always prefer sessions. And even if you have scaling requirements, even if you have a requirement where multiple servers need to authenticate a particular user
request, you can make use of distributed key value pair based databases like radius to store the user sessions. That is not a problem. So session based
authentication, stateful authentication is always preferred and always advised as per my opinion over stateless authentication. The tradeoffs of stateless authentication are just not
worth it. Stateful authentication or session based authentications are even simpler. The revocation strategies very straightforward and the whole workflow
and the architecture is compatible with most kind of SAS projects. But if you do use JWTs, always use short expiration
times. something from minutes to hours, not days. And implement refresh token flows with serverside storage and always use HTTP only cookies rather than local
storage. With all these different workarounds, you can make the pain points of stateless authentication go away, but they still feel like hacks as
compared to a stateful authentication. One more important piece of topic when we are talking about authentication is rate limiting. A very important mechanism for securing your
authentication. Without rate limiting, if you have set up your server, what happens? An attacker, a malicious user, they can try thousands of requests per
second, per minute, millions of billions of requests for trying different different kinds of password, different different combinations of username and passwords on your server on your
authentication endpoint. And since you are not limiting the rate at which a particular user can make API calls or send you requests, all of these go
through. Now from this two things can happen. One, they are [snorts] able to find a match and get access to a user's account which is very bad or second they
send such a volume of requests to your server that your server cannot handle the load of all these requests per second and your server crashes. This is also bad. So rate limiting is obviously
a very important and a mandatory security mechanism for your authentication workflow and also your other endpoints some kind of global
endpoint and the general practice is you should be more restrictive when it comes to your rate limiting configurations for your authentication workflows for your
authentication endpoints and you should be a little more generous when it comes to your other endpoints. So the configurations for rate limiting uh differs whether it is your
authentication endpoint or your general service endpoints. Since we are talking about authentication, it is also recommended to have multiple layers of rate limiting not just a very simple
straightforward rate limiting strategy for your authentication endpoints. uh first layer usually is per IP rate limiting which means that you limit how
many login attempts can come from a single IP in a given time frame maybe something like 10 attempts per minute. This will help you stop automated
attacks. But there are some problems with per IP based rate limiting because in a lot of organizations, universities, the outgoing IP address might match of
different different users who are trying from different different devices because all of them go through the same hop. So that will not give you the correct rate limiting metric all the time. And even
if you don't consider that with the modern network infrastructure at current times, attackers have techniques to use multiple IP addresses using things like
botn nets or proxy networks, VPNs or rotating IPs. There are a lot of exploiting techniques. So IP based limiting does not really work to
mitigate advanced attacks. So another strategy that you can implement is per account locking or per account limiting which means you limit how many failed
attempts can happen for a single account. So maybe like five failures per 15 minutes should log the account for 24 hours unless they contact the support.
So this will help you stop attacks that rotate IP addresses or target a particular account. But for this if the attackers use another clever trick which
is trying one password let's say something like 1 2 3 4 5 and they try this one password across different accounts. So one attempt at account A
one attempt at account B and so on. In that case your per account limiting or per account locking will not work. And since they're anyway using multiple IP
address, multiple IP addresses, your first layer of rate limiting which is per IP rate limiting that will also not work and this attack will go through. So for this the last resort that you can
implement is global rate limiting which means that how many login attempts your system can take in a given time frame or how many failed login attempts your
system can take in a given time frame. So even if the attacker uses multiple IP addresses and tries a single password across a lot of accounts and for your global rate limiting you have configured
that in your whole system in 1 minute you will only allow let's say 100 attempts per minute your whole systemwide in that case the moment the attacker process the number 100 for
their attacking attempt for your password brute forcing your system will raise an alert and you will be able to flag that incident and you can actively
try block all those IP addresses. You can try showing capture to all your users to filter out all the genuine users from the malicious users etc etc.
Using this layered approach of rate limiting you can prevent two things. the attackers getting access to the account of a genuine user through brute forcing
and your servers crashing because the thousands of requests per second the attacker is sending to your server. So rate limiting is obviously a very
important strategy whenever we are talking about security. We talked about authentication. Now it's turn for authorization. Again, if you want to go deep into authorization and the
mechanics of it, please watch the authentication and authorization video separately. This is solely focused on the issues, the security issues related to authorization. And we already
discussed the security issues related to authentication. To briefly explain, authorization basically means while authentication means the identity of the user to identify which row in our
database, which row in the users table in our database is associated with the user who is trying to access our system currently. The same way when we say authorization it basically means after
we have identified the user after we know exactly which user we are talking about. The next step is what are the permissions of the user? What are the
activities that the user is allowed to perform in our system in our web app and in turn in our back end that's basically the difference between authentication and authorization. Now the confusion and
when I say confusion I don't mean the obvious confusion which is confusing the definition between authentication and authorization. I'm pretty sure any engineer can easily understand what is
the difference between these two. But the confusion that happens in a technical implementation between authentication and authorization that's where most of these vulnerabilities
arise that is related to authorization. For example, I'll take a very realistic scenario that I often see in code bases. Most probably we have already discussed all these things. But we have a routing
layer. This is the first entry point of our backend system. From routing layer, we go to handlers which deals with things like request body parsing and
validation and passing all the validated data to our service layer. And our service layer interacts with our repository layer. Okay. So usually what happens we place our authentication
middleware which is like require o something like this. We place this at the routing layer after finding out which route the user is trying to access
because for login routes and for some of the public routes we do not need the user to be authenticated. So depending on what routes the user is trying to access, we place this middleware which
checks if it is stateful authentication, the presence of the cookie with the session ID and the validity of that. And in case of stateless authentication, it checks for the JWT token in the
authorization header and the validity of that. If there is something wrong in there, it throws either 401 403 back to the user asking the user to properly authenticate. Now this happens at the
routing layer. After this point, what happens? The confusion arises here. The confusion is once the user is authenticated, we have this false sense
of security that now that we know the user is our own platform's user and we already have the data about the user, we have already authenticated the user. Now
the user is allowed to access any parts of any of the systems, right? The handler, any kind of handler, any kind of service, any kind of repository. Of course, we place some very obvious
authorization related checks also at the routing layer, which is things like if the user is a member or the user is an admin or the user is a super admin etc
etc. Or if we go a little bit more granular, if the user has access to the read permission on the entity books, if the user has access to write permission
the entity books, things like that. If we are going more granular on the authorization layer. Okay, again that is still at the routing layer but the confusion the security vulnerabilities
that arise at the service and deposit layer because because let's say at our routing layer we have a require o middleware which properly authenticated
the user. We checked that the user has a proper session ID in the cookie and the user is authenticated. In the next layer, we also check the authorization of the user which is the user has the
permission of read books is a granular permission which states that this particular user has the permission to have read access on the entity books in
our database. Okay. Now finally we let the user pass to our handler layer service layer repository. Now the API the particular endpoint that the user is trying to access is this one / books
with a search parameter which says id equals to something like five. Now what is happening here in our web app in our front end we have an interface here
where we are showing a list of books to the user and there is a search bar here and there are some filters here and in the filters there is one particular filter where they can pass multiple book
ids here and we will only show the details of those books here. We will only show the books in the table which matches one of these ids. Since the user
selected or passed the ID five here our front end made an API call and this is the API for that which is /books in a search parameter we are saying ID equals
to 5. And this again at the first entry level it hit our routing layer. We check the authentication check the authorization with the granular permission. And finally in the service
method we call the repository layer and this is the final query that we ran which is select star from books where id equals to this is the final query we run
and whatever the result that we got we returned back to our front end and the user was able to see here. Okay that was the whole experience. Now the problem here is yes this part the problem was
this particular book id the book number five this is not a resource and not an entity which was created by the user for which the user has access to which means
the book five is not of this user but since they pass this particular ID in the filter and we call the API and since in the API layer we are not doing an authorization check. So what we should
have done is after this query we should have wrote and user id equals to what is context dot user ID. Whatever user ID that we were able to fetch at our
authentication reader we store it in our request context and we have to match that in our database query to make sure that we only return the books for which
the user has access to. Now this is the kind of confusion that I'm talking about. Just because the user was able to pass the authentication check, just because the user was able to pass
authorization check of having read access to books does not mean they have access to all books in our system. So in our database layer for every single query we have to deliberately think what
are the entities exactly that the user has access to. on a high level uh because of this kind of confusion a lot of authorization related vulnerabilities arise. So let's talk about a a few
famous ones for which we have already named those vulnerabilities. The first kind of vulnerability is broken object level authorization or in short BA. Now
this vulnerability which is called broken object level authorization. This is exactly the scenario that I mentioned above. Just a fancy way of describing it. Which means that our system does not
check the authorization of a user on entities on database level. It just checks the authentication and authorization during our routing layer but does not check it at our repository
level. So that kind of scenario can cause this vulnerability which is called broken object level authorization. Now this scenario I explained sounds a little harmless but someone with more
technical knowledge what they can do they can write a script they can iterate through all possible ids of books in our system but instead of books it can also be invoices or payment related
information in our database and they can download every single invoice from our system they can get a hold of all users financial data and they can also modify
they can also tamper with the invoice URLs uh account numbers etc. A lot of damage can be done if we are not careful about broken object level authorization.
So how do we actually fix this? How do we fix BA? As I have already mentioned, the fix is pretty simple, pretty intuitive, which means that even though
we check authentication at the routing layer, even though we check authorization at the routing layer for entity level, which is read invoices, for example, in our repository layer, in our service layer, we also have to make
another check, which means that don't just fetch invoice number five, don't just fetch book ID with ID 5. fetch books or fetch invoices with ID5 and
where the created user ID or the user ID also matches the user ID that we were able to extract the authentication layer which means we stored the user ID in the context and we have to match that in our
database query. So what we are saying instead of just fetching the book with ID 5 we are fetching the book with ID 5 which also belongs to this user or which
also can be accessible to this user. We are mindful about authoration till the last point of access which is the repository layer. We don't end up with our false sense of security from the
routing layer. We have to take it all the way towards our repository. That's the way to prevent this broken authorization issue. Now the second important thing which is a very subtle
thing here is and I have seen this in a lot of real world systems is backend engineers they try to provide a very good user experience because they are only thinking about the happy path about
their own front end they don't entertain the scenario of an attacker trying all these combinations so some engineers what they do when they get an API
request let's say with invoice ID 5 an API which is supposed to fetch the invoice invoice details with the id five. So what they do they try to fetch
the invoice details with some kind of dbquery db.query something something and they find this particular invoice entity. Then at the next step what they do they check if the context dot user is
equals to invoice dot user ID they have this condition. If this condition does not match they throw an 403 error which means forbidden. Now technically this is
right. We are supposed to throw 43 errors, forbidden errors whenever we have an issue related to authorization, whenever we have an issue related to a user not having access to a particular
entity. But the problem with this is if we return 403, if we return this particular response which is forbidden, we are confirming the fact that this
particular invoice, the invoice number five, which the attacker, if they have malicious intention, they most likely have guessed. They don't know for sure that this particular ID exists or not.
they have guessed this ID and they're trying to verify if they can get access to this invoice or at least they can make sure this particular invoice exists in our system so that they have other
ways to get an access to it. The moment we throw a forbidden error, we are saying that this particular invoice exists in our system but we will not give you access to that. Even though you
did prevent an attack there but you also did some amount of information leakage which can also be destructive over the long run because if you are following
this pattern then an attacker can come up with a list of ids 5 17 18 whatever a list of ids and they can enumerate they can call your API in a loop and they can
find out these are all the invoices that exist in the system so that they can go ahead and plan another layer of attack something like social engineering attack
which involves interacting with humans fooling humans and tricking humans into extracting sensitive details from our system and that is the reason another important point that we have to keep in
mind is when it comes to authorization related checks we have to be a little more reserved about it. So ideally in this scenario when we get a API call for
an invoice with ID 7 ideally what we should check instead of checking the existence of this invoice in one database query and then checking if the
user has access or not then throwing a 403 forbidden error. Instead of doing all this simply what we can do is we just write a very simple SQL query which just say select star from invoices where
id equals to 7 and user id equals to whatever context dot user ID and when we run this since this particular invoice this does not belong to this user what
we'll get no rows and when we get no rows error by default what we do we return a 404 which is not found not found response code back to our client.
And since we return not found as a response for this particular query, the attacker, they cannot tell the difference between an invoice that does not exist in our system versus an
invoice that exists in our system but that belongs to someone else. If they can make sure if they can verify point number two, they can resort to other layers of attack. Other layers of attack
which is mostly social engineering. Now that they cannot differentiate between these two, they cannot tell if the invoice exists in our database in the first place which in turn helps
preventing the other layers of attack since we stopped them at the root level without giving them any kind of information. Now this whole thing this authorization check database query level
this needs to happen not just for select queries not just for view queries where we are returning information. [clears throat] This needs to happen for any kind of database query which is either edit
which means update or delete or insert any kind of database query that we run. We need to check this authorization of the user at the database level without
leaking any information. That is also an important. Now another similar vulnerability another similar vulnerability here is broken function level authorization. Now this is pretty
much similar to the above vulnerability which is BA and object level authorization but this has a different angle. So this is more about restricting
the access to functions as compared to restricting the access to data which is about this DFL is about functions and what kind of functions do we mean are
sensitive functions especially the functions which are only supposed to be accessed by our admins. Let's say we have an endpoint which is something like
/ admin/invoices again. Now this particular endpoint is only supposed to be accessed by the site admin so that they can review different
different invoices. They can provide support to the clients whenever there is some kind of issue related to an invoice. Now for this particular endpoint we cannot do this which is this
pattern select star from invoices. We cannot put a condition here which is where id equals to five user id equals to context dot. We cannot do that here
because the admin needs to see all the invoices of our system. There should be no wear condition. Even if there is a wear condition it should be only for filtering like active invoices or
pending invoices etc etc. There should be no authorization related checks here. For that reason this is not a object authorization related vulnerability. This is a function authorization
vulnerability which means that let's say we have an admin panel here and in this admin panel we are listing all the invoices by using this type of API which fetches all the invoices of our system
and shows it to our admin. Now one clever trick that we did was we did not share this URL which is safe do web.app app whatever is your URL is you did not
share this particular URL with anyone only the admin knows this URL but apart from that you don't have any other kind of security mechanism here if the admin
knows this URL and as long as the admin is a user of our platform they can go into the admin panel they can login as usual the login flow is same for a regular member and the admin the only
difference is the URL the access to the URL once any user is able to login into the admin panel they can call this particular API and they will be able to see all the invoices of our system. Now
again this sounds a little stupid but these kinds of vulnerabilities still happen in legacy systems where people try to provide security through
obscurity. This is a very famous phrase in the world of web security which means that instead of providing actual security instead of providing robust security what you do you just hide
information you just do clever tricks and you just do a lot of assumptions and you just feel this false sense of security that your system cannot be accessed by malicious users or
attackers. This is exactly the same case. Just by assuming that no other user has access or knowledge about this URL, our API is safe or our admin panel is safe. That is not the case. This
whole setup can backfire. As long as someone monitors your internet traffic and the moment they see this particular URL and they find out that there are no admin level checks in your system, they
can call this API independently and they can get a hold of all of your invoices in your system. The mitigation to this is something that we have already discussed. The routing layer we perform
authentication and after authentication we also need to perform authorization and in the authorization we not just check the granular permissions in this case which is read invoices which is in
our case true. A user who is trying to access their invoices, they also have read invoices permission. And a user who is trying to access all information,
which is the admin level function, they will also have this same permission is read invoices. The only difference is the user role. So this user is a normal user which is something like member and
the admin will have a role something like admin. So another middleware should be present here. Not just our permission middleware, also the role middleware
which makes sure that the user that is requesting access to this function which is requesting access to this particular API has the role of admin. They are authorized to access this particular API
as admin. Then only we let them call this function and then only we return all the invoices of our system. That is how we prevent broken function level authorization. Another interesting
vulnerability here is indirect object references and broken access control patterns which talks about using sequential ids of your database which is
something like 101 102 103 and using these in your APIs something like /invoices slash 1 02 to fetch the invoice
information of this particular invoice. Using this pattern enables [clears throat] attackers to guess all the other invoices in our system, the existence of all the other 101 and 103
109 and so on. Since our ids are sequential, since they are predictable, they are prone to all kinds of attacks, all kinds of enumeration based attacks. As long as the attacker can predict the
existence of all these invoices in a system, they can plan more number of attacks targeting these. One prevention to this is using uu id as our primary
key which is looks something like this here. These are randomly generated and often used as database primary keys. They're also URL friendly etc etc. But
using UID also comes with a a couple of performance related issue that you have to read about. But generally UIDs are recommended for this. Okay. Now that we
have discussed all this stuff about authorization, try to cover two summarizing points which will cover all these vulnerabilities in a very simple manner. After discussing all these
vulnerabilities related to authorization, we can categorize the authorization related attacks into two types. One is horizontal authorization attacks. The other one is vertical
attacks. Now this is a very simple mental model a very simple mental framework that you can use instead of memorizing all these different vulnerabilities related to authorization. You can just think it in
a very intuitive way. First [clears throat] is horizontal. Now horizontal authorization attacks involve user A getting access to the resources of user B due to some incapability some
kind of linkage in our system. is usually happens because we ignore authorization at the database level and we call it horizontal authorization attack because the scope widens here. We
talking about different different users. The scope widens here at a system level. That is one kind of attack and under this we have a broken object level authorization that we have discussed and
this is also called ID or something like indirect object reference. Both of them mean the same thing. Now what is vertical authorization attack? This is
the other case that we discussed which is BFLA which means a user after entering into our system instead of widening their scope horizontally what they do at our system level they widen
their scope vertically which means getting access to more of our systems resource instead of other users which was the case when we talked about admin level scenario. So after entering into
our system, a user is able to get access to the functions sensitive functions which are only accessible to an to an admin user. So these are the two categories of authorization related
attacks that we just discussed. Now to discuss the framework, how do we actually mitigate how do we actually prevent all these authorization related attacks? First thing is you need to
centralize your authorization logic. You should not scatter your authorization related checks throughout your codebase where they'll be applied inconsistently
or or they are prone to getting forgotten. So you should have a clear authorization layer from where every single request passes through right all your logic related to authorization live
in the same place so that it is easy to understand by all engineers in your team and it is easy to maintain and it is easy to scale. Second thing is a very simple framework which is called default
deny which basically means that once you have your authorization layer the framework that you should follow is if your authorization logic does not explicitly allow something does not
explicitly allow certain permission certain user role or whatever then you should by default deny it. Instead of checking the other way around, you should deny any other kind of conditions
which is not explicitly allowed in your logic. This way what happens when you add new resources in your database or new endpoints in your routing layer, they are protected by default. Even
though you forget to grant access to them in your authorization layer, but until you do that, they are protected by default. Now that is a very important safety mechanism. Third thing, test
authorization specifically which means that don't just test your happy parts. A normal user, a benign user, a genuine user is able to go through the whole
workflow correctly and you move on from there. That is not how you should be building systems. You should also test for the edge cases which means that user A should not be able to access the
resources of user B. Same way a regular member role user should not be able to access the functions of a user with the
role of admin or an unauthenticated user should not be able to access any kind of authenticated resources in your system and should not test these things
manually. You should have automated tests in your system. a dedicated test just for authentication authorization which checks for all these use cases because if you test all these scenarios
manually most likely there will be some change in your system someday which will break one of these functionalities and since you are manually testing all these things. It cannot be as comprehensive as
automated test and you'll miss one of these scenarios and that will leave a huge security hole in your system. If you have an automated test suite, every time a new change is added to your
system in your CI/CD pipeline, all these authentication and authorization related test suites are run. And you can be rest assured that your system is still secure
as the day you build your authentication authorization system, no matter how many additional changes are added to your system over the time. And the last thing is audit logs. Every time a sensitive
resource something like an admin endpoint is accessed you should log that in your audit table that this particular endpoint was accessed at this time stamp
by this particular user etc etc. Same way every time an authorization check fails when a user tries to access the resources of user B you should flag that
incident and you should log that as a breach event dedicated because if there is someone who's trying to probe your system if there is someone who is trying to break your system you should have the
knowledge of that so that you can take advanced preventive measures for those. Okay. Now that pretty much covers all the things that you should know when it comes to authentication and
authorization. Moving on to our next major vulnerability which is XSS. It stands for crossside scripting. Now we are going to see a small demo of this
towards the end of this video. So before that we'll just briefly explain what this vulnerability means and how does it work as how to prevent it and we'll actually see it live in the demo. to
explain crossite scripting. It means that when a malicious user or an attacker [clears throat] they manage to get their JavaScript any
JavaScript code or any JavaScript snippet to execute in a user's browser in a genuine users browser in the context of your platform in the context
of your front end then this whole attack is called cross-sight scripting. It's still not clear how does it work and why it is so dangerous. So let's cover that part first. Why is it actually
dangerous? So we all know that everything that happens in our browser, all the processing that happens to JavaScript. Any kind of computation that runs and JavaScript in the context of a
browser can do a lot of things. It can read everything that is happening on a page as long as it has the context of that page including the sensitive data. It can also make API requests to a
remote server by impersonating as the logged in user because they have access to the cookie. They have access to the local storage. As long as it is not HTTP
only, they have access to the cookie. They can also redirect the user to other pages, pages like fishing pages to fish the credentials of the user. And lastly,
they can also alter or they can also change the content of the page to trick a genuine user into thinking the website that they are looking at is a completely
different website. To summarize, if an attacker manages to run their code, their JavaScript code in another user's browser exactly as it sounds like, it
can be very destructive. Let's look at how this is possible in the first place. The first kind of excss attack that we can talk about is stored excss and this is how it works. Let's say you have a
blogging platform and in that platform you have a discussion board a comment system discussion board where users can come and they can post their comments and other users can reply to that
comment etc etc. So if you are aware of how markdown rendering works in SPS or in client side JavaScript based
applications usually if we want to support markdown in some kind of input then what happens the user writes their comment in markdown let's say they use a
hashtag to start an heading so that this is a comment then they use hyphens to uh write bullet points etc etc And when we
preview this or when we try to render this as markdown, what we do? We use these libraries, if we are talking about the React ecosystem, the most popular
client side libraries for interactive web apps, then what happens? This gets converted into HTML using different different libraries like Remark, Rehype,
and they have a plug-in based architecture. So you can customize how this looks and how to style it etc etc. But this is the whole idea. The user writes their markdown in the input and
to show that markdown. You convert it to HTML and you provide some kind of styling and you render that HTML by injecting the HTML directly into your
DOM node. So we just take that and inject whatever in a div or something like that and whatever the user has typed bullet points they will be converted to something like lists. So an
unordered list starting the tag here li first point li second point etc. Then closing the unorder list tag here. So the markdown gets converted to this
particular HTML and we actually have to go ahead and inject this into the DOM so that it can be rendered and we can see what the user intended us to see. Even
though if you are using these popular libraries like Remar, Hype and React, all the safety mechanisms are already taken care of. But assuming that you are in a scenario where you are doing all of
this yourself or maybe we are talking about a legacy system where the user types some kind of markdown or some kind of HTML and you render it by injecting that into the web page and to store it
you send the whole converted HTML to your server and the server in our database and the next time the server fetches the server sends it back and the client injects that and shows us in the
comment list. Now a malicious user what they can do if they want somehow by tampering with the behavior of this markdown to HTML conversion and finding
a hole somewhere they are able to inject a script tag somewhere inside the HTML and since we are taking the whole thing and injecting into the DOM what happens
this script tag it starts getting executed as genuine JavaScript code in the context of our platform since that's where this script is injected and that
is the reason if [clears throat] you take a look at React and search for dangerously set inner HTML come here in the docs as you can see in order to
render this particular HTML what you have to do in react is you have to pass the HTML string inside a attribute which is called dangerously set inner HTML. The reactive has intentionally set the
name in a way that you are always warned that when you are setting HTML like this, you are on your own and you have to be careful about XSS attacks since
there is a possibility that you are injecting scripts into your web app. So this particular vulnerability happens not because of this practice for injecting HTML into the DOM. that has to
be done. If you want to render userdefined markup in our web app, then we have to do some kind of injection. But to prevent that what we should have
done, what the server should do is when the server receives this particular markdown, this particular markup structure that the user has typed in the form of a comment, it has to do a step
of sanitization, which means it has to look for signs detecting if this can be a potential exploit something like the presence of script tag etc etc. So the
server can detect if a script tag is present and it can strip that off and only save the HTML. If the server does not do that then your web app your platform is prone to an XSS attack
especially a stored XSS attack because every user who ends up viewing that particular comment the script tag will start running in their browser. Now the
moment the script starts running in their browser, the attacker who has written the script, they can send all their session cookies, all their local storage information to a server which is
controlled by the attacker and they can view all those sessions and they can if they want hijack those sessions also or they can also retype the user to a fishing page and steal their
credentials. So there is a lot of damage that can be done here. So you have to be very careful about whenever you are injecting HTML structure into your web app and you have to make sure that when
you're storing that user provided markup you are doing proper sendation. There are also different types of excss called reflected XSS which deals with rendering
the script tag in your URL. There is also DOM based XSS which is pretty much the same as the scenario that we just discussed setting the inner HTML
property of your web app. But to summarize all these different kinds of XSS attacks the cross site scripting attacks all of them happen for a single
reason. The root cause for all of these vulnerabilities is userdefined content being treated as code rather
than data. Again, this is similar to SQL injection attack where the vulnerabilities happened because of the confusion between code and data. And
accesss is pretty much the same but instead of happening at the server layer instead of happening at the SQL or database layer it happens in the user's browser. When a user provided data when
put into a particular languages context in this case our HTML and JavaScript combined gets treated as code instead of what it was meant for which is data.
That is exactly when XSS attacks arise. The easy prevention for this is obviously sanitization in your validation layer. Make sure whatever user provided content that you are
dealing with that you're storing you are properly sanitizing it before storing it in your database. The point appears here again that every time you are dealing with user provided content. It does not
matter whether it is a user provided search query. It does not matter whether it is user provided HTML or an image which can be of a very big size. Every
time you are getting something from the user either you're storing it in case of this or you are processing it using your database or you are doing some kind of file handling every time you're dealing
with users data you have to be very mindful very careful about how you are handling it. What kind of access you are giving what kind of resources you are spending on this particular operation.
If you do that then you can get out of 99% of vulnerabilities very easily. Another prevention here and the technical prevention is CSP is also called as content security policy. Now
content security policy is provided by your browsers. This is an HTTP header which is sent by your server back to your browser. And the purpose of this
particular SP header is your server tells your browser exactly what kind of resources it is supposed to execute and what kind of resources it is supposed to
block. Okay, very clear instructions about the what kind of resources it can actually start executing, what kind of resources it should trust. So you can specify using your server things like
only run scripts from these specific sources from these specific domains which are coming from our domains or you can specify that don't run inline
scripts at all. No inline script which means our earlier attack which was caused by injecting a script into our DOM. this will completely get blocked
since you saying that don't run any kind of inline scripts in the context of our web app. So using CSP is also a very robust fix for these kinds of situations
for dealing with XSS attacks. You can also specify from what domains you are supposed to load images from. If the images are from a different domain then
you'll also get an error from CSP. so that even if a malicious user is able to inject some kind of script tag into your browser, the browser will block it
immediately. It will not let it run in that context at all. Now, one thing to focus here is the CSP, the content security policy that is provided. This is not actually a prevention. This is
just one way to fight if you have any kind of XSS vulnerability. The core reason for which your site is vulnerable to accesses that you have to fix
yourself first by properly sanitizing your user provided inputs and everything. But CSP is configured just in case you forgot just in case there
was a very remote edge case which let an excss attack to pass through. In that case CSP can your last line of defense but it is definitely not a prevention.
So you should be doing all kinds of prevention and in the end you should trust CSP to block any kind of attacks that that passes through all your defenses. There is another category of
attack which is called CSRF also known as cross-sight request forgery and I just want to mention this very briefly because in my opinion CSRF
is not a major threat to modern web applications. We cannot say that this is non-existence because we still have legacy systems. We still have outdated practices in a lot of systems. But as
long as you are working with latest technologies and using decent libraries and frameworks, most likely you are not going to face CSRF attacks. But just to
cover it in theory, CSRF works because of the cross site nature of cookies. Basically when you visit a particular
website what happens? Your browser stores cookies for that site. This particular website and when you make a request to that site your browser
automatically includes those cookies in the next request. And this whole thing happens even if the request originates from a different site. For example, if we take a realistic destructive
scenario, you are logged into your bank's site and your browser, it has a cookie for let's say bank.com and now
when you visit another site which is a malicious site created by an attacker and the domain is evil.com either through a fishing link or any kind of
genuine web browsing and when you land in this particular website there will be a form which will get autosubmitted or there might be some kind of image tag
with an URL that triggers some kind of javascript which submits the form for you. Now this evil.com it will trigger a code snippet where evil.com is making a
request to bang.com and since it is your browser which is making the request to bang.com even though the context is evil.com from where you are originating
this request from but it is still your browser and that is the reason your browser will include the cookie or bank.com in this particular request and since the cookie was included the server
of bank.com it will see the request as a genuine request and it will let it get through and by using this whole workflow the malicious user the one who created evil.com they can perform some kind of
destructive actions but again as I have already mentioned CSRF is not one of the major threats that you should be worrying about as long as you are using modern technologies and modern libraries
another reason I say this is not a major threat is because the same site cookie config that we discussed in the authentication section where It is recommended that you always set this
either as strict or if your front end or back end are in the different domains, you can set it as lax. By default, our modern browsers set the same site config as lax. And unless your same site config
is none, you're by default protected against the CSRF attacks. And the next point of defense is our coarse config. Now course is definitely not a security
mechanism but using course what happens typically our backend they have course configs and it checks if the request that we are receiving it is coming from
a request that is own front end. If it is not then we do not set the course header and the browser of the client blocks that particular request. Now that is another not a very robust prevention
but it is still a line of defense against CSRF attacks. So that's one of the reasons worrying about CSRF attacks does not make a lot of sense at least with our current technologies, libraries
and frameworks. So we are just going to mention it briefly and move on from here to the next section. So far we have mentioned and discussed different kinds of vulnerabilities, how they work and
how to prevent them etc etc. Lastly, we will discuss a few practices which can turn destructive which can create security holes in your system and how to
prevent those and how to think about those. The first thing is misconfiguration. Misconfiguration in your system can also open a lot of security holes. For example, let's talk
about secrets management. The most abused concept in back end secrets management. And when we say secrets, we mean things like API keys or database
passwords or encryption keys or JWT or whatever technology that we are using or authentication secrets or secret. So one
obvious security vulnerability is which I don't think anyone does these days is storing any kind of secret in our source code, right? which gets committed into
git and that in turn lands in GitHub or Bitbucket or GitLab whatever version control system that you're using. And the moment that happens, everyone who has access to your GitHub repository,
your Bitbucket repository or your GitLab repository, they can now see all your secrets. They can access your database. They can access all your external services using the API keys. They can also mess with users data using the
authentication secrets and the encryption keys. So making sure all your secrets always live in environment variables either using things like or using some kind of external secret
management like AWS parameter store or vault is from hashior etc. There are a lot of services these days. So always make sure any kind of secret does not go
into your version control system. Even if a secret gets leaked into your VCS or your GitHub and you go ahead and delete that, that is still in your commit
history. Someone who goes back in the commit history, they can still access that particular secret. So the moment you accidentally commit your secret into your VCS into GitHub, you should go
ahead and rotate that secret or delete that secret and create a new one. Okay, one of those things that you have to keep in mind when you're talking about security. The second kind of misisconfiguration that we should talk
about is debug mode in production in our live environment. Typically when we are developing in our local writing code in our local our logging mode our logging
mode is set at debug level. What that means is we log all kinds of data in our in our standard output things like very detailed error messages with stack
traces. Now stack traces itself contain a lot of information about how our functions are named, how our files are named, how our code is structured etc.
Same way we also print a lot of information about our databases. Things like the explicit SQL query, the database P configs, the database transaction related information, all
these things that get printed in our local standard output or in our local terminal. Ideally when we go to production the log level is set to info
which means we only log the information related stuff for our production server which means all these things the error stacks the database query logs and the
whatever debug logs that we are printing for example users sensitive information etc etc all these things they never show up in production logs they are only meant for our local as long as our log
level is set to debug That's only when they'll show up otherwise they'll never get printed. But if we don't configure the log level specifically at info level and anything which is not debug level in
our production in our live environment all these informations are all these information are going to show up in our logs and if and when log production logs get breached the attacker can get a lot
of information about how our code is structured what are the sensitive data about the user what is the sensitive data about database etc. So it is important that you are mindful about
your log levels. What is the log level of your production environment? What is the log level of your local environment? And what is the log level of your staging environment? That is another misconfiguration that can cause a
security hole in your system. The third one is security headers. Security headers. Now when it comes to security headers, we have already talked about CSPs which tell your clients what kind
of JavaScript it can execute in your platform's context. There are some other HTTP headers which help us prevent a lot of security related issues. Things like
frame options which when properly configured it prevents other sites, other websites to embed our website inside an iframe and which in turn helps
our users from being the victim of fishing attacks. Because if we do not properly configure Xframe options, an attacker can configure our website and they can show our website inside an
iframe. But using a technique called clickjacking or any kind of other JavaScript code that is running behind the scenes, they can capture the user's credentials and they can do a lot of
stuff depending on the users interaction on that site. Since the site itself is in control of the attacker, but in an separate iframe they are hosting our website. There are a lot of different
kinds of attacks that can be planned there. Similarly, there are other security headers and these days pretty much all the web frameworks does not matter whether you are using NodeJS,
Express or Golang, Echo, Jin, Python, Django, whatever web server framework that you're using. All of them provide radius security middleares which when
configured is just a oneline change. All these industry standard security headers are configured for you. you don't have to manually configure each of them as long as you use these modern middleares.
So that's pretty much all that I wanted to discuss about security in the context of back end. Now finally putting all of this into perspective, what I want to
say is every vulnerability that we talked about and there are others there are hundreds and thousands other vulnerabilities. They are all about the same fundamental thing which is
boundaries. So data crossing boundaries between systems or between privilege levels, between programming languages, between different markup structures, any
kind of boundary. Whenever data crosses any of these boundaries or any of these trust zones, you have to be very careful. We have to be very mindful about what exactly is happening in your
system because every time we have crossed a boundary, that's when we have encountered a vulnerability. For example, things like SQL injection where untrusted input coming from the user
that crossed into the boundary of database query language. Same way when we were talking about XSS cross-ite scripting that crossed the boundary of users markdown into the boundary of
HTML. Same way for broken authorization the requests cross the boundary of what different users should access. So the tip here is every time you're writing
code ask yourself where is data crossing a boundary. What assumptions am I making about the data? And what if those assumptions are wrong? If you ask
yourself these three questions repeatedly every time you encounter data crossing boundaries then I can promise you that you can avoid 99% of the
vulnerabilities that modern backend applications suffer from. And another thing is no single kind of defense is perfect. So you always have to think in
layers. For example, your first layer of defense and this I have stressed in so many videos. It should be input validation. Always validate every single
thing that is coming into your system which is the first entry point for your back end which happens after the route is matched. At that point you have to strictly validate every single thing
with as many conditions as possible. The final data that leaves your validation layer and reaches your handler layer should be in exactly the structure that you expect it to be. There should be no
surprises from that point on. That is your first line of defense. The second is parameterized operations. Always use APIs that provide explicit parameters.
Whether we are talking about databases or we are talking about OS level functions like file operations etc etc. Always use predefined framework defined
or your programming language defined functions instead of just constructing the commands by string concatenation. Then authorization checks verify every access at the point of access. Since for
the broken object level authorization vulnerability what we did we verified the authorization of the user at the routing layer but the actual access was
at the repository layer and that's why the vulnerabilities happened there. So always verify the access at the point of access. You have to be mindful about where exactly the authorization point of
access is and you have to check the access there instead of a couple of layers upstream. Then security headers and policies you have to configure your browsers and your servers so that they
can limit the damage in case something fails. For example, things like course CSP and security headers all these things they are not very robust defense
mechanisms. But in case some kind of attack passes through your first line of defenses, then these secondary mechanisms can help you tone down the
scope of the attack. Then monitoring and logging. Always monitor any kind of suspicious activity and log them properly. Send them to your login service, raise alerts, etc. That should
be very obvious. And the whole point of this is maybe there can be some kind of weakness in one of these layers. But if you have multiple layers of the security
then an attacker has to bypass all these layers at once before they can cause any harm to your system which has very less amount of chances. So the last thing
that I want to mention is security is not about learning a couple of techniques or or a feature that you just learn and go and implement. Security is
more about being paranoid about your systems and the mindset that you bring before writing every single line of code. So always think about boundaries,
always think about assumptions. And one last thing that I want to mention here is if you want to learn more about security explore in depth, there are two
resources that you can take a look at. First thing is port sugar academy. Now this is the company that has created the tool which is called BSU which is a very
famous ethical hacking tool, penetration testing tool and and this port sugar academy has a very comprehensive catalog about a lot of vulnerabilities how they
work with practical labs and the theory behind it the computer science behind it and everything. You can also recognize a lot of vulnerabilities that we talked about things like SQL injection
cross-ite scripting CSRF and clickjing SSRF and OS command injection that we talked about also authentication related vulnerabilities oath related
vulnerabilities JT attacks and a lot more that is beyond a video like this that you can explore here and the best part is all of this is completely free.
There are no charges, no hidden charges here. I have personally learned a lot about web security and security in general from this particular resource. So I hope you can spend some time and
learn more way more than what I was able to cover in this particular video. Then we have OASP a security focused foundation and OASP top 10
vulnerabilities. It is a very famous list of current setup vulnerabilities that modern systems suffer from. They keep publishing this list every few
years. So the current list is something like this. On the top we have broken access control which covers things like BA, BFA, all these things that we talked about. There is security
misconfiguration that we discussed and an injection which covers OS command level injection, SQL injections and a lot of vulnerabilities and how severe they are the real world instances of
those all of them are listed here. Now there is a resource called OASP cheat sheet which is again a very good resource that you can take a look at to
learn about a specific concept. For example, if we go to let's say authentication here, then you'll find a lot of information about what are the
good practices that you should follow during authentication, what are the bad practices, what are the security threats that you should be aware of and they cover a wide variety of domains when it
comes to security. Let's say if you go to session management then here you'll find a lot of important information a lot of useful information about what are the best practices what are the industry
standards that you should follow while working with sessions how to manage sessions the importance of session ID why it has to be a cryptographically secure pseudo random number generator
what are the conflicts of that and a lot more information you can find a lot of information for a lot of security related I have personally gone through all of these multiple times over the
years and this has been excellent in building the security mindset by covering different domains by keeping a single goal in mind which is security but coming at security from different
different domains that opens up a completely new perspective in your mind. So just wanted to share these two resources with you because of course this video by no means is comprehensive.
Security as I have already mentioned is a very huge concept very huge domain. The goal of this video was just to make you aware of some of the things that you should think about while building your
back end. And I have shared these resources so that you can independently do a lot more reading on security and understand these concepts a lot more deeply better than I was able to
explain. Okay, that's pretty much all I had to say about security.
Chat & Create Study Materials
Chat with this video