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 &amp;, &lt;, &gt;, &quot;, &#x27;, and &#x2F;, 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.