So how can we now protect against
cross-site scripting attacks?
Well, to protect against those attacks
we got two main building blocks
that you should be aware of.
The first very important building block
is that you should try to output escaped user content.
So if you're outputting user content
somewhere on your page,
you should not take the raw HTML code
and parse it as such
or hand it to the browser to parse it as such,
instead you should escape it.
And escaping the content simply means that
certain features of HTML will be disabled
or the entire HTML block
will actually be treated as raw text instead of HTML.
So you might have all the HTML tags in there,
but instead of handing them to the browser
for the browser to parse them,
you might output them as plain text
and not let the browser parse them.
And then the script tag which I entered before
would be output as plain text on the screen
instead of being executed by the browser.
I will show you how you can escape user input
in just a second,
but that's the first building block.
The second building block,
which would be an alternative is that you sanitize,
that you clean the user input
before you process or store it.
Now, if you do escape the output,
which I'll show you in a second,
you don't need to sanitize and clean
because escaping is enough protection.
If you do output raw and escaped content though,
then you should really try to sanitize it first
so that you do ensure
that you don't work with raw user input
but instead it was cleaned first.
Because sanitizing means that you take a look
at that user-generated content
and you look for a certain suspicious things
like script tags
and you then remove those script tags
or you convert them to something
that can't do any harm.
So sanitizing is important
if you plan to output unescaped user input.
If you do escape, you don't need it.
But we're going to take a look at both
and they offer let's start by taking a look at
escaping user input when we output it on a page.
But let's start with the first one
with escaping the content
so that instead of injecting a script like this
into the rendered template,
we actually just output it as plain text.
And that is possible.
Back here on the backend where we have this
injected content in the database right now,
we can go to the discussion EJS file.
And here there is something wrong
with how I'm outputting this comment text.
I'm using this EJS tag with a dash here
after the EJS opening tag.
Now it's been some time
since I introduced you to the EJS templating language,
but you might recall that we typically output text
and so on with the equal sign instead of the dash here.
The reason for that is that
when you use this EJS tag with the equal sign,
the content that will be output will be escaped,
it will be treated as plain text in the end.
And therefore you should always use
this equal sign instead of the dash
when outputting user-generated content
unless you really, really, really know
that you must parse it as actual HTML content.
And in that case,
you should have made sure that it was sanitized first.
But typically you wanna use this tag with the equal sign
and that is the tag we used
throughout this course therefore.
The tag with the dash can for example,
be seen when we use the include function
because there indeed
I'm about to include a bunch of HTML code
that's defined in a different file
and it should be parsed
and used as HTML here.
But here I'm including HTML code,
which I, the developer wrote, therefore it's safe,
it's not user-generated content.
User-generated content instead
should typically be output like this,
with the equal sign tag.
So if you save this now and reload this page,
now you see this as plain text
and you didn't get that alert.
And if we inspect this in the developer tools,
we indeed see here it still looks like HTML,
but it was parsed to the browser as plain text
and not as HTML.
So therefore actually here,
the browser did not execute this script
because to the browser it's just a regular text
and hence now we have this injected code
but it's not doing anything bad.
And that therefore
is something you should do.
Always output user-generated content
with that equal sign tag here
or with any other techniques that escapes the content.
If you're using a different templating engine,
all those engines typically have escape tags
and you should always escape
that user-generated content
before you output it.
Now that was escaping
before I mentioned that an alternative to that
would be to sanitize the user input.
Again, you don't need to do both
and typically you shouldn't sanitize,
you should instead prioritize escaping.
But it is worth knowing about sanitizing as well
especially if you have scenarios
where you can't escape
but where you need to output some raw HTML content
and where you need to parse it as HTML.
In such cases you might at least consider
sanitizing it before you output it.
Now sanitizing means cleaning
as I mentioned before,
it means that you take a look at that content
and you get rid of any dangerous things in that content.
And whilst you could write your own code,
which takes a look at the user content
and tries to clean it,
very often it's more convenient
to instead use third-party packages again.
For express JS,
if you search for express sanitize user input,
you will find discussions
that share different approaches that can be used
and different packages that can be used.
And you will also find packages themselves
like the express-validator package,
which can also be used for general input validation.
So that's also a package
you might wanna to take a look at.
Now for cross-site scripting attacks specifically,
you can also search for express-XSS.
You will also find more information
on how you can add cross-site scripting protection
in your express app.
And for example this XSS package here
could be used for that.
This is a package which you can install
into any node JS project.
And there you can use it to take some input, for example,
some user input,
which you received in one of your routes
and let the package parse
and convert it into a safe alternative.
So this package will then strip certain content,
like for example, such script text.
Now to use this package in our project,
we can stop the server
and npm install xss,
so install this package.
And then in the routes, for example,
we can import this package here
by requiring it as we always do.
And then in the post route
where we receive content
that might be malicious,
so where we receive user-generated content
that we plan on outputting on the page
there we can actually wrap this with a function call.
So we pass request body comment,
queue this xss function
and this function will then sanitize and clean it
and return the clean value.
And it's then the cleaned sanitized value
that is stored here in the database.
So if we now start this server again,
and I go to the MongoDB shell,
if I have a look at my comments,
I see this one comment here,
which we added before with the script in there.
But if I now go back to my main page
and I reload and try to do the same thing again,
so I try to add another script
where I say hacked again,
you'll see a different result now.
If I click send,
we see a different output here.
And we see a different output here
because the user input
was translated to this output here.
The script tag was basically replaced
with a non HTML version of that content.
If we have a look at the database,
we also see that this data was stored like that
in the database.
Now in case you're wondering what this
andlt and andgt means,
these are special character descriptions
you could say in HTML
where andlt; stands for the lower than sign
and andgt; stands for the greater than sign.
So that is how this was translated.
If you would switch back to outputting it
as raw HTML again,
then if you reload
you of course get the alert from the first try,
but then here you see the
second comment which I added,
which was sanitized before it was stored.
And here the strange lt and gt parts
are translated back into lower than
and greater than signs,
but this overall is not executed as JavaScript
but instead just output as plain text,
because it was already transformed
from HTML queue text
in the sanitization step
and now the browser just translates it
into a more user-friendly version.
But of course here I do escape
in addition to sanitizing it
and therefore we output the sanitized version
as plain text
and hence we have these ugly
lt and gt placeholders here.
That's why you typically shouldn't do both,
you should not sanitize and escape
instead you should do either of the two.
And typically just escaping is preferred,
but you should sanitize if you plan
on outputting unescaped content
so that you cleaned it first,
that's the idea.
Here we're doing both
because I wanted to show both.
Now you can also change the configuration
for this XSS package
to change how things are getting sanitized,
or as mentioned before you check out express-validator,
which is a great alternative,
which also offers sanitization,
but in addition also offers validation
where you can use a middleware-based approach
to validate incoming user data
and for example check if something is an email and so on.
So what we did manually before in the course,
this package could also do it for you
and it could also add sanitization in addition.
Of course you could also add logic
that for example checks if some user input
that's about to be saved
contains things like script text
and then block that input all together.
It will always depend on how your website should behave
and what your overall goal is.
But this is sanitization in action
and together with escaping user output,
you can now really protect
against cross-site scripting attacks
and you can make sure that this code,
this malicious JavaScript code
can't be injected into your site.