Uploaded by ajithyodo

pdfcoffee.com securing-node-applicationspdf-pdf-free

advertisement
Securing Node
Applications
Protecting Against OWASP Top 10 Risks
Chetan Karande
Securing Node Applications
Protecting Against OWASP Top 10 Risks
Chetan Karande
Beijing
Boston Farnham Sebastopol
Tokyo
Securing Node Applications
by Chetan Karande
Copyright © 2017 O’Reilly Media, Inc. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA
95472.
O’Reilly books may be purchased for educational, business, or sales promotional use.
Online editions are also available for most titles (http://oreilly.com/safari). For more
information, contact our corporate/institutional sales department: 800-998-9938 or
corporate@oreilly.com.
Editor: Allyson MacDonald
Production Editor: Shiny Kalapurakkel
Copyeditor: Octal Publishing, Inc.
Proofreader: Amanda Kersey
Interior Designer: David Futato
Cover Designer: Karen Montgomery
Illustrator: Rebecca Demarest
First Edition
April 2017:
Revision History for the First Edition
2017-04-14:
First Release
The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Securing Node
Applications, the cover image, and related trade dress are trademarks of O’Reilly
Media, Inc.
While the publisher and the author have used good faith efforts to ensure that the
information and instructions contained in this work are accurate, the publisher and
the author disclaim all responsibility for errors or omissions, including without limi‐
tation responsibility for damages resulting from the use of or reliance on this work.
Use of the information and instructions contained in this work is at your own risk. If
any code samples or other technology this work contains or describes is subject to
open source licenses or the intellectual property rights of others, it is your responsi‐
bility to ensure that your use thereof complies with such licenses and/or rights.
978-1-491-95241-2
[LSI]
Table of Contents
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
1. Injection Attacks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Command Injection
Database Injection
Conclusion
Additional Resources
1
5
11
11
2. Broken Authentication and Session Management. . . . . . . . . . . . . . . 13
Securing the Authentication Mechanism
Securing Session Management
Conclusion
Additional Resources
13
17
21
21
3. Cross-Site Scripting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Attack Mechanics
How to Prevent XSS
Conclusion
Additional Resources
23
24
29
29
4. Insecure Direct Object References. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Attack Mechanics
Preventing Insecure Direct Object References
Directory Traversal
Protecting Against Directory Traversal
Conclusion
Additional Resources
31
32
33
33
34
34
iii
5. Security Misconfiguration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Attack Mechanics
Preventing Security Misconfiguration
Conclusion
Additional Resources
36
38
42
42
6. Sensitive Data Exposure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Attack Mechanics
Protecting Against Sensitive Data Exposure
Conclusion
44
46
53
7. Missing Function-Level Access Control. . . . . . . . . . . . . . . . . . . . . . . . . 55
Attack Mechanics
Preventing Missing Function-Level Access Control
Conclusion
Additional Resources
55
56
59
59
8. Cross-Site Request Forgery. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Attack Mechanics
Protecting Against CSRF
Conclusion
Additional Resources
61
62
66
66
9. Using Components with Known Vulnerabilities. . . . . . . . . . . . . . . . . 67
Attack Mechanics
Protecting Against Unsecured External Components
Conclusion
Additional Resources
67
69
71
72
10. Unvalidated Redirects and Forwards. . . . . . . . . . . . . . . . . . . . . . . . . . 73
Attack Mechanics
Protecting Against Unvalidated Redirects and Forwards
Conclusion
iv
|
Table of Contents
73
76
80
Preface
The frequent data breaches and attacks on web applications in
recent years have plagued startups and large, high-profile organiza‐
tions alike. Although the rate of security incidents is on the rise,
based on the analysis of 64,199 security incidents and 2,260 breaches
in 2015, the Verizon Data Breach Investigations Report published
that the top 10 vulnerabilities accounted for 85 percent of these suc‐
cessful exploits. These findings were also in line with the results
from earlier years, thus revealing that hackers use what works, and
what works doesn’t seem to change all that often.
Securing web applications against these frequently occurring critical
risks is the most effective first step toward producing secure code.
The OWASP Top 10 is a community-driven, consensus-based list of
such commonly occuring, most-critical web application security
risks.
This book covers the OWASP Top 10 security risks as they pertain to
the Node.js web applications.
How This Book Is Organized
This book contains a chapter for each of the Open Web Application
Security Project (OWASP) Top 10 vulnerabilities. Each chapter con‐
tains primarily two sections:
• Explanations of how malicious actors conduct attacks. This
helps application builders to understand the scenarios to con‐
sider while coding the application.
v
• Specific measures to prevent the attack and its implementation
in Node.js.
To further aid learning, references to additional reading resources
and examples of related vulnerabilities found in Node.js applications
and npm modules are included.
This book is intended to act as a quick reference guide during
design, development, code reviews, testing, and ultimately contrib‐
ute toward baking in security right from the beginning in the Node
applications you build.
Conventions Used in This Book
The following typographical conventions are used in this book:
Italic
Indicates new terms, URLs, email addresses, filenames, and file
extensions.
Constant width
Used for program listings, as well as within paragraphs to refer
to program elements such as variable or function names, data‐
bases, data types, environment variables, statements, and key‐
words.
Constant width bold
Shows commands or other text that should be typed literally by
the user.
Constant width italic
Shows text that should be replaced with user-supplied values or
by values determined by context.
This element signifies a tip or suggestion.
This element signifies a general note.
vi
|
Preface
This element indicates a warning or caution.
Using Code Examples
Supplemental material (code examples, exercises, etc.) is available
for download at https://github.com/ckarande/securing-node-appsbook-examples.
This book is here to help you get your job done. In general, if exam‐
ple code is offered with this book, you may use it in your programs
and documentation. You do not need to contact us for permission
unless you’re reproducing a significant portion of the code. For
example, writing a program that uses several chunks of code from
this book does not require permission. Selling or distributing a CDROM of examples from O’Reilly books does require permission.
Answering a question by citing this book and quoting example code
does not require permission. Incorporating a significant amount of
example code from this book into your product’s documentation
does require permission.
We appreciate, but do not require, attribution. An attribution usu‐
ally includes the title, author, publisher, and ISBN. For example:
“Securing Node Applications by Chetan Karande (O’Reilly). Copy‐
right 2017 Chetan Karande, 978-1-491-95241-2.”
If you feel your use of code examples falls outside fair use or the per‐
mission given above, feel free to contact us at permis‐
sions@oreilly.com.
O’Reilly Safari
Safari (formerly Safari Books Online) is a
membership-based training and reference
platform for enterprise, government, educa‐
tors, and individuals.
Members have access to thousands of books, training videos, Learn‐
ing Paths, interactive tutorials, and curated playlists from over 250
publishers, including O’Reilly Media, Harvard Business Review,
Preface
|
vii
Prentice Hall Professional, Addison-Wesley Professional, Microsoft
Press, Sams, Que, Peachpit Press, Adobe, Focal Press, Cisco Press,
John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks,
Packt, Adobe Press, FT Press, Apress, Manning, New Riders,
McGraw-Hill, Jones & Bartlett, and Course Technology, among oth‐
ers.
For more information, please visit http://oreilly.com/safari.
How to Contact Us
Please address comments and questions concerning this book to the
publisher:
O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international or local)
707-829-0104 (fax)
For more information about our books, courses, conferences, and
news, see our website at http://www.oreilly.com.
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia
Acknowledgments
I would like to express my gratitude to O’Reilly Media, especially
Allyson MacDonald for providing me the opportunity and inspira‐
tion for writing this report. I would also like to thank the talented
and passionate O’Reilly production team, including Shiny Kalapur‐
akkel and Octal Publishing, Inc., for providing insightful sugges‐
tions and finishing touches to my work.
I would like to thank Bryan Devereaux for his invaluable technical
review and feedback on improving this report. This report could not
have been possible without the patience and strong support of my
wonderful wife, Aparna, and loving kids, Yash and Jui, while I was
devoting hours of family time to completing this report.
viii
|
Preface
Finally, I would like to thank my mom and dad for being a continu‐
ous stream of love and support in all endeavors of my life.
Preface
|
ix
CHAPTER 1
Injection Attacks
Injection vulnerabilities are the most prevalent and dangerous of
web application vulnerabilities. Typically, an injection vulnerability
manifests when application code sends untrusted user input to an
interpreter as part of a command or query. Attackers exploit the vul‐
nerability by crafting hostile data that tricks the interpreter into exe‐
cuting unintended commands or accessing data without proper
authorization.
In this chapter, we review two prevailing mechanisms of injection
attacks: command and database.
Command Injection
Using command injection, an attacker can execute arbitrary com‐
mands on the host operating system of a vulnerable application.
This flaw gives enormous opportunities to an attacker, ranging from
reading restricted file contents to installing malware with which the
attacker can take full control of the server and host network.
Attack Mechanics
The child_process core module enables Node developers to invoke
underlying OS commands from the application code. Due to its
name and simplicity of use, the child_process.exec method is
commonly used for making system calls.
1
The exec method takes three arguments: a command in string for‐
mat, an optional options object, and a callback function, as demon‐
strated in Example 1-1.
Example 1-1. child_process.exec command
child_process.exec(command[, options][, callback])
Although the exec method executes OS commands in a nonblock‐
ing manner, perfectly aligning with Node’s async programming
paradigm, its flexibility to pass the command as a string often
invites injection flaws. This is particularly the case when a user input
is used to construct the command.
For example, Example 1-2 shows using the child_process.exec
method to invoke the gzip command that appends a user-supplied
dynamic file path to construct the gzip command.
Example 1-2. Executing gzip command by using child_process.exec
child_process.exec(
'gzip ' + req.body.file_path,
function (err, data) {
console.log(data);
});
To exploit the injection vulnerability in the preceding code, an
attacker can append ; rm -rf /, for instance, to the file_path
input. This allows an attacker to break out of the gzip command
context and execute a malicious command that deletes all files on
the server. As part of the user input, an attacker also can chain mul‐
tiple commands by using characters such as ;, &, &&, |, ||, $(), <, >,
and >>.
The attack manifests because, under the hood, the exec method
spawns a new bin/sh process and passes the command argument for
execution to this system shell. This is equivalent to opening a Bash
interpreter for an attacker to run any commands with the same priv‐
ileges as the vulnerable application.
Preventing Command Injection
Now that you know the potential of an injection attack to cause
severe damage, let’s go over methods to prevent it.
2
|
Chapter 1: Injection Attacks
Use execFile or spawn instead of exec
When possible, use the child_process module’s execFile or spawn
methods instead of exec. Unlike exec, the spawn and execFile
method signatures force developers to separate the command and
its arguments.
The code in Example 1-3 demonstrates executing the gzip com‐
mand by using execFile method.
Example 1-3. Executing the gzip command by using
child_process.execFile
// Extract user input from request
var file_path = req.body.file_path;
// Execute gzip command
child_process.execFile(
'gzip',
[file_path],
function (err, data) {
console.log(data);
});
Any malicious commands chained to file_path user input end up
in execFile method’s second argument of type array. Any malicious
commands in user input are simply ignored or cause a syntax error
if they’re not relevant to the target command, thus foiling the com‐
mand injection attempts.
Input validation
Although execFile or spawn are safer alternatives to exec, these
methods cannot completely prevent command injection. The related
scenarios include developers using these methods to invoke a cus‐
tom script that processes user inputs, or to execute certain OS com‐
mands (such as find, awk, or sed) that allow passing options to
enable file read/write.
Just like other injection attacks, command injection is primarily pos‐
sible due to insufficient input validation. To protect against it, verify
that user-controlled command arguments and command options
are valid.
Command Injection
|
3
Using a Whitelist Approach for Input Validation
When writing the validation logic, use a whitelist
approach; that is, define what is permitted and reject
any input that doesn’t fit this definition. Avoid writing
this logic in an opposite manner (blacklist approach),
or, in other words, by comparing input against a set of
known unsafe characters. Attackers can often find a
way to circumvent such filters by being creative with
the input construction.
Using the Joi Module for Input Validation
The Joi module provides a convenient and robust
mechanism for input validation. It allows externalizing
validations rules in a schema object and validating user
inputs against it. These rules can verify the shape of the
user input object, data type, and specific constraints on
input values, as well as whitelist validation by using the
any.valid() API method.
Limit user privileges
If an attacker becomes successful at command injection, the injected
command runs with the same OS-level privilege as the vulnerable
application. By following the principle of least privilege, you can
limit the attack surface of the command injection.
Specifically, the Node.js process should not run with root privileges.
Instead, run it with a user that has access to only the required
resources and no read–write access outside the web application
directory.
Command Injection in Node Modules
The command injection is a frequently found vulnerability in Node
modules. Following are some related advisories published by the
Node Security Project:
dns-sync (<0.1.1)
The dns-sync library resolves hostnames by using a shell script
for DNS lookup. This module was vulnerable to arbitrary com‐
mand execution via maliciously formed hostname user input.
As a remediation, the author applied input validation to verify
a valid hostname before using it in the program.
4
|
Chapter 1: Injection Attacks
printer (⇐ 0.0.1)
This module used child_process.exec to issue the lpr system
command to print. The user-supplied _printer name_ input
value was an attack vector for the command injection. The
author fixed it by replacing the exec method by execFile.
Similarly, vulnerabilities in the following node modules were fixed
by using execFile or the spawn method instead of exec:
• ep_imageconvert
• hubot-scripts
• ungit
Database Injection
With a successful database injection, an attacker can execute mali‐
cious commands on a database to steal sensitive data, tamper with
stored data, execute database administration operations, access con‐
tents of files present on the database filesystem, and, in some cases,
issue commands to the host operating system.
Let’s review the injection attacks on both SQL and NoSQL databases.
SQL Injection Attack Mechanics
Dynamic database queries that include user-supplied inputs are the
primary target behind the SQL injection attack. When malicious
data is concatenated to a SQL query, the SQL interpreter fails to dis‐
tinguish between the intended command and input data, resulting
in the execution of the malicious data as SQL commands.
Let’s consider a vulnerable SQL query, as shown in Example 1-4, that
authenticates a user.
Example 1-4. A dynamically constructed SQL query that is vulnerable
to SQL injection
connection.query(
'SELECT * FROM accounts WHERE username ="'
+ req.body.username + '" AND password = "' + passwordHash + '"',
function(err, rows, fields) {
console.log("Result = " + JSON.stringify(rows));
});
Database Injection
|
5
Example 1-4 illustrates code that dynamically constructs a SQL
query by appending the user-supplied request parameter username.
To exploit this query, an attacker can enter admin' -- as a username,
which ultimately results in executing the SQL query, as shown in
Example 1-5.
Example 1-5. Resultant query with username as admin’ —
SELECT * FROM accounts WHERE username = 'admin'
The malicious user input eliminated the need for an attacker to sub‐
mit a correct password because the part after -- in a SQL query is
interpreted as a comment, thus skipping the password comparison.
Another, more destructive, variation of SQL injection is possible
with databases that support batch execution of multiple statements
when those statements are followed by a semicolon.
For example, if an attacker enters the string admin'; DELETE FROM
accounts; -- as username, the resultant query is equivalent to two
statements, as shown in Example 1-6.
Example 1-6. Resultant query containing multiple SQL statements
SELECT * FROM accounts WHERE username = 'admin';
DELETE FROM accounts;
This query results in removing all user accounts from the database.
An attacker can use a wide variety of malicious inputs for conduct‐
ing injection, such as the following:
• 1' OR '1'='1 or its equivalent URL-encoded text 1%27%20OR
%20%271%27%20%3D%20%271 to get all records and ignore the lat‐
ter part of the WHERE clause
• % in user input to match any substring or _ to match any charac‐
ter
Preventing SQL Injection
The good news is that SQL injection is easy to prevent by using a
few simple measures.
6
|
Chapter 1: Injection Attacks
Use parameterized queries to bind all user-supplied data
A parameterized query is considered a silver bullet for preventing
the dreaded SQL injection. Parameterized queries prevent an
attacker from changing the intent of a query, and enable the SQL
interpreter to distinguish clearly between code and data. Hence, as
shown in Example 1-7, always bind all user inputs to the query with
parameters.
Example 1-7. Using a parameterized query to prevent SQL injection
var mysql = require('mysql2');
var bcrypt = require('bcrypt-nodejs');
// Prepare query parameters
var username = req.body.username;
var passwordHash = bcrypt.hashSync(req.body.password,
bcrypt.genSaltSync());
// Make connection to the MySQL database
var connection = mysql.createConnection({
host : 'localhost',
user : 'db_user',
password : 'secret',
database : 'node_app_db'
});
connection.connect();
// Execute prepared statement with parameterized user inputs
var query = 'SELECT * FROM accounts WHERE username=? AND password=?';
connection.query(query, [username, passwordHash],
function (err, rows, fields) {
console.log("Results = " + JSON.stringify(rows));
});
connection.end();
If an attacker enters the username as admin' --, the resultant query
from the code in Example 1-7 would explicitly look for a username
that matches the exact string admin' -- instead of admin, thus foil‐
ing the SQL injection attack.
Database Injection
|
7
Using Stored Procedures Instead of Parameterized Queries
If SQL query construction takes place inside a stored
procedure, generally stored procedures are safe and
have the same effect as parameterized queries. How‐
ever, you need to ensure that stored procedures do not
use unsafe dynamic SQL generation by integrating user
inputs.
Apply input validations based on whitelist
Validating user input data serves as an additional layer of protection
against SQL injection. While writing the validation logic, compare
the input against a whitelist of allowed options.
Besides SQL injection, input validation is also crucial in preventing
other attacks, such as cross-site scripting (XSS), HTTP parameter
pollution, denial-of-service, and other types of injection attacks.
Although input validation is a highly recommended
practice and can detect erroneous input before passing
it to the SQL query, it is not an alternative to using par‐
ameterized queries. Validated data is not necessarily
safe to insert into dynamic SQL queries constructed by
using string concatenation.
Use database accounts with least privilege
If an attacker becomes successful at SQL injection, to minimize the
potential damage, use database accounts with the minimum
required privileges. Here are some specific recommendations:
• Never use a database account in Node application code that has
admin-level rights.
• Consider creating separate users with read-only and read-write
access, and choose the user account with the minimum required
privileges that meet the requirements.
• When using stored procedures, restrict user account rights to
allow executing only required, specific stored procedures.
• Use SQL views to limit user account access to specific columns
of a table or joins of tables.
• If multiple applications share a common database, use different
database accounts for each application.
8
| Chapter 1: Injection Attacks
• Beyond database privileges, minimize the privileges of the oper‐
ating system account that the database uses.
Database Injection in Node Modules
Prior versions of the sequelize (<2.0.0-rc8) Node module were vul‐
nerable to SQL injection attack when the application using it passed
untrusted user inputs to the order parameter, as shown in
Example 1-8.
Example 1-8. SQL injection in a sequelize module
Test.findAndCountAll({
where: { id :1 },
order : [['id', 'UNTRUSTED USER INPUT']]
})
An example of malicious user input in this case can be DESC;
delete from test;.
As a fix, as shown in Example 1-9, the author added a whitelistbased validation to prevent invalid inputs, rejecting anything other
than ASC or DESC as a value for the order parameter:
Example 1-9. Whitelist-based fix for SQL injection in the sequelize
module
var _ = require('lodash');
...
if (!_.contains(['ASC', 'DESC'], order.toUpperCase())) {
throw new Error(util.format(
'Order must be \'ASC\' or \'DESC\', \'%s\' given',
order
));
}
NoSQL Injection Attack Mechanics
Even though NoSQL databases do not use SQL syntax, it is possible
to construct unsafe queries with user-supplied inputs. Hence,
NoSQL databases are not inherently immune to injection attacks.
To illustrate NoSQL injection attacks, let’s consider a MongoDB find
query, as shown in Example 1-10. I chose MongoDB for this exam‐
ple just because it is one of the popular NoSQL databases.
Database Injection
|
9
Example 1-10. A find query using MongoDB
db.accounts.find({
username: post_username,
password: post_password
});
In Example 1-10, post_username and post_password are usersupplied inputs.
In MongoDB, the $gt comparison operator selects documents
where the value of the field is greater than the specified value. Now,
let’s consider a malicious input, as shown in a JSON object in
Example 1-11.
Example 1-11. A malicious input object added to a find query
{
"post_username": "admin",
"post_password": {$gt: ""}
}
With this input, the find query in Example 1-10 would compare if
the password in the database is greater than an empty string, which
would return true, resulting in retrieving the admin user’s account
data.
You can achieve the same results by using another comparison oper‐
ator, such as $ne, in the input.
Another mechanism to manifest NoSQL injection is exploiting the
$where operator, which takes a JavaScript function and processes
user inputs within the function. Example 1-12 presents code that is
vulnerable to just such a NoSQL injection:
Example 1-12. NoSQL Injection with the $where operator
db.myCollection.find({
active: true,
$where: function() { return obj.age < post_user_input; }
});
In example Example 1-12, the user input post_user_input is used
in an unsafe manner. If an attacker passes a valid JavaScript state‐
ment 0; while(1); as a value for post_user_input, the find query
would run an infinite loop, making the database unresponsive.
10
|
Chapter 1: Injection Attacks
The $where operator-based NoSQL injection provides an attacker
with the ability to craft malicious inputs using JavaScript language,
which offers greater flexibility and options to an attacker when com‐
pared to a plain old SQL injection attack, in which strict SQL inter‐
preter rules confine the ways to construct malicious inputs.
Preventing NoSQL Injection
Here are ways to prevent NoSQL injection:
• There is no equivalent mechanism to parameterized SQL quer‐
ies for NoSQL databases. To mitigate NoSQL injection, your
best option is to validate and escape all user-supplied inputs
before using it.
• Avoid using options such as $where with JavaScript functions
that directly process user-supplied inputs.
• Similar to SQL databases, using database accounts with the least
privileges can limit the potential damage if an attacker becomes
successful at NoSQL injection.
Conclusion
Although injection flaws are very prevalent, as we reviewed in this
chapter, these security flaws are easy to avoid by applying safe cod‐
ing practices. Using APIs that avoid passing untrusted data directly
to an interpreter, validating user inputs, and applying the principle
of least privileges are the key mechanisms to prevent it.
Additional Resources
Here are some more resources related to injection attacks:
“Testing for SQL Injection”
Injection flaws are difficult to discover via testing. This article
goes over techniques and tools to test for them.
“Stored Procedure Attacks”
This article illustrates SQL injection attacks against stored pro‐
cedures that are often assumed safe against SQL injection by
default.
Conclusion
|
11
Server-Side JavaScript Injection
This whitepaper explains server-side JavaScript injection when
using eval in JavaScript code to parse JSON requests.
12
|
Chapter 1: Injection Attacks
CHAPTER 2
Broken Authentication and
Session Management
As the name indicates, broken authentication and session manage‐
ment risks target flaws in user authentication and session manage‐
ment implementation. An attacker can exploit these flaws mainly to
impersonate other users and perform malicious or unintended oper‐
ations on their behalf.
As per the Open Web Application Security Project (OWASP), this is
the second most highly ranked web security risk and is widespread.
It encompasses a broad category of attack mechanics, all related to
compromising a user’s identity.
The typical attack scenarios include exploiting weak account pass‐
words, passwords stolen from database breaches, active session IDs
stolen from a victim user’s browser or network communication, or
bugs in password management features such as changing or recov‐
ering a forgotten password.
Let’s dive deeper into specific attack mechanics and mitigations.
Securing the Authentication Mechanism
Even though two-factor authentication is safer and experiencing
increasing adoption, requiring a valid user ID and password combi‐
nation to log in is still the most commonly used authentication
mechanism.
13
As per a study of 2,260 confirmed breaches in 2015, 63 percent
involved weak, default, or stolen passwords. Thus, safety of user
accounts hinges on protecting these credentials, mainly passwords,
from attackers.
Let’s go through attacks targeting the authentication mechanism and
look at some mitigations.
Password Cracking
Password cracking is equivalent to a burglar picking the lock on the
front door of a house to break in. Password cracking isn’t always as
difficult as one would expect, given the various tools and techniques
that are available to automate the process.
The most guaranteed but time-consuming method to crack a pass‐
word is the brute-force attack. It works by cycling through every
possible combination, ranging from a single character to N charac‐
ters, and trying to log in using it. Tools such as John the Ripper or
Brutus are commonly used to accomplish this.
Because the brute-force technique is slow, a significantly faster ver‐
sion of it is the dictionary attack. Unlike brute-force, a dictionary
attack doesn’t enumerate through every possible character. Instead,
it cycles through words in a particular dictionary or word list to
check if it works as a password. Tools such as Hydra can not only
automate this process, but also morph the characters in words and
try combinations of these words.
Preventing Password Cracking
Implementing these simple guidelines make password cracking
attempts significantly less fruitful:
• Require strong passwords. Do not prevent users from entering
long passphrases or highly complex passwords. Additionally, to
block the dictionary attack, prevent commonly chosen and
weak passwords.
• Rate-limit maximum login attempts allowed per user ID or IP
for a given time period. Multiple node modules provide this
functionality.
• Implement an account lockout mechanism by temporarily pre‐
venting a user from logging in upon multiple failed attempts.
14
| Chapter 2: Broken Authentication and Session Management
The lockout period should be short (20 minutes, for example),
and accounts should unlock automatically after that period to
prevent exploiting the lockout mechanism for distributed
denial-of-service (DDoS) attacks.
• Log all login failures with request metadata (such as IP address)
and lockouts, and actively monitor it.
• Secure password recovery features such as changing a password
or recovering a forgotten password. Here’s a few important fac‐
tors to consider in this regard:
1. Ensure that the change password functionality requires the
old password.
2. If secret questions are provided to retrieve the password,
avoid questions with a few finite number of answers, or with
answers based on facts about the user found on public or
social-media sites.
3. Ensure that the forgot password and other recovery paths do
not reveal the current password. In addition, instead of send‐
ing the new password in clear text, email a link to the user to
reset it.
Rainbow Tables Attack
Storing passwords in an encrypted format is highly recommended
because it protects against incidents such as an attacker gaining
access to the database. However, just encryption is not sufficient.
Attackers often use rainbow tables to crack encrypted password
hashes. This is a long table of [plaintext]-[encrypted] pairs for
most common passwords. Attackers use it to look up the stolen
password hash and get the corresponding plain-text password.
Protecting Against Rainbow-Table Attacks
To protect against a rainbow-table attack, combine a random value,
referred to as salt, with the password before encrypting it. Even with
a small random salt of 16 bits, rainbow tables are worthless because
there are now 65,536 variants of each hash, drastically increasing the
number of entries and size of the rainbow-table files.
Securing the Authentication Mechanism |
15
The bcrypt algorithm is specifically suitable for encrypting user
passwords. It incorporates a salt value by default. It is also a deliber‐
ately slow algorithm, thus significantly hampering an attacker’s
attempt to compute the hashes for a large number of possible pass‐
words. Moreover, it allows you to control the cost of encryption
with a configurable work factor, which you can subsequently
increase as computational power increases and hardware costs fall
in the future.
The Example 2-1 shows how to use the bcrypt algorithm for hash‐
ing a password using the bcrypt module.
Example 2-1. Encrypting a password using the bcrypt.hash
var bcrypt = require('bcrypt');
bcrypt.genSalt(12, function(err, salt) {
bcrypt.hash(user.password, salt, function(err, hash) {
// Store hash in the password database
});
});
The first argument to genSalt, the work factor, decides the cost or
slowness of computing. For example, setting it to 12 means 212
rounds.
To check if a user entered a valid password, simply use the compare
method, as shown in Example 2-2.
Example 2-2. Comparing a password by using bcrypt.compare
// The hash below is the encrypted password from the database
bcrypt.compare(user.password, hash, function(err, res) {
// res == true for correct password
// res == false for wrong password
});
16
|
Chapter 2: Broken Authentication and Session Management
Authentication Using OAuth: Tools of the Trade
The OAuth standard enables developers to allow users to log in to
the application by using their Google, Facebook, Twitter, GitHub,
or many other accounts, without exposing their password.
The Passport module provides an easy way for Node applications to
integrate with these OAuth providers. Besides supporting tradi‐
tional authentication with username and password, Passport pro‐
vides individual modules, called strategies, from more than 300
OAuth providers from which you can choose. See the Passport doc‐
umentation for code examples and more details.
Another open source tool, Anvil Connect, greatly simplifies the
integration with multiple OAuth providers for Node apps that con‐
sume third-party data from multiple sources. Beyond local pass‐
words, OAuth 1.0, and OAuth 2.0, it also supports authentication
and single sign-on (SSO) with OpenID, SAML 2.0, LDAP, Active
Directory, and more.
Securing Session Management
Because HTTP is a stateless protocol, to remember a user, the server
maintains a session identifier (SID) that is passed back and forth
between the client and server. This SID is equivalent to a user’s iden‐
tity for the server; hence, it also is a primary target for attackers who
are trying to impersonate other users.
Let’s go over related attacks and measures to protect against them.
Session Hijacking Attack
In session hijacking, an attacker steals the session cookie from an
active user session and uses it to re-create the session without need‐
ing to enter the user ID or password.
An attacker can steal the session cookie using techniques such as
utilizing a network sniffer, which is a piece of malicious script injec‐
ted by using a cross-site scripting (XSS) attack or malware installed
on a victim’s computer.
Securing Session Management
|
17
Protecting Against Session Hijacking
The following are some guidelines to protect against session hijack‐
ing.
Never include the SID in a URL parameter
URLs can be bookmarked, cached, shared, logged, or sniffed on the
network. Hence, a safer way to store and transmit SIDs is through
cookies.
Example 2-3 shows how to create a session that uses cookies
employing the express-session middleware.
Example 2-3. Creating express-session cookie-based authentication
var express = require('express');
var session = require('express-session');
var app = express();
app.use(session({
resave: false, // Prevent saving session if unmodified
saveUninitialized: false, // Prevent creating session until
// something to store
secret: '...', // Secret key gets used to sign the cookie
cookie: {}
}));
app.listen(80);
A point worth noting about Example 2-3 is that the cookie is used
just to store the SID, not the session data. Session data stays on the
server side. The default server-side session storage is MemoryStore,
which is not designed for a production use; instead, use a databasebacked session middleware such as connect-mongo, connectcouchbase, or others.
Set httpOnly and secure flags on cookie options
Now, because we have SIDs transmitted only by using a cookie, next
we can make the cookie difficult to steal by setting the httpOnly and
secure flags on the cookie’s options. Here’s what this accomplishes:
• The httpOnly flag ensures that the cookie is not accessible to
JavaScript code running on the browser, foiling the attempts to
steal it by using XSS attack. See Chapter 3 for more details on
XSS.
18
|
Chapter 2: Broken Authentication and Session Management
• The secure flag ensures that the cookie is transmitted only on a
secure HTTPS connection, thus eliminating network sniffing
and man-in-the-middle attacks. However, if a server sets a
cookie with a secure flag from a nonsecure connection, the
cookie can still be intercepted on its way to the user. Therefore,
for maximum security, all pages should use a secure connection.
If a Node.js application is behind a proxy, along with secure:
true, set the trust proxy property in express application set‐
tings, as shown in Example 2-4.
Example 2-4. Hardening session cookie security
var app = express();
app.set('trust proxy', 1); // Trust first proxy
app.use(session({
resave: false,
saveUninitialized: true,
secret: '...',
cookie: {
httpOnly: true, // Prevent cookie accessed by JavaScript code
secure: true, // Prevent browser sending cookie on unencrypted
// connection
maxAge: 10 * 60 * 1000 // 10 minutes
}
}));
app.listen(80);
Set the session timeout period
It is important to limit the lifetime of an active session on the server
by using either expires or maxAge cookie options. This protects
against events such as an attacker accessing a shared computer on
which the victim user left an active session open without explicitly
logging out, or an attacker stealing the session cookie by using ses‐
sion hijacking.
Depending on the type of session store used, the default value for
session timeout varies. For example, by default, it is one day for
connect-redis–based RedisStore, and 14 days for connect-mongo–
based MongoStore. Set session timeout to the minimum possible
value depending on the context of the application. Example 2-4
shows using maxAge cookie options to set the inactive session time‐
out.
Securing Session Management
|
19
Destroy session on logout
Lastly, you should make the logout button plainly visible and easily
accessible from all pages that need authentication. Upon logout,
destroy active session data by using the destroy method on
session; for example, req.session.destroy(). Additionally, inva‐
lidate SSO tokens if they are in use.
Session Fixation
To carry out a session fixation attack, typically an attacker performs
these steps:
1. Obtains a legitimate SID from the server by making an HTTP
request; for example, by opening a login page.
2. Sets the cookie with SID in the victim user’s browser and indu‐
ces the victim user to use it to authenticate on the target applica‐
tion.
3. Hijacks the user-validated session with the knowledge of the
SID used.
Protecting Against Session Fixation Attack
The key to prevent a session fixation attack is to simply regenerate
the SID each time a user authenticates. Thus, even if an attacker
fixes a known SID on the victim user’s browser, that SID would be
no longer valid when the attacker attempts to use it.
Example 2-5 demonstrates that to regenerate the session, simply
invoke the regenerate method on session. This initializes a new
SID and the session instance that is accessible by using req.session
thereafter.
Example 2-5. Regenerating a session upon successful login
req.session.regenerate(function(err) {
// A new session here
})
20
|
Chapter 2: Broken Authentication and Session Management
Conclusion
In this chapter, we covered attacks that mainly aim to steal a user’s
identity to access that user’s account on a web application. Pass‐
words and SIDs are the key targets for an attacker.
As covered in this chapter, we can harden password security by
requiring strong passwords, using a one-way slow and strong
encryption algorithm that incorporates salt, and transmitting cre‐
dentials on a secure network.
Similarly, we can protect session cookies by using techniques such as
setting secure, HTTPOnly flags on cookie configurations, limiting the
session lifetime, and regenerating the session immediately after suc‐
cessful authentication.
Additional Resources
Here are OWASP checklists and cheat sheets related to authentica‐
tion and session management:
• OWASP Application Security Verification Standard
• OWASP Authentication Cheat Sheet
• OWASP Forgot Password Cheat Sheet
• OWASP Session Management Cheat Sheet
Conclusion
|
21
CHAPTER 3
Cross-Site Scripting
Cross-site scripting (XSS) is the most prevalent web application
security flaw. Attackers use XSS for a variety of intents, such as
hijacking a user session, navigating a user to a malicious page, log‐
ging keystrokes, manipulating page contents, posting data to a
remote server, or installing a malicious program on a user’s
machine.
In this chapter, we learn about XSS attack mechanics and ways to
protect against it.
Attack Mechanics
The key mechanism behind XSS attack is content injection, wherein
an attacker inserts malicious JavaScript content on a web page.
Attackers typically inject just a tiny bit of initial script, which, when
executed, downloads additional required scripts.
Depending on whether the target web application saves the injected
malicious content on the server, there are two primary XSS attack
mechanisms: reflected and stored. Let’s review each of these mecha‐
nisms in more detail.
Reflected XSS
In this mechanism, an attacker provides a link to the victim user
with malicious contents embedded in request parameters. When a
victim user clicks this link, a request goes to the target application
server, where a flaw in the application code results in including the
23
malicious content in the request as-is in the response body. When
the contents of this response load on the user’s browser, the mali‐
cious code executes, causing a successful XSS attack.
For apps that generate page content on the client side, the reflected
XSS can still manifest if the unsafe JavaScript code takes the mali‐
cious content from the attacker-supplied URL and injects it into the
web page.
Reflected XSS is typically a one-to-one attack because an attacker
needs to send the malicious link to each user; hence, it affects only
those users that click it. So, it has relatively less chances of success as
compared to the stored XSS attack, which we’ll review next.
Stored XSS
In a stored XSS attack, the malicious content injected by an attacker
is saved by the application. After that, every user visiting the same
page becomes a victim of the XSS attack. For example, consider the
comments on a blog post. If the comments with malicious content
posted by an attacker are not validated and sanitized before saving
into the database, every time the blog post is read by any user, the
malicious code injected in the comments is executed. Thus, stored
XSS is a one-to-many attack with a greater possibility to cause dam‐
age.
DOM-Based XSS
In both reflected and stored XSS mechanisms, actual
content injection could take place either on a server or
on a user’s browser.
In past years, most security analysts considered stored,
reflected, and DOM-based XSS as three different
attack mechanisms. However, as OWASP clarified in
this article, the DOM-based XSS is not a separate
mechanism, but a particular case of either stored or
reflected XSS attack, wherein payload injection hap‐
pens on a victim user’s browser.
How to Prevent XSS
The XSS flaw occurs when the application code takes untrusted data
supplied by a user and renders it in the browser without proper vali‐
24
| Chapter 3: Cross-Site Scripting
dation or escaping. In this case, the browser is unable to distinguish
between the script added by the application developer and the piece
of malicious script injected by an attacker. Knowing this root cause,
let’s go over ways to mitigate XSS.
Add a content-security-policy header
Adding a content-security-policy (CSP) response header is the easi‐
est and most robust way to mitigate XSS. The CSP header allows
developers to specify a whitelist of domains from which the browser
is allowed to accept content. Thus, the browser blocks any scripts
injected by an attacker because these scripts would not originate
from the whitelisted domains.
When a CSP header is present in the response, by default the
browser rejects any inline scripts on the page, even if those were
added by the application developer. Hence, include all script content
inside JavaScript files.
To test how the site behaves when enabling the CSP
header for the first time, or with updates to existing
policy, you might find the report-only mode useful. In
this mode, the policy is not enforced, but any viola‐
tions are reported to a provided URI.
Beyond the JavaScript files, the CSP header can also whitelist sour‐
ces for images, CSS, fonts, and media such as audio and video.
Example 3-1 shows how to add a CSP header to Node apps by using
the helmet module.
Example 3-1. Adding a CSP response header by using helmet
var policy = {
defaultPolicy: {
"default-src": ["'self'"],
"img-src": ["static.example.com"]
}
}
helmet.csp.policy(policy);
Here’s what’s happening in Example 3-1:
How to Prevent XSS
|
25
Setting default-src to self allows the browser to download
JavaScript code only from the same domain from which the
page was loaded.
In img-src, we are adding an exception for images to come
from an additional domain, static.example.com.
Thus, a CSP header makes it difficult for an attacker to inject mali‐
cious scripts on the pages.
Hardening CSP Against Polyglot JPEGs
A polyglot JPEG image contains JavaScript code in its
header that can be executed by including the image on
a page using a script tag such as the following:
<script charset="ISO-8859-1"
xss.jpg"></script>
src="/uploads/
If you allow users to upload images on the same
domain as your app, and the CSP default-src policy
is set to self, an attacker can bypass this CSP protec‐
tion and execute malicious code. An attacker can do so
by uploading a polyglot JPEG containing the code and
injecting a script tag on the page that points to the
uploaded image.
To protect against it, if you allow image uploads on
your site, place uploaded images on a separate domain
and ensure that this domain is not whitelisted for
script execution as part of the CSP policy. Also, sanitize
the uploaded images by removing JPEG header com‐
ments. For more details, take a look here.
Encode untrusted data
Another effective way to protect against XSS attacks is by encoding
user-supplied data before using it on the page. The output encoding
converts the untrusted data into a safe form so that it is displayed as
data to the user instead of being executed as code in the browser.
However, the key factor for the success of this approach is that out‐
put encoding must be applied as per the context, that is the place
where the untrusted data is expected to go on the web page. When
rendering a web page, the browser follows different rendering rules
for the various contexts. Hence, the context-sensitive encoding is
26
| Chapter 3: Cross-Site Scripting
necessary. These contexts are HTML tag body, HTML tag attribute,
CSS, JavaScript code, and URI.
Table 3-1 shows examples of the contexts and how to encode data
for each.
Table 3-1. Output encoding for different contexts
Context
HTML
entity
Code sample
Encoding type
<span>Untrusted Data</span>
Convert &, <, >, ", ', and /
to &, <, >,
", ', and
/, respectively. The
escape-html node module
and most of the template
libraries also take care of it.
HTML
attribute
encoding
<input type="text" name="fname"
value=" Untrusted Data">
Escape all nonalphanumeric
characters and spaces with
the HTML hex entity
(&#xHH;) format.
Most of the template
libraries take care of it. In
addition, include quotes
around the attribute value.
URI
encoding
<a href="Untrusted Data">Show
Details</a>
or <a href="/site/search?
value="Untrusted Data">Show Details</a>
Use encodeURI() for
attributes and encodeURI
Component() for query
params.
JavaScript
encoding
<script>var value='Untrusted Data'</
script> or
<script>window.setInterval(Untrusted
Data);</script>
Ensure that JavaScript
variables are inside quotes.
Escape all nonalphanumeric
characters having ASCII
values less than 256 with
Unicode (\uXXXX) or hex
(xHH) encoding format.
CSS
encoding
<div style="width: Untrusted
Data;">Selection</div>
Escape all nonalphanumeric
characters having ASCII
values less than 256 with the
hex (\HH) escaping format.
DOMPurify Module for HTML Sanitization
The DOMPurify module sanitizes HTML and prevents
XSS attacks. It supports sanitizing HTML5, CSS, Java‐
Script, SVG, MathML, HTML custom data attributes,
and Shadow DOM. It uses the native browser features
to implement XSS filters for faster performance.
How to Prevent XSS
|
27
To protect from possible code injections on both client
and server sides, apply the output encoding at both
sides.
Applying the HttpOnly Flag on Session Cookies
Applying httpOnly flag on cookies prevents scripts from accessing
it. Even though adding this flag won’t prevent the malicious script
injection, it foils an attacker’s attempt to steal the session cookie,
which is often a primary goal behind the XSS attack.
The Example 3-2 callout shows you how to apply the httpOnly flag
on session cookie in Express.
Example 3-2. Adding httpOnly attribute on session cookie
var session = require("express-session");
app.use(session({
secret: "s3Cur3",
key: "sessionId",
cookie: {
httpOnly: true,
secure: true
}
}));
XSS in Node Modules
The XSS vulnerability is frequently found in node modules. Let’s go
over a couple of examples of this vulnerability, and the way module
authors mitigated it:
• The Express framework serve-index is an extremely popular
and widely used module for serving pages that contain direc‐
tory listings for a given path. The serve-index (< 1.6.3) was vul‐
nerable to the stored XSS attack via remote user-supplied
malicious file or directory names. As a prompt fix, the author
HTML-encoded the file and directory names before using it in
the output.
• In another case, the mustache (< 2.2.1) and handlebars (<
4.0.0) template libraries had XSS vulnerabilities when an app
developer added user inputs inside HTML attributes without
using quotes around it. For example, the template <a
28
| Chapter 3: Cross-Site Scripting
with foo as {foo
:
test.com
onload=alert(1)}, was translated to <a href=test.com
onload=alert(1)/>. As a fix, the authors added an additional
encoding for the equals sign.
href={{foo}}/>
Similarly, here are other node modules that had XSS vulnerabilities:
mapbox.js, backbone, dojo, and marked.
Conclusion
The XSS is a prevalent attack. Although output-encoding the
untrusted user data before using it is the best way to protect against
XSS, it is challenging to encode data correctly for all contexts due to
different encoding rules for each context. Also, testing and detecting
XSS flaws is difficult, especially for cases such as stored XSS, for
which feedback is not immediate, or when the contents are gener‐
ated at the client side by using JavaScript libraries. Fortunately, the
wider support for the CSP header in modern browsers provides a
very simple-to-use and robust mechanism for application develop‐
ers to protect against XSS.
Additional Resources
HTML5 Security Cheatsheet
A collection of XSS attack vectors that make use of HTML5 fea‐
tures
Conclusion
|
29
CHAPTER 4
Insecure Direct Object References
The insecure direct object references vulnerability allows an attacker
to steal other users’ data of a specific type. Beyond just the data in a
database, an attacker can exploit it to access restricted files or direc‐
tories on the server. According to the Open Web Application Secu‐
rity Project (OWASP), an insecure direct object references
vulnerability is commonplace and easy to exploit.
So, what makes an application vulnerable to this attack? As the
name indicates, it is caused when a direct reference (such as a data‐
base ID or a filename) to a restricted object is exposed to users as
part of the URL parameter. In addition, the application fails to verify
whether the user is authorized to access the requested object for
which the reference is present in the request URL.
Let’s examine some specific attack vectors and ways to mitigate
them.
Attack Mechanics
In this exploit, attackers manipulate the identifier in the request
URL to access other records in the database that do not belong to
them. For example, consider this URL on a vulnerable application:
www.example.com/profile/3032
In this URL, 3032 is an ID of a profile record in the database.
Because it is exposed in the URL and predictable, an attacker can
31
simply change it to some other value and access other users’ restric‐
ted profiles.
Here is an another example of using a URL to retrieve a filesystem
resource:
www.example.com/reports?name=feb2016report.pdf
The name parameter in this URL specifies the exact filename to
retrieve. Attackers can modify this predictable parameter to access
reports for other months, for which they have neither paid nor are
subscribers.
Preventing Insecure Direct Object References
The following subsections present some concrete measures to pre‐
vent insecure direct object references.
Avoid Exposing Direct Object References
Instead of requiring the references in the URL, use the information
already present in the user’s session on the server to locate the
resources to serve. For example, in the www.example.com/profile/
3032 route implementation we just reviewed, if the users are allowed
to access only their profile, we can use the logged-in user’s ID in the
session to locate it, thus eliminating the need for passing id as a
URL parameter.
Use an Indirect Reference Map
If it is not possible to avoid exposing the references to objects in the
URL, as explained earlier, the indirect reference map technique is
helpful. The idea behind it is to substitute the sensitive direct inter‐
nal reference in URL parameters or form fields with a random value
that is difficult to predict (such as a GUID) or specific only to the
logged-in user (such as sequential values per user or session).
The indirect reference map stores mapping between an object’s
internal reference and corresponding indirect reference exposed in
the URL. For Node applications, the indirect reference map can sim‐
ply be a JavaScript object stored in a user’s session. When rendering
an object in a view template, use the mapped indirect references
instead of direct references. In the other direction, when a user
request contains an indirect reference, refer to the indirect reference
32
|
Chapter 4: Insecure Direct Object References
map to look up and retrieve the corresponding internal reference
and use it for further processing.
The OWASP Enterprise Security API project provides a good refer‐
ence implementation for indirect reference map in Java to get a bet‐
ter idea of how to implement it.
Check User Access at the Data-Object Level
This defense mechanism addresses the primary issue causing the
vulnerability: an insufficient or missing access check.
First, never rely only on client-side validations for access control,
because an attacker can easily circumvent those.
Second, while implementing the server-side access controls, it is rel‐
atively obvious to add authorization checks at the functionality or
route level. Unfortunately, many applications stop at that level. A
common issue that causes this vulnerability is missing access checks
at the data or object level to protect against tampered IDs in URL
parameters. You need to enforce the data layer access controls by
verifying that the current user owns or is allowed to access the
requested data.
Directory Traversal
The directory traversal is a special case of the insecure direct object
references vulnerability, in which an attacker manipulates the path
exposed in a URL to access directories and files outside of the web
root folder and possibly compromise the entire web server.
To exploit it, an attacker uses absolute or relative path traversal char‐
acters such as /, ../, or its encoded versions %2f, %2e%2e%2f, or %2e
%2e/ to manipulate the path.
Protecting Against Directory Traversal
To prevent the directory traversal attack, you need to take measures
at the system-configuration as well as the application-code level:
• At the system-configuration level, configure the root directory
and access control list to confine the application user’s access
and restrict access to files and directories outside of the web
root.
Directory Traversal
|
33
• In the application code, validate user inputs for presence of path
traversal and corresponding encoded characters.
Directory Traversal Vulnerability in Node Modules
The directory traversal vulnerability is commonplace in Node.js
applications. The Node Security Project found and reported direc‐
tory traversal vulnerabilities in multiple modules. Here are some
examples:
• The classic exploit using ../ in user input is possible in fancyserver (<0.1.4) and nhouston Node modules.
• Versions prior to 0.2.5 of the st module are vulnerable to leak‐
ing sensitive data from the server with encoded path traversal
characters, such as /%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e
%2e/etc/passwd.
• A slight variation of directory traversal was spotted in the
send(< 0.8.4) Node module, in which an attacker could access
files in a similarly named restricted directory. For example, a
configuration like static(_dirname + '/public') would
allow access to the _dirname + '/public-restricted' direc‐
tory.
Conclusion
The internal references to objects such as database IDs or filenames
are often exposed publicly as part of the URL parameters. The tech‐
niques to prevent insecure direct object references primarily focus
on protecting these references as sensitive data and checking access
control at the data-object level.
Additional Resources
• “Testing for Insecure Direct Object References”
• “Meteor Security in the Wild”
34
|
Chapter 4: Insecure Direct Object References
CHAPTER 5
Security Misconfiguration
Security misconfiguration risk encompasses a broad category of
configuration issues originated at any level of an application stack,
including OS, network, application server, database, framework, or
the application code itself.
Depending on the misconfiguration, the impact of vulnerability
could range from an attacker identifying the platform and frame‐
works used to build the app to gaining a full control of the enter‐
prise network and the application server.
As published in the HPE Cyber Risk Report 2016, Figure 5-1 shows
the most commonly found vulnerabilities by analyzing more than
7,000 web applications. Incidentally, most of these vulnerabilities
could be attributed to the security misconfiguration issues.
35
Figure 5-1. Top 10 most commonly occurring vulnerabilities from the
data sample used for the HPE Cyber Risk Report 2016
This chapter covers related attack mechanics and ways to protect
against them in Node applications.
Attack Mechanics
There are a variety of attack mechanics to exploit security misconfi‐
guration, each affecting distinct attack surfaces. The subsections that
follow present some examples.
Attacks from Browsers
An attacker can exploit the following security misconfigurations by
using a browser or a browser-equivalent tool:
• Any server configuration information leaked to the browser
aids an attacker to build the security profile for the application
and exploit known and zero-day vulnerabilities applicable to
that platform. Such information can include stack traces,
platform-specific error messages, or response headers such as
x-powered-by and server.
• If an application is not configured correctly to set the HttpOnly
flag on cookies, or the Content-Security-Policy (CSP) header
on the response (to prevent the execution of scripts from
untrusted domains), an attacker can use a cross-site scripting
(XSS) attack to steal the session cookies.
36
|
Chapter 5: Security Misconfiguration
• If any default accounts or development-only APIs are not dis‐
abled, an attacker can use them to access restricted features or
data.
• On a shared computer, an attacker can collect sensitive data
belonging to earlier users via form autocomplete or browser
caching features.
Attacks on the Network
Following are the attacks that target an active user’s network con‐
nection:
• An attacker can steal session cookies from an unencrypted net‐
work if it is not set with the secure flag.
• If the HTTP Strict Transport Security (HSTS) response header
is missing, an attacker can execute a man-in-the-middle (MitM)
attack, even on the TLS-enabled site, by piggybacking on an
HTTP request for a static resource.
• An attacker can eavesdrop on the HTTPS connection that uses
a weak TLS/SSL protocol version or cipher.
Attacks on Application Servers
Here are examples of attacks that target misconfigured application
servers:
• An attacker can exploit publicly known vulnerabilities in older
versions of frameworks, Node.js, and npm modules.
• If the application server is configured to run as root, an attacker
can run malicious scripts (by exploiting eval family functions,
for example), start new child processes on the server, or kill the
server process causing denial-of-service (DoS) attack.
• Similarly, as a result of assigning filesystem permissions beyond
necessary, an attacker can gain access to protected files and
directories; edit or delete application source code, log files, and
user data; or create and run binary files.
Attack Mechanics
|
37
Preventing Security Misconfiguration
The security misconfiguration mitigations fall under the realm of
responsibilities of both app developers and system administrators.
Let’s go over some specific measures to prevent related issues.
Apply the Principle of Least Privileges
If a Node application server is running as a root user, on a successful
exploit of a vulnerability in application code or its dependencies, an
attacker could gain complete control of the system. Hence, per the
principle of least privilege, run services as an ordinary user with the
least amount of privileges necessary.
Node developers might find it necessary to run apps as root to listen
to user requests coming on privileged TCP/IP ports below 1024,
such as port 80 or 443. However, you can achieve the same in a safer
way by using these options:
• Start the server as root and then downgrade to an ordinary user
using process.setuid() after the connection to port 80 is estab‐
lished.
• Use a separate proxy server such Nginx or Apache that listens
on the desired privileged port, such as 80, and then redirect
requests to a Node server running on a nonprivileged port.
• Set up iptables to bind to the desired port (80 or 443) and for‐
ward requests to the application running on any nonprivileged
port such as 3000. Note that such a forwarding rule is not per‐
sistent, so you might need to set it in the system startup script to
apply it each time the server starts.
In addition, to limit the impact of directory traversal issues (for
example, vulnerabilities in older versions of these Node modules:
geddy, fancy-server, and nhouston), restrict the directory access
permissions to the least necessary level.
Disable Any Development-Specific Features and Default
Users
Remove any default test users and disable any features implemented
for ease of development or debugging purposes before deploying the
app to production. As an example, a related vulnerability was found
38
|
Chapter 5: Security Misconfiguration
in the i18n-node-angular (<1.4.0) Node module. This module sup‐
ported a REST API endpoint intended to be used only in the devel‐
opment environment. However, it was not set to disable when
running the application in production. As a result, by using this
endpoint, an attacker could fill up the server with requests culminat‐
ing in successful DoS or content injection attacks.
Apply Security Headers on Response
Security headers are very effective and the easiest way to improve
the security of an application. The Express and Koa apps can use the
helmet middleware to apply these headers, as demonstrated here:
var express = require('express');
var helmet = require('helmet');
var app = express();
app.use(helmet());
Following are the security configuration-related options supported
by the helmet:
contectSecurityPolicy
Allows specifying the whitelist of safe domains that browsers
can load scripts from, effectively preventing XSS and frame
attacks.
frameguard
Sets the X-Frame-Options response header that prevents open‐
ing a page in a frame or iframe to protect against clickjacking
and cross-frame scripting.
hidePoweredBy
Removes the X-Powered-By header that reveals the framework
name (such as Express) used for building the app, thus helping
attackers to identify possible unpatched or zero-day vulnerabili‐
ties.
HTTP Strict Transport Security
Adds the Strict-Transport-Security response header. It
enforces the network communication over secure HTTPS pro‐
tocol.
Preventing Security Misconfiguration |
39
ieNoOpen
Specifically for the Internet Explorer browser, this sets the XDownload-Options header to noopen, which prevents down‐
loading and executing untrusted HTML in your site’s context.
noCache
Sets the Cache-Control header that disables client-side caching,
preventing the sensitive data leakage.
noSniff
Sets the X-Content-Type-Options header to nosniff. Thus, it
requires browsers to verify the expected MIME type set explic‐
itly in the content-type response header instead of inferring it.
For example, noSniff would prevent browsers from loading con‐
tents of a .css file as a stylesheet unless the MIME type matches
to “text/css”.
xssFilter
Adds X-XSS-Protection HTTP header that provides basic pro‐
tection against XSS.
Out of the preceding options, you need to set the contentSecurityPolicy and noCache explicitly. The other packages are added by
default just by running app.use(helmet()).
Protect Cookies by Using the httpOnly and Secure Flags
The session cookies are the primary target for attackers looking to
hijack a user session. Setting the httpOnly flag prevents JavaScript
code on the page from accessing the cookies, thus protecting against
an XSS attack (for more details, go to Chapter 3).
On an HTTPS-enabled site, setting the secure flag to true transfers
cookies only on a secure HTTPS connection, preventing an attacker
from intercepting the cookies from the network.
For Express apps behind a proxy, to use the secure flag, you need to
set the trust proxy property, as demonstrated in Example 5-1.
40
|
Chapter 5: Security Misconfiguration
Example 5-1. Setting the trust proxy property
var app = express()
app.set('trust proxy', 1) // Trust first proxy
app.use(session({
secret: 'SECRET_HERE',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
For more details, see the documentation on setting an Express appli‐
cation behind the proxy.
Mozilla Observatory—A Server Configuration Assessment
Tool
The Observatory project by Mozilla is a free assess‐
ment tool designed to help developers, system admin‐
istrators, and security professionals configure their
sites safely and securely. This tool can scan and score
your site and provide helpful recommendations to
configure it securely.
Use Application Logs Effectively for Incident Detection
and Response
The longer it takes for an organization to detect and contain a data
breach, the more costly it becomes to resolve it. Application logs are
key to an organization’s ability to promptly detect and accurately
respond to any security incidents.
For complete traceability, while logging a transaction or an event,
consider adding details such as what happened, when it happened,
who tried to do it, and the result of the operation. Ensure that you
mask or prevent adding any sensitive data to the logs.
The Node modules such as bunyan or morgan provide excellent log‐
ging features.
Preventing Security Misconfiguration
|
41
Keep Versions of Node.js and npm Modules Up to Date
Keep watch on vulnerabilities published and patched in Node.js,
frameworks such as Express, and npm modules that your project
uses. Chapter 9 further explains this topic along with a list of useful
tools and resources.
Securely Deploy the SSL/TLS
You should use strong certificate signature algorithms such as
SHA2, stronger 2,048-bit RSA or 256-bit Elliptic Curve Digital Sig‐
nature Algorithm (ECDSA) private keys, and secure protocols such
as TLS v1.2. For more details, see the “SSL/TLS Deployment Best
Practices” guide.
Conclusion
The Node platform has its unique advantages and challenges related
to security misconfiguration issues.
The Node frameworks such as Connect or Express are designed to
be minimalistic; that is, unlike some other platforms, a typical Node
server does not come preconfigured with dozens of default features
and switches that developers need to be aware of and tune. Instead,
Node developers assemble the server with only the middleware that
they actually use. This reduces the risk of unknown or default fea‐
tures slipping into production.
On the other hand, the community-driven npm modules add an
extra responsibility on Node developers to vet these dependencies
before using them, and to constantly keep watch on any vulnerabili‐
ties published, keep them up to date, and test apps against them. In
this chapter, we covered possible measures and best practices to pre‐
vent this and other security misconfiguration issues for the Node
apps.
Additional Resources
“Exploiting CORS Misconfiguration” explains a very common
CORS headers misconfiguration which an attacker can exploit to
steal private API keys.
42
|
Chapter 5: Security Misconfiguration
CHAPTER 6
Sensitive Data Exposure
The frequent news about data-breach incidents in recent years made
it evident that sensitive information such as personal, financial, or
health records is a lucrative target for attackers due to the associated
monetary gains. A single data-breach incident could cause cata‐
strophic losses for an organization. As published in a research study,
Figure 6-1 shows the cost of data breaches in the year 2015 for 64
benchmarked companies, sorted by the number of breached
records.
Figure 6-1. Total cost of data breach, based on monetary values and
number of records stolen
Often, costs of sensitive data exposure span over incident investiga‐
tion efforts, notifications to users, fines, lawsuits, settlements, as well
as indirect costs such as loss of employee productivity, brand reputa‐
43
tion, and user’s trust. In some cases, they result in businesses
actually closing.
This Open Web Application Security Project (OWASP) risk primar‐
ily focuses on mitigating attacks that target web applications in an
attempt to steal sensitive data. In this chapter, we go over related
attack mechanics and ways to protect Node applications against it.
Attack Mechanics
There are three sources from which an attacker can steal the sensi‐
tive data managed by applications: a client such as a browser, a net‐
work, or application servers. Let’s review each.
Stealing Sensitive Data from a Client
Stealing sensitive data from the user’s browser has relatively less
impact because it affects a single user per incident, but it still consti‐
tutes a prevalent attack vector. Let’s look at some specific mecha‐
nisms.
Cross-site scripting
The most common client-specific attack mechanism is cross-site
scripting (XSS). With XSS, an attacker injects malicious JavaScript
code into a web page, primarily to steal a user’s session identifiers
and then use that information to impersonate the user for the pur‐
poses of gaining access to sensitive data.
The next attack mechanisms require both the victim user and
attacker to access the target application from the same browser on a
shared or public computer.
Browser autocomplete feature
With autocomplete, as a user types values in text fields, the browser
provides suggested values based on values submitted earlier. Thus,
on a shared computer, an attacker can access any sensitive field val‐
ues submitted by earlier users.
Browser caching mechanism
Browsers cache server responses to improve the page performance.
However, on a shared computer, browser caching can be a source of
44
| Chapter 6: Sensitive Data Exposure
sensitive data exposure because an attacker could access the data
fetched by the earlier users.
Sensitive Data Exposure from Mobile Clients
Besides web browsers, it is a common requirement for Node appli‐
cations to support mobile clients. These mobile devices are vulnera‐
ble to sensitive data exposure, similar to typical web browsers. The
following features on both iOS and Android OS are key sources for
data leakage:
• URL caching
• Keyboard press caching
• Copy/paste buffer caching
• Application backgrounding
• Logging
• HTML5 data storage
• Browser cookie objects
• Analytics data sent to third parties
For more information on unintended data leakage from mobile cli‐
ents, refer to the OWASP Mobile Top 10 project.
Stealing Sensitive Data from a Network
In this mechanism, attackers use the network sniffing tools or mal‐
ware installed on the victim user’s browser to secretly intercept the
communication between the browser and a server to capture any
sensitive data being transmitted.
In a more intrusive form, referred to as the man-in-the-middle
(MitM) attack, an attacker impersonates each endpoint of a network
connection to the satisfaction of the legitimate other end. Then, an
attacker can secretly read or alter the communication between the
two parties, who each believe that they are directly communicating
with the other. For example, an attacker within the range of an
unencrypted WiFi network can insert himself as a man in the mid‐
dle, clandestinely stealing the sensitive data.
Attack Mechanics
|
45
Stealing Sensitive Data at Rest
This is the most common mechanism behind large-scale data
breaches. In these attacks, the malicious user gains direct access to
the application’s database server containing the sensitive data. Also,
if application developers are not careful about what goes in the log
files, those log files, and any backups, can be a source of sensitive
data at rest and, consequently, a target for an attacker. The same
applies for any reports generated by an application containing the
sensitive data.
Protecting Against Sensitive Data Exposure
Now let’s go over the measures to protect against sensitive data
exposure on the client, network, and server.
Securing Data in the Browser
There are several ways to prevent an attacker from stealing data
from user’s browser. Let’s take a look at them.
Prevent XSS
By taking measures such as setting the HTTPOnly and Secure
attributes on cookies, input sanitization, and adding Content Secu‐
rity Policy (CSP) headers, we can successfully protect against XSS
attack. Chapter 3 covers these options in more details.
Disable autocomplete on sensitive form fields
To prevent exposure of the sensitive data via the browser’s autocom‐
plete suggestions, apply the autocomplete="off" attribute at the
form level or field level, as shown in Example 6-1 and Example 6-2,
respectively.
Example 6-1. Disabling autocomplete on an entire form
<form method="post" action="/saveForm" autocomplete="off">
...
</form>
46
|
Chapter 6: Sensitive Data Exposure
Example 6-2. Disabling autocomplete on selective form fields
containing sensitive data
<form method="post" action="/saveForm">
<input type="text" id="name" name="Name">
<input type="text" id="cc" name="Credit Card" autocomplete="off">
...
</form>
Turning off autocomplete has two effects: the browser stops showing
suggestions based on previously submitted data when a user types in
the form field; and it doesn’t cache field values in the session history.
Thus, after submitting a form, if a user clicks on the browser Back
button, she returns to the form page, but the fields marked as auto
complete="off" remain empty.
Prevent browsers from caching sensitive information
To prevent the browser from caching the server response containing
sensitive data, set the Cache-Control response header to no-store,
as shown in Example 6-3.
Example 6-3. Preventing browser from caching responses by setting the
Cache-Control response header
res.set('Cache-Control', 'no-store');
...
res.send(sensitiveData);
You also can use the Node helmet module to set the Cache-Control
header to no-store.
With this header, the browser avoids saving response content in the
cache; instead, it makes a new request to the server each time a user
visits the page or endpoint.
Another most-preferred option to prevent caching is to communi‐
cate via an HTTPS connection. On a secure SSL/TLS network,
browsers assume that all traffic is sensitive; hence, when a user
closes the browser, any content cached in memory is cleaned auto‐
matically. In this case, to enforce static public resources to cache for
a long term, set the Cache-Control response header to public.
Protecting Against Sensitive Data Exposure
|
47
Securing Data in Transit
The most effective way to protect the data in transit is to use a
secure HTTPS connection to communicate the data between the
server and client. The SSL/TLS protocol used for HTTPS connec‐
tions includes authenticating one or both parties by using a mutu‐
ally trusted certificate authority; when correctly configured, this
eliminates the possibility of a MitM attack.
The Node.js core https module makes it possible to build a server to
support HTTPS with just a few lines of code, as shown in
Example 6-4.
Example 6-4. Creating an HTTPS Node server
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('app/keys/key.pem'),
cert: fs.readFileSync('app/keys/cert.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
Another commonly used option to establish secure HTTPS connec‐
tion with clients is to use web servers such as NGINX or Apache as a
reverse proxy in front of the Node.js application server. This option
is often preferred for performance reasons. In this case, the secure
SSL/TLS connection from the client terminates at the reverse proxy
server. The Node.js server sends and receives content in unencryp‐
ted format to and from the reverse proxy server.
Although the aforementioned options make establishing HTTPS
connection easy, the related configuration is complex and prone to
misconfiguration. The following are related best practices:
• Obtain certificates from a reliable certificate authority (CA). As
an example, Let’s Encrypt is a free, automated, and open CA
that is run for the public’s benefit.
48
|
Chapter 6: Sensitive Data Exposure
• Use strong certificate signature algorithms such as SHA2
instead of the weak but still commonly used SHA1 hashing
function.
• Use 2,048-bit RSA or 256-bit Elliptic Curve Digital Signature
Algorithm (ECDSA) private keys for all your servers. Keys of
this strength should stay secure for a considerable amount of
time.
• Password-protect the private keys and restrict their access to the
smallest possible group of employees.
• Renew certificates every year, always with new private keys.
• Of the five protocols in the SSL/TLS family—SSL v2, SSL v3,
TLS v1.0, TLS v1.1, and TLS v1.2—use TLS v1.2. It’s superior
and offers important features that are unavailable in earlier pro‐
tocol versions.
• Transmit all page content via SSL/TLS, including static resour‐
ces such as JavaScript files, images, and CSS files. An attacker
can piggyback on even a single unprotected resource and hijack
the entire user session.
• Set the secure flag on all cookies.
• Finally, the most important safety feature with respect to TLS
configuration issues is to set the HTTP Strict Transport Security
(HSTS) response header. Its benefit is if a site is first accessed
via HTTPS and the response contains the HSTS header, the
browser ensures to continue using HTTPS connections for all
subsequent requests to the site, automatically converting any
HTTP requests to HTTPS.
Secure SSL/TLS Configuration: Tools and Resources
Securely configuring the SSL/TLS requires careful attention and
knowledge of related pitfalls. Fortunately, we have a couple of great
free tools at our disposal:
• The Mozilla SSL Configuration Generator is a web-based tool
that generates an SSL configuration for multiple servers,
including Apache, Nginx, Lighttpd, HAProxy, and AWS ELB.
• The Qualys SSL Lab (Figure 6-2) provides a free online SSL
assessment tool that allows system administrators to easily vali‐
Protecting Against Sensitive Data Exposure
|
49
date and know about any vulnerabilities in their SSL configura‐
tion.
Figure 6-2. Qualys SSL Server Test results
In addition, there is a clear and concise guide to best practices for
deploying an SSL/TLS network.
Securing Data at Rest
In this section, we go over the ways to secure data residing on the
server side.
Do not store unnecessary sensitive data
The more sensitive data an organization stores on its system, the
larger the attack surface as well as the liability if a data breach hap‐
pens. Hence, store only essential sensitive data and discard it as soon
as you are no longer required to maintain it.
Do not put sensitive data in system logs
Ensure that no sensitive data is in system logs. If required for tracea‐
bility, mask part of the sensitive data with characters such as # or *
so that is not usable if the logs end up in wrong hands.
Protect against database injection attacks
The SQL injection is commonly used to steal data from the applica‐
tion database. Some easy changes to the application code, such as
using prepared statements and not using user inputs to construct
50
|
Chapter 6: Sensitive Data Exposure
dynamic SQL statements at runtime, can prevent this attack. Chap‐
ter 1 covers database injection attacks in more detail.
Encrypt sensitive data
You should encrypt passwords, Social Security numbers, and credit
card numbers so that they are not directly usable to an attacker.
Encrypting sensitive data before inserting it into the database adds a
layer of protection in the event of a breach.
Depending on the nature of the sensitive data, you can choose oneway or two-way hashing algorithms.
The data, such as user passwords, is only used for comparison with
user-supplied values. It is never needed to present back to the user
in a clear-text format. Consequently, a one-way hashing function
such as bcrypt is often suitable for such data. The bcrypt incorpo‐
rates a salt, making it difficult to crack by using dictionary attacks.
Also, the iteration count required for encrypting/decrypting the data
is configurable and can be increased. Thus, it can slow down
attempts of a brute-force attack even with ever-increasing computa‐
tion power. See Chapter 2 for additional details on using bcrypt for
password storage.
On the other hand, for data that needs to be decrypted in a clear-text
format to present to a user, the Node crypto module provides essen‐
tial cryptographic functionality. The crypto module includes a set of
wrappers for OpenSSL’s hash, HMAC, cipher, decipher, sign, and
verify functions.
The Example 6-5 illustrates how to use the crypto module to imple‐
ment encrypt and decrypt functions.
Example 6-5. The encrypt/decrypt functions using the Node crypto
module
// Include crypto module
var crypto = require("crypto");
//Set keys config object
var config = {
cryptoAlgo: "aes192", // or other secure encryption algo here
cryptoKey: "a_secure_key_for_crypto_here"
};
Protecting Against Sensitive Data Exposure
|
51
// Helper methods to encrypt / decrypt
var encrypt = function(toEncrypt) {
var cipher =
crypto.createCipher(config.cryptoAlgo, config.cryptoKey);
return cipher.update(toEncrypt, "utf8", "hex") +
cipher.final("hex");
};
var decrypt = function(toDecrypt) {
var decipher =
crypto.createDecipher(config.cryptoAlgo, config.cryptoKey);
return decipher.update(toDecrypt, "hex", "utf8") +
decipher.final("utf8");
};
Example 6-5 uses aes192 as a cryptoAlgo value. However, you will
need to choose this cipher algorithm from those supported by the
OpenSSL on your platform. For recent OpenSSL releases, you can
get the list of available algorithms by running the command
openssl list-cipher-algorithms.
After you have the encrypt/decrypt functionality implemented, you
can use it to encrypt/decrypt sensitive data, as shown in
Example 6-6.
Example 6-6. Encrypting/decrypting sensitive data
// Encrypt values before saving in database
user.ssn = encrypt(ssn);
user.dob = encrypt(dob);
// Decrypt values to show on view
user.ssn = decrypt(user.ssn);
user.dob = decrypt(user.dob);
Secure encryption keys
Protecting the encryptions keys from theft by an external attacker or
misuse from an insider is an important part of keeping sensitive data
safe. Fortunately, some robust solutions are available to address this
challenge:
Hardware security modules (HSMs)
A HSM is a tamper-resistant device with a dedicated crypto
processor specifically designed to secure cryptographic keys and
provision encryption, decryption, authentication, and digital
signing services to applications. Using HSM can remove the
52
|
Chapter 6: Sensitive Data Exposure
burden on applications for maintaining keys or writing and
maintaining the encryption/decryption logic.
An HSM could be USB-attached to provide encryption services
to on-premises applications, or it could be connected to the net‐
work so that it could be used by applications in virtual and
cloud environments.
Hosting encryption and key management tool
A tool such as Vault, an open source project, can safely store
and tightly control access to enterprise secrets such as tokens,
passwords, certificates, API keys, and other secrets required by
the applications. It also presents a unified API for applications
to access multiple backends, including HSMs, AWS IAM, SQL
databases, raw key/value, and so on, containing the enterprise
secrets.
A benefit of such a tool is that developers do not need to deal
with encryption keys or writing code to encrypt/decrypt data.
Instead, they just call the Vault API to encrypt/decrypt data as
needed. Thus, the responsibility of encryption is on Vault and
the security team managing it.
To use with Node apps, multiple Node clients for vault are avail‐
able on npm.
Using cloud-based key management services
Various cloud-based secret management and encryption serv‐
ices such as Azure Key Vault or AWS Key Management Service
can safeguard cryptographic keys and secrets used by applica‐
tions deployed on the cloud. These cloud-based key manage‐
ment services internally use HSMs to protect the security of the
keys.
Conclusion
A data breach could result from various incidents, such as an acci‐
dental or malicious act of an insider within an organization or even
a business partner; lost or stolen assets such as smartphones, tablets,
laptops, external hard drives, USB keys; or as a result of an external
attack on the web applications that handle sensitive data.
Conclusion
|
53
In this chapter, we focused on web-application-specific attacks to
steal sensitive data from a user’s browser, network, or application
servers, and ways to protect against them.
54
|
Chapter 6: Sensitive Data Exposure
CHAPTER 7
Missing Function-Level
Access Control
The missing function-level access control vulnerability refers to the
flaws in the authorization logic. By exploiting it, an attacker, who
could be an existing user of the application, is able to escalate privi‐
leges and access restricted functionalities. For example, the restric‐
ted administrator-level features are often a target for this attack.
Attack Mechanics
Attackers exploit this vulnerability primarily by manipulating URLs.
For example, consider these URLs provided by an application:
example.com/account/view
example.com/account/remove
Although both require authenticated users, let’s assume that
the /remove endpoint should be available only to the admin user. If
an unauthenticated or an authenticated nonadmin user could access
the /remove endpoint, this is a missing function-level access control
flaw.
55
Preventing Missing Function-Level Access
Control
The first step toward preventing access-control issues is to define
the access-control policy for the application. The access-control pol‐
icy describes security requirements for each functionality so that
developers can implement it in a consistent manner.
In its simplest form, the Node applications can implement access
control by using middleware in a route configuration. The middle‐
ware function acts as a filter that allows you to modify the request
object, write to response, invoke next middleware, or end the
request without continuing the chain. We can add a number of mid‐
dleware functions, each implementing a single specific accesscontrol rule before invoking the route handler. For example, you can
secure the account/remove endpoint discussed earlier by chaining
isLoggedIn and isAdmin middleware, as illustrated in Example 7-1.
Example 7-1. Using middleware to add access control
app.post("/remove", isLoggedIn, isAdmin,
accountHandler.removeAccount);
Example 7-2 shows isAdmin middleware implementation. You can
implement the isLoggedIn middleware in the same way.
Example 7-2. The isAdmin middleware
module.exports = function isAdmin (req, res, next) {
if (req.session.userId) {
userDAO.getUserById(req.session.userId, function(err, user) {
// Check isAdmin property on user object
if(user && user.isAdmin) {
// Allow invoking the action for an admin user
next();
} else {
// Redirect a nonadmin user to the login route
return res.redirect("/login");
}
});
} else {
// Redirect an unauthenticated user to the login route
return res.redirect("/login");
56
|
Chapter 7: Missing Function-Level Access Control
}
};
The access-control logic must be implemented on the
server side. You should not rely on client-side checks
or hiding links from the application UI as mechanisms
to restrict the access, because an attacker can easily
bypass them.
Scaling the Authorization Logic
Chaining the access-control middleware functions before the route
handler serves simple authorization needs very well. However, this
approach faces limitations when it comes to a large-scale application
that has many routes or needs to accommodate frequently changing
client demands that result into adding new roles, or adjusting exist‐
ing authorization rules. Thus, while designing the system, you need
to be mindful of following possible issues as the application grows:
Maintainability
The tight coupling of the access-control middleware with routes
makes it difficult to locate and change access-control logic that
is scattered across the route definitions.
Security
An ideal security proposition should deny access to all users by
default and whitelist roles that are allowed. However, in the
approach we covered earlier, if a developer misses chaining an
access-control middleware before any route, by default it is not
secure, because users without the required rights get access to it.
This mistake is more likely to happen as routes in an application
increase significantly.
To address these concerns, it is recommended that you extract and
encapsulate authorization rules in a central access-control logic such
as an access control list (ACL). To get an idea of how to implement
the ACL-based authentication, let’s review its implementation in two
popular Node.js frameworks.
In sails.js, the ACL configuration is defined in a policies file. The
config/policies.js file contains a mapping between a controller’s
actions and corresponding access control rules. For example, the
code in Example 7-3 illustrates how to externalize the access control
rules for AccountController’s view and remove actions:
Preventing Missing Function-Level Access Control
|
57
Example 7-3. The ACL declaration in the sails.js framework
// In config/policies.js
// ...
AccountController: {
// Apply the `false` policy globally to the Controller to prevent
// access to all AccountController's actions, making it secure
// by default
'*': false,
// Apply the 'isLoggedIn' policy for the view action
// This overrides the `false` value set as global policy
view : 'isLoggedIn',
// Apply the `isLoggedIn` AND `isAdmin` policies to `remove` action
remove: ['isLoggedIn', 'isAdmin']
}
// ...
Similarly, the policies file contains ACLs for all controllers in the
application. In the policies file shown in Example 7-3, the isLogge
dIn and isAdmin values refer to simple Express.js middleware func‐
tions, as shown earlier in Example 7-2. By convention, these
middleware should be coded in policies/isLoggedIn.js and policies/
isAdmin.js files, respectively.
For more details on writing policies with sails.js, see the website’s
policies section.
The LoopBack framework also implements the ACL-based authori‐
zation. It provides a command-line tool to make generation of ACL
entries easy. However, unlike sails.js, LoopBack maps ACL rules to
data models and its CRUD methods instead of controller actions.
LoopBack provides a mechanism to resolve any conflicts in ACLs by
applying permission precedence (For example, the DENY rule takes
precedence over ALLOW and DEFAULT), and specificity prece‐
dence (a more specific rule prevails over a more general rule), thus
providing a flexible system that allows specifying the access control
at a granular level.
See the LoopBack documentation for more details on its data accesscontrol mechanism.
58
|
Chapter 7: Missing Function-Level Access Control
Conclusion
The key to protecting against missing function-level access control
is to apply access control rules to all functions or routes. When
implementing them, you should keep these rules easy to add,
update, and audit.
Additionally, when implementing the access check in middleware,
ensure that the logic is restrictive in granting access, denying access
by default, and grant permission only when specific conditions pass.
Additional Resources
Here are some additional resources on this topic:
• OWASP “Access Control Cheat Sheet”
• “Harnessing the Magic of Hapi scopes”
• ESAPI Access Control API
Conclusion
|
59
CHAPTER 8
Cross-Site Request Forgery
The cross-site request forgery (CSRF) vulnerability enables an
attacker to exploit a victim user’s authenticated session and trick the
user into performing any state-changing operation that the victim
user is authorized to perform. For example, this attack can result in
changing a user password, updating account details, or making pur‐
chases without the user knowing.
Attack Mechanics
As the name indicates, an attacker lures a user to visit a cross-site;
that is, an externally hosted malicious web page, which typically
contains a forged HTML form with hidden fields matching the web
page rendered by the target web application.
For example, Example 8-1 shows a malicious web page targeting an
admin user of a vulnerable application. It entices the user to click a
button, which triggers the form submission. Because the server
response goes in a hidden iframe, the victim user doesn’t get any
clue about the transaction.
Example 8-1. A malicious web page crafted for a CSRF attack
<form method="POST" action="http://example.com/admin/changeRole"
target="iframeHidden">
<h1> You are about to win a brand new iPhone!</h1>
<h2> Click on the win button to claim it...</h2>
<input type="hidden" name="accountId" value="67887"/>
<input type="hidden" name="newRoleId" value="1"/>
61
<input type="submit" value="Win !!!"/>
</form>
<iframe name="iframeHidden" width="1" height="1"/>
In this example, when an admin user—who is authorized to escalate
the role of other users—clicks on the button, the user with the ID
specified in the accountId hidden field gets a privileged role with
roleId 1.
This attack is possible because along with the request, the user’s
browser automatically sends any cookies initially set by the target
web application, including the session cookie. Because a session
cookie is the only way by which the server identifies the user, its
presence in the request makes the server treat it as a legitimate
request, and the attacker succeeds in executing the transaction on
the server.
Even though an attacker must jump through a few hoops (require a
user to have an active session, deliver, and lure users to click an
external page link), as per the Open Web Application Security
Project (OWASP), the CSRF attack has a common prevalence. Even
multistep transactions fail to block the CSRF. As long as an attacker
can predict or deduce the payload for each step of the transaction,
CSRF is possible.
Protecting Against CSRF
Let’s examine two ways to prevent the CSRF attack.
Support Only AJAX APIs and Disable Cross-Origin
Resource Sharing (CORS)
Most of the modern web applications expose JSON-based REST
endpoints that frontend code invokes by using AJAX calls. If your
application does not support plain HTML form submissions as well
as cross-domain requests, the Express framework protects it against
the CSRF attack out of the box. Let’s look more closely to learn how
it is possible.
In a CSRF attack, because the malicious web page is hosted on the
attacker’s external server, the first preventive measure is to disable
supporting cross-domain requests. The Express framework being
minimal, it doesn’t enable CORS support by default. Thus, the appli‐
cation is safe against any AJAX calls coming from the malicious
62
|
Chapter 8: Cross-Site Request Forgery
page externally hosted by an attacker without you having to do any
configuration for it.
However, to conduct a CSRF attack, malicious actors often use a
plain HTML form instead of an AJAX call, and the browser’s sameorigin policy doesn’t block plain HTML form submissions to cross
domain. It restricts only calls generated from JavaScript code such as
AJAX calls. Here, the minimal default configuration of Express
works in your favor again. In Express, you must explicitly enable a
parser for each request content type. If your application needs to
support only JSON-based APIs to be invoked via AJAX calls, you
can safely omit enabling a body parser for URL-encoded content
types, as illustrated in Example 8-2.
Example 8-2. Server configuration to support only a JSON body parser
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
// Parse JSON contents from request body using bodyParser.json()
// Not using bodyParser.urlencoded() ignores urlencoded
// requests such as plain HTML form submissions
app.use(bodyParser.json());
Note that not invoking bodyParser.urlencoded() in Example 8-2
doesn’t block the cross-domain HTML form submission from mali‐
cious pages. However, it prevents parsing the request body contents
by setting field values in the request object to undefined. So, mali‐
cious data posted by an attacker is filtered. Application code should
validate inputs and reject requests with undefined values.
Thus, if application requirements allow us to drop support for plain
HTML form submissions and CORS, all you need to do it is skip
enabling the URL-encoded body parser to protect against CSRF.
Use an Anti-CSRF Token
If an application needs to support cross-domain requests or plain
HTML forms, you can protect it against CSRF by using the antiCSRF token. The csurf module provides the req.csrfToken()
method to generate a token with a random value. For all actions that
mutate the application state, add this token to a hidden form field or
query string. When this form is submitted back by the user, before
processing the request, the csurf module verifies whether the token
Protecting Against CSRF
|
63
in the request matches the one it had set initially. If the token is
missing or mismatches, it rejects the request. Thus, there is no way
for an attacker to create forged HTML forms, because the token
value generated by the middleware is unpredictable.
Example 8-3 shows the server-side code that renders a form with an
anti-CSRF token in a hidden form field and verifies it when the
form is posted back.
Example 8-3. Enabling CSRF protection
var
var
var
var
express = require('express');
session = require('express-session');
csrf = require('csurf');
bodyParser = require('body-parser');
// Create express app
var app = express();
// Enable populating req.body object with POST request parameters
app.use(bodyParser.urlencoded({
extended: false
}));
// Use a memoryStore based session on server
app.use(session({
secret: 'secret key here',
resave: false,
saveUninitialized: true
}));
// Enable CSRF Protection
// Either a cookie-parser or session middleware must be
// initialized before to this line
app.use(csrf());
// Optional CSRF error handler to show a custom error message
// when the token in a request is missing or mismatches
app.use(function (err, req, res, next) {
// When the CSRF token validation fails, an error is thrown with
// an error code 'EBADCSRFTOKEN'
if (err.code !== 'EBADCSRFTOKEN') return next(err);
// Display custom error message
res.status(403);
res.send('form tampered');
});
app.get('/form', function(req, res) {
// Pass the csrfToken to the view
64
|
Chapter 8: Cross-Site Request Forgery
res.render('send', { csrfToken: req.csrfToken() });
});
app.post('/changeRole', function(req, res) {
res.send('data is being processed');
});
Next, inside the view template, set the csrfToken value in a hidden
input field named _csrf, as seen in Example 8-4.
Example 8-4. Including the CSRF token in a view template
<form action="/changeRole" method="POST">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<label>Account ID:</label>
<input type="text" name="accountId"/>
<label>New Role: </label>
<input type="text" name="newRoleId"/>
<button type="submit">Submit</button>
</form>
Express csurf Configuration Pitfalls
Although the Express csurf module makes it extremely easy to
incorporate anti-CSRF tokens, you need to be aware of a few related
pitfalls.
First, the csurf module skips validating the token for HTTP GET,
OPTIONS, and HEAD requests. Hence, the application code
shouldn’t use these methods to mutate states on the server.
The second pitfall comes from using method-override modules.
This is commonly used to enable HTTP verbs such as PUT or
DELETE in places where the client doesn’t support it. For example,
the HTML form in Example 8-5 overrides a POST method to DELETE
by using a query parameter _method=DELETE on the action URL.
Example 8-5. Passing the override value as a query string
<form method="POST" action="/resource?_method=DELETE">
<button type="submit">Delete resource</button>
</form>
As a result, when the form is submitted, the method-override
module on the server converts the actual request method POST to
DELETE.
Protecting Against CSRF
|
65
As an alternative to using method_override, you should always
prefer using AJAX calls when possible. If the application can’t avoid
using it, as shown in Example 8-6, ensure that the server uses the
method_overide module before the csurf module. Otherwise, an
attacker could use the HTTP GET method for a CSRF request that
is ignored by the csurf module, and then have the method_over
ride module to convert the request to any desired state-mutating
methods.
Example 8-6. Correct order of including method_override and
csurf
var methodOverride = require('method-override')
var csrf = require('csurf');
...
// Include methodOverride middleware before csurf
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(csurf());
Conclusion
Thanks to the minimal default feature set of the Express framework,
protection against CSRF is relatively effortless for those applications
that do not need to support plain HTML form submissions and
CORS. All you need to do is not enable middleware such as body‐
parser for URL-encoding and CORS. However, if an application
needs to support these features, including an anti-CSRF token using
the csurf module is an effective way to block the attack.
Additional Resources
The OWASP CSRF prevention cheat sheet
66
| Chapter 8: Cross-Site Request Forgery
CHAPTER 9
Using Components with Known
Vulnerabilities
This Open Web Application Security Project (OWASP) Top 10 risk
highlights the threats from using external dependencies in an appli‐
cation. For Node applications, these dependencies range from
Node.js binary itself to frameworks such as Express and thousands
of modules available on npm. Security vulnerabilities in these
dependencies directly affect the application. As a result, it is a criti‐
cal task for Node developers to stay aware of any vulnerabilities dis‐
covered in an application’s external dependencies and diligently
keep the application safe against it.
Attack Mechanics
Let’s review how attackers can exploit these vulnerabilities.
Exploit Publicly Known Vulnerabilities
As a first step of an attack, malicious actors typically perform appli‐
cation foot-printing to discover the platform, frameworks, and
server on which the application is built. When this is known, an
attacker can look up publicly known common vulnerabilities and
exposures (CVEs) published for that platform and apply them
toward the target application. In other cases, attackers automate
exploits based on CVEs and spray them across the internet to catch
the prey.
67
Exploiting publicly known vulnerabilities often yields success for
attackers. As per a research study done by Verizon, 99 percent of
close to 80,000 security incidents in the year 2014 involved exploit‐
ing vulnerabilities for which CVEs were published at least a year
earlier, some published as far back as 1999. Thus, even decade-old
CVEs often prove useful to attackers and are actively exploited in
the wild.
For Node apps, the applicable CVEs include Node.js vulnerabilities,
as well as advisories found in Node modules.
Publishing Malicious Modules to npm
The npm code of conduct has an explicit policy against malware,
stating that “a package which is designed to maliciously exploit or
damage computer systems is not allowed.” However, in the spirit of
being an open and community-driven environment, npm doesn’t
impose a formal audit or an approval process before a module can
be published.
Although the following are not very common, here are some possi‐
ble ways by which an attacker could sneak in a malicious module on
victim user’s application:
• By publishing a module that provides some useful features, but
performs malicious activities under the hood.
• By naming a malicious module with a spelling that is very close
to an existing popular module, targeting npm users who fatfinger and mistype the actual module name.
• By compromising popular module author’s npm account (by
using brute-force attack, or exploiting HTTP Bearer Token Vul‐
nerability in npm <v2.15.1 or <v3.8.3, for example), and pub‐
lishing a new version of a module with malicious code.
To implement a malicious package, an attacker could take advantage
of the npm-script hooks in the package.json that allow executing a
script or command on the server. As an alternate option, an attacker
could have the module send files or data to an external location,
delete files, or install additional malicious files by using Node core
APIs such as filesystem, child process, and HTTP. The possibilities
of what an attacker could do after the malicious package is installed
68
| Chapter 9: Using Components with Known Vulnerabilities
are limited only by permissions of the user the application is run‐
ning as.
Exploit Unpublished Zero-Day Vulnerabilities
This mechanism involves an attacker using unpublished vulnerabili‐
ties in Node.js or Node modules for which no patch or release with
fixes exists.
Protecting Against Unsecured External
Components
Because it is unlikely that you can avoid external dependencies of a
Node application, let’s go over some ways to protect against related
threats.
Vet npm Modules Before Using Them
As vulnerabilities in any npm module that an application uses
directly affect the security of the application, you need to be diligent
in reviewing the module for potential security issues before instal‐
ling and using it.
As a heuristic approach, you should prefer using a module that is
written by a reputable author, actively maintained by the commu‐
nity, has a healthy amount of download stats, and comes with an
extensive test suite.
If these general guidelines fail to establish a good confidence level,
but you have good reasons to use a module, you should undertake
to review the source code of the module. The following are a few
ideas about what to look for during the code review.
Scripts in package.json
Before installing a module, check the scripts section in the pack‐
age.json, either from the source code or by running the npm view
<pkgname> scripts command.
Ensure that hooks such as preinstall, install, postinstall, preuninstall,
uninstall, or postuninstall do not invoke any unexpected command
or script. Verify that no commands have a “sudo” prefix.
Protecting Against Unsecured External Components
|
69
Usage of Node core APIs
Inside the module code under review, check whether Node core
APIs and bindings are being used that could potentially help an
attacker conducting malicious activities such as deleting, updating,
or adding files, sending data over the network to a remote location,
executing commands on the server, and so on. The commonly used
core modules for such activities are fs, dns, http, https, net, os,
child_process, and zlib. If you notice these modules being used in
the source code, verify its necessity for the module’s intended func‐
tionality and the way it is used.
Blacklisting Node.js Core Modules and Bindings by Using
N|Solid
N|Solid is a Node.js runtime that has been enhanced to
address the needs of the enterprise. It provides a way to
blacklist Node core modules and bindings that could
be harmful. It allows specifying severity levels to
ignore, warn, throw an error, or exit an application
when a function on a blacklisted core module or bind‐
ing is used by the application code or external mod‐
ules, thus helping to detect possibly harmful code.
There is an excellent article with more details on black‐
listing.
Keep Versions of Node.js, and npm Modules Up to Date
Keep watch on vulnerabilities found and patched in Node.js runtime
and npm modules that your project uses. Here are some useful tools
and resources to keep track of these security updates:
Node.js
• The nodejs-sec forum and Node.js blog
• The Node wiki with information on breaking changes in relea‐
ses
npm modules
• The Express security updates page
• The Node Security Project advisories page
70
|
Chapter 9: Using Components with Known Vulnerabilities
• Tools: fortunately, the daunting task of keeping track of new
versions and vulnerabilities discovered in numerous direct and
indirect project dependencies is much easier with the help of
excellent tools at the disposal of node developers:
1. Tools for checking for outdated, incorrect, and unused
dependencies: npm-check, david-dm, and npm outdated
command
2. Tools for scanning external modules used in a project for
known vulnerabilities: snyk, nsp, requireSafe, and retire.js
Maintaining Suites
You can incorporate most of these tools in the build
script and run them as part of the continuous integra‐
tion.
In addition, maintaining a good test suite for applica‐
tion code is extremely useful to ensure functionalities
work as intended after upgrading the dependencies.
Apply the Principle of Least Privilege
The privileges of a user that an application is running under are
important in limiting the extent of the damage an unsecured exter‐
nal dependency could cause. Therefore, limit these access privileges
to an absolute minimum.
Conclusion
The majority of code in a Node application comprises external
dependencies. Because this is inevitable, the security of the applica‐
tion is only as strong as the weakest link in its dependencies.
In this chapter, we covered strategies to vet such external dependen‐
cies, along with tools and resources useful to stay on top of everemerging vulnerabilities and security updates for these
dependencies.
Conclusion
|
71
Additional Resources
Here are some additional articles and resources on this topic:
• Security and NPM packages An infographics on vulnerabilities
in npm packages.
• A Malicious Module on npm Covers some helpful tips when
inspecting npm packages.
• Yarn—Security as a core value Explains the package integrity
check run by yarn to ensure that you always get the same bits
for a specified version of a package.
• NodeSource certified modules A NodeSource-hosted commer‐
cial registry of actively monitored modules with no known
security vulnerabilities.
72
|
Chapter 9: Using Components with Known Vulnerabilities
CHAPTER 10
Unvalidated Redirects and
Forwards
Even though this is the least significant of the Open Web Applica‐
tion Security Project (OWASP) Top 10 risks, unvalidated redirects
and forwards are dangerous, pervasive, and a favorite tool for phish‐
ers.
According to a 2016 research study conducted by Verizon on phish‐
ing, 30 percent of recipients open phishing messages (median time
for the first user to open a message: 1 minute, 40 seconds), and 12
percent click malicious attachments or links (median time for the
first click: 3 minutes, 45 seconds). This reveals the success rate and
execution speed of phishing attacks.
An attacker can abuse this vulnerability to install malware or trick
victim users into disclosing passwords or other sensitive informa‐
tion.
Attack Mechanics
Let’s begin by having a clear understanding of what redirects and
forwards mean:
Redirects
Typically, a redirect involves a server sending an HTTP 302
response to the browser with a different destination URL. On
receiving it, the browser makes a second request to the supplied
destination URL.
73
Forwards
Forwards are transparent to the browser. The server internally
transfers a request to a different resource on the server.
Both redirects and forwards can be unsafe if you’re using usersupplied input such as a query parameter to decide the destination
without validations. Let’s explore the attack mechanics involving
redirects and forwards.
Scenario 1: Redirect to an External Web Page
It is a relatively common use case to redirect users to an entirely dif‐
ferent website upon clicking a link. For example, consider the fol‐
lowing link:
http://trustedbank.com/offers?redirect=
http://affiliatesite.com
When the user clicks this link and the request reaches the server (see
Example 10-1), the application code simply takes the target URL
from the redirect query parameter and performs a redirect by
sending an HTTP 302 response to the browser.
Example 10-1. Unsafe URL redirect implementation
var app = express();
app.get("/offers", function(req, res, next) {
// Some logic to track user activity here ...
// Once done, take the user to the destination site
return res.redirect(req.query.redirect);
});
Such redirects, also referred to as open redirects, often provide a
way for attackers to facilitate phishing attacks. An attacker can craft
a link by pointing the redirect query parameter to an externally
hosted malicious page, such as the following URL:
http://trustedbanksite.com/offers?redirect=http://evilsite.com
To make it difficult to detect, an attacker could encode the target
URL value. In the following URL, the redirect query parameter is
equivalent to the encoded value for http://evilsite.com:
http://trustedbank.com/offers?redirect=
http:%2F%2%65%76%69%6C%73%69%74%65%2E%63%6F%6D
As the domain name in the URL http://trustedbanksite.com is
familiar and trustworthy, users are likely to click it without carefully
74
|
Chapter 10: Unvalidated Redirects and Forwards
inspecting the redirect path. After a victim is on the malicious page,
an attacker has multiple options to cause damage, including down‐
loading and installing malware on the user’s computer.
Scenario 2: Redirect to a Fake Login Page
Let’s consider a very common authentication requirement for appli‐
cations; that is, when a user clicks a link after the session times-out,
the user is required to reauthenticate first before being able to access
the requested page.
For such an implementation, the server needs to remember the page
the user wanted to access and present it upon successful authentica‐
tion. A possible way to implement it is to include a path to the
requested page in a query parameter of the login page request URL
and the login form submit request.
After the user logs in successfully, the login service on the server
uses the path specified in the query parameter to redirect the user.
Although this is a convenience feature for users and relatively simple
to implement on the server, attackers can exploit such redirects by
substituting the query parameter value to any external malicious
web page, as shown in the following URL:
http://trustedsite.com/login?onautheticate=http://evilsite.com
An attacker can then publish such a specially crafted URL by using a
phishing email or on social media.
At the redirect destination, an attacker might host a fake login page
exactly matching the one on the trustedsite.com in the earlier
example. After successful login on the trustedsite.com, users are
then redirected to the forged login page. It is highly likely that users
would again enter their credentials on this page, thinking something
went wrong the first time and never notice the change in the
domain name in the address bar. Thus, attackers could abuse such
redirects to harvest user credentials.
Attack Mechanics
|
75
Incidents Related to Unvalidated Redirects and
Forwards
As a real-world example, an open redirect attack was mounted
against eBay.com. On an external domain, the attacker hosted a
fake login page that looked exactly the same as the legitimate login
page from eBay.com, and redirected users to it. The attack resulted
in leaking numerous eBay.com user credentials.
As another Node.js specific example, the popular serve-static
node module (< 1.7.2) was vulnerable to the open redirect attack.
With this vulnerability, an attacker could append a malicious redi‐
rect destination to a legitimate URL. For example, if a user visits a
URL http//truestedsite.com//www.evilsite.com/%2e%2e, a redirect
happens to URL part after //, that is to www.evilsite.com/%2e%2e
site.</p>
Protecting Against Unvalidated Redirects and
Forwards
The good news is that unvalidated redirects and forwards are easy to
detect and fix. Let’s go over some simple but effective measures to
prevent this vulnerability.
Avoid Using User Inputs to Calculate the Destination
URLs
When possible, do not derive the redirect path from a user-supplied
input such as a URL parameter. Instead, either have the redirect
path identified on the server based on the user’s state or use session
or cookies to store it.
For example, the second attack scenario we just covered abused the
query parameter onautheticate to redirect a user to a malicious
location when a user reauthenticates after session timeout. A safer
implementation would be to set the redirect URL in a cookie instead
of a query parameter before redirecting the user to the login page.
You can make it further tamper proof by setting it in a signed cookie
as:
76
|
Chapter 10: Unvalidated Redirects and Forwards
res.cookie('redirectOnLogin',
req.path,{signed: true});
Next, as shown in the Example 10-2, on a successful credential
check, redirect the user to the path in the cookie if it exists; other‐
wise, take the user to the default home page of the application.
Example 10-2. Using a signed cookie to store redirect path
handleLoginPost = function (req, res, next) {
userDAO.validateLogin(req.body.userName, req.body.password,
function(err, user) {
if (err) {
return res.render("login", {
loginError: 'Invalid username or password'
});
}
var redirectPath = req.signedCookies.redirectOnLogin;
if (redirectPath) {
res.clearCookie('redirectOnLogin');
return res.render(redirectPath, user)
}
return res.render('/home', user); //default landing page
});
};
Use Mapped Redirect Values
If you must include a redirect path in the URL query parameter, you
should use a mapping value; for example, a GUID or a random
value instead of setting an actual URL in it. On the server, maintain
a map in a session or database with the mapping values and their
corresponding URLs.
With this fix, as part of processing the redirect, the application code
on the server can now derive the target URL from the mapped value
received from the query parameter, and if no mapped value exists, it
prevents the redirection. Thus, foiling an attacker’s attempt to add a
malicious location as a redirect target.
Validate and Sanitize Redirect URLs
If you can’t avoid setting the redirect URL in the query parameter or
use indirectly mapped values, validate and sanitize the user-supplied
URL before using it for redirect or forward. Here are some related
tips:
Protecting Against Unvalidated Redirects and Forwards
|
77
• Use the whitelist approach to ensure that the user-supplied
value is legitimate. As illustrated in Example 10-3, as part of
whitelist approach, verify the destination URL against a known
list of valid URLs.
Example 10-3. Whitelist approach to validate a redirect URL
var app = express();
// Whitelist of valid redirect URLs
var validRedirectURLs =
['www.affiliate1.com',
'www.affiliate2.com',
'www.affiliate3.com'];
app.get("/offers", function(req, res, next) {
var redirectTo = req.query.redirect;
if(validRedirectURLs.includes(redirectTo)) {
return res.redirect(redirectTo);
} else {
return res.status(500).send({ error: 'Invalid URL' });
}
});
• For internal forwards, allow only relative URLs, and reject abso‐
lute URLs beginning with http, https, or //.
• As part of unvalidated forwards, an attacker typically crafts a
URL that passes the access control check and then forwards the
attacker to a restricted functionality for which the attacker has
no direct access. Therefore, before forwarding to a different des‐
tination resource, verify that the current user is authorized to
access it. See Chapter 7 on missing function-level access control
for more information on related attack mechanics.
Check the Referrer Header to Prevent Misuse of the
Redirect Page from Outside the Application
Attackers often use the redirect mechanism on a vulnerable applica‐
tion as a medium to bring victim users to a malicious site. Such spe‐
cially crafted URLs are often published on external social media
sites for phishing attacks.
A possible way for an application to protect against such phishing
attacks is to check the referrer header in the incoming request. On
78
|
Chapter 10: Unvalidated Redirects and Forwards
Express-based applications, you can retrieve this header from the
request object by using req.headers.referer. For all internal for‐
wards within an application, the referrer header must be the same as
the hostname of the application. Thus, you can safely reject any
redirect requests that contain an unknown external domain in the
referrer header.
Phishing Attacks by Exploiting target="_blank”
The target="_blank" attribute is very commonly used on links to
open the target page in a new window. The less-known fact about
using this attribute is that the target page opened in a new tab gains
access to the source window’s content via the window.opener
object. So what is possible with it? Regardless of the security origin,
the newly opened page can change the window.opener.location to
redirect the user to a malicious page. For example, the source page
can be replaced with a malicious phishing page designed to look
like the real opener page, asking for login credentials:
window.opener.location =
'https://phishing-site.com/fake-gmail-login-page.html';
Users likely wouldn’t become suspicious about it, because they trust
the page that is already opened.
To prevent it, add the rel="noopener noreferrer" attribute to the
link. This ensures that window.opener is set to null.
Any page that opens a new window by using window.open(); is
also vulnerable to the same exploit, so always reset the “opener”
property after opening a new window, as demonstrated in
Example 10-4.
Example 10-4. Resetting the opener property after opening a new
window
var newWindow = window.open();
newWindow.opener = null;
Protecting Against Unvalidated Redirects and Forwards
|
79
Conclusion
Unvalidated redirects and forwards mainly aid phishing. Even the
diligent users who are cautious before clicking links in email or
social media could fall prey to this attack because the domain name
appearing on mouseover of the link is of a legitimate and trusted
site. Similarly, anti-phishing browser filters fail to detect or warn of
such redirects.
The best way to protect users of your application against such
attacks is to not use a user-supplied query parameter for deciding
the destination page. However, it is argued that the benefits of redi‐
rects and forwards outweigh the little practical risk they pose. For
example, Google categorizes open redirects as a nonqualifying sub‐
mission for its Vulnerability Reward Program.
Nevertheless, in this chapter, we covered some simple techniques to
implement such redirects and forwards in a safe way, if you choose
to use them in Node applications.
80
|
Chapter 10: Unvalidated Redirects and Forwards
About the Author
Chetan Karande is a full stack web developer, security researcher,
writer, and speaker at developer conferences.
He works as a principal software engineer at DTCC, with a focus on
building fast, maintainable, and secure user interfaces.
He is a contributor to multiple open source projects. He is a member
of the Open Web Application Project (OWASP) organization and a
project leader for the OWASP NodeGoat project, an open source
learning platform for Node.js security.
Download