- Security Best Practices for Node.js
- Original article by Diogo Souza
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: Ashira97
- Proofread: PassionPenguin, samyu2000
Best practices for secure Programming in Node.js
With most systems now connected to the Internet, or at least interacting with it in some way, Internet companies have become more focused on cyber security.
Internet security has long drawn public attention only when news reports of breaches, hacking or the theft of data from companies, some of them giant, such as Google or LinkedIn.
It’s important to have security measures in place on systems outside the bizarre world of the giants that most of us don’t work for, but it’s often overlooked, and some developers forget to use them.
Installing, best practices, optimizing performance, testing, and focusing on metrics are all things you’ve probably done every day of your programming life. Unfortunately, however, they do not equate to secure programming best practices.
This is not alarmist. If your work is open source, Github’s protection mechanism will prompt you about dependency defects. The GitHub code platform is getting better at detecting vulnerabilities in thousands of dependent libraries in many different languages, which is also a cause for concern.
There are many security tools or platforms that can help developers address vulnerabilities in code and applications, and they make it easier for small and medium-sized businesses to implement security measures on their systems.
Regardless, whether you use these security platforms or not, you should understand and be aware of the security threats your applications may be exposed to, and use some simple and effective best practices to eliminate them, which is the subject of this article.
In fact, although we chose Node.js as our target, many of the concepts mentioned in this article can be applied to other platforms as well.
As a reference, OWASP (Open Web Application Security Project) guides us through the top 10 most critical security threats facing Web applications. The top 10 security threats come from the consensus of its users after analysis. Let’s take the Node project as an example to understand these security threats.
Injection attacks
One of the biggest security threats facing Web applications is the possibility that an attacker can send a destructive piece of SQL code to the back end and execute it.
This usually happens when the developer calls the data layer object directly and executes an important SQL statement like this:
// "id" comes from an unprocessed request parameter
db.query('select * from MyTable where id = ' + id);
.then((users) = > {
// Return user information in the response
});
Copy the code
If the developer does not validate the input parameters carried in the request, an attacker can pass not only an integer ID, but also an SQL command that can retrieve sensitive information or even delete it. (This is not to emphasize the importance of a sound backup strategy.)
Most programming languages and their respective object-relational mapping frameworks provide ways to avoid SQL injection. These methods typically parameterize the input parameters in the statement before directly connecting to the database to execute the statement. This parameterization process is accomplished by internal logic in the programming language executable library.
In this case, it is important to know the language/framework in depth and learn how it implements these features.
For example, if you use Sequelize to do this, a simple implementation would look like this:
const { QueryTypes } = require('sequelize');
await sequelize.query(
'select * from MyTable where id = :p1',
{
replacements: { p1: id }, // the id comes from the request parameter
type: QueryTypes.SELECT
}
);
Copy the code
Authentication failure
Authentication is an important part of a system’s functionality, especially if the framework or tools used fail to hide sensitive user information.
OWASP is very focused on authentication. Standards like OAuth (the second version is in common use, and the third version is in the works) are already out there, and it will continue to be updated and try to accommodate a wide variety of network environments.
It can be implemented in a variety of ways, depending on the business scenario of the project, or companies can customize the appropriate standard usage.
If your team or company is able to purchase a large, mature security service for your project, like Auth0, Amazon Cognito, or many other tools, then you’re halfway there with security best practices.
If you need to implement the OAuth2 protocol in your Node.js project, you can use one of the many open source and mature third-party libraries, such as the well-known Node-OAuth2-Server module, to avoid developing from scratch.
Whether the module or framework you choose is open source or paid for, you need to review the official documentation regularly. Also, when adding security modules for authentication, don’t use small, recently released open source projects (because the risk is too great for key modules in your application).
Disclosure of sensitive information
First and foremost, define what data is sensitive sensitive data. This definition varies from project to project. However, information such as credit cards or personal credentials must be sensitive information in any application.
How is such information transmitted into the system? Is it encrypted? No? Is it true?
Now that you’ve sorted out what’s “really important,” it’s time to decide what information to store and for how long.
You would be surprised to learn that many applications store useless sensitive information, or even obtain and store it without the user’s consent
Let’s take a look at the to-do list (also known as the “must do” list) :
- Encrypt sensitive information. MD5 is not powerful enough, your data should have a more powerful algorithm to encrypt, please use Scrypt for encryption.
- Remind the user how the application protects sensitive information. You should send regular emails with explanatory charts, push patterns of information when users log in, and of course, your terms of use should also state this.
- The application must be based on THE HTTPS protocol. Otherwise, Google may not be compatible with your application.
- If you can go a little further, use the HSTS protocol. HSTS is a protocol that helps applications resist man-in-the-middle attacks.
Set the HSTS in the Node application like this:
const hsts = require('hsts');
app.use(hsts({
maxAge: 15552000 // 180 days in seconds
}));
Copy the code
You can fine-tune the configuration items with different definitions, such as whether to include subdomain names:
app.use(hsts({
maxAge: 15552000.includeSubDomains: false
}));
Copy the code
Obviously, you need to download the HSTS package through NPM. For more information, please consult the official documentation.
(old) XML external entity injection
An attacker could inject those external entities into an application by defining them and exploiting vulnerabilities in the application’s parsing of XML files. Older versions of XML parsers are more vulnerable to this attack, which we call XXE external entity injection
If the parser configuration is not strong enough, an attacker can steal sensitive data and confidential information such as server passwords.
Let’s imagine an example where an XML-based service receives the following XML content as input:
<? The XML version = "1.0" encoding = "ISO - 8859-1"? > <id>1</id> <name>[email protected]</name> ... </xml>Copy the code
At first glance, this input is as common as any other. However, if the server does not recognize the attack and your application is deployed on such a server, the following might happen:
<? The XML version = "1.0" encoding = "ISO - 8859-1"? > <! DOCTYPE foo \[ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///c:/boot.ini" >\]> <id>1</id> <name>[email protected]</name> ... <foo>&xxe; </foo> </xml>Copy the code
This will return the contents of the boot.ini file in the response.
Another example is when dealing with file upload tasks. For example, if you restrict only the type of file you receive, you can also run malicious code because DOCX documents or SVG images are xmL-based and can be accepted by the system.
The easiest way to prevent this attack is to disable the parsing features of third-party libraries. The Node-libxml third-party library, for example, provides a number of ways to validate file types and protect your application against such attacks.
Access control failure
Whether this problem exists depends largely on how well the application tests user permissions from different fields or urls.
In other words, when you have restricted access to parts of your application, such as the administrator panel, that ordinary users don’t have access to, you have access vulnerabilities in your application.
This vulnerability is easy to fix and does not require any special fixes or solutions, so you can continue to use the code as it is. The only care is to implement permissions properly and have the appropriate testing process to ensure that permissions are covered to any new endpoints.
Node provides a set of libraries to assist with this, as well as a set of middleware to check the permissions of the current user. These libraries and middleware can also be implemented themselves.
Incorrect security configuration item
Early in a development project, developers need to define three main environments (development, test, and production) and typically initialize them with the same configuration.
This incorrect configuration can go unnoticed for a long time and can lead to serious attacks. Such applications are vulnerable because test and production environments are typically configured to provide only minimal protection.
When we talk about configuration, make sure we’re talking about all types of dependencies (database, external integration, API, gateway, and so on).
It is important to have well-defined configurations, which are often different and independent from each other. Typically, we store certificates or sensitive information separately from the project files to another remote site.
Your team’s technology selection also plays a role in this issue. For example, if you use Splunk or another logging tool, make sure you have a configuration that does not allow your development environment to print sensitive information or have a way to detect this behavior, because sensitive data in Splunk is more accessible than in a database that stores the same data.
It reminds me of the time a company’s key database passwords were leaked on the public GitHub repository because developers “accidentally” copied the company’s repository and went home to study. Note that I’m not saying the biggest mistake here is the developer himself.
Headache XSS
XSS is a headache. This is a well-known problem that developers often forget to tackle in the daily grind.
This problem is somewhat similar to SQL injection. There is an endpoint on your application that receives the request and returns the response. This is not much of a problem, however, when you put request parameters directly into the response without doing anything about it.
Classic cases are as follows:
app.get('/users'.(req, res) = > {
const user = db.getUserById(req.query.id);
if(! user) {return res.send('<span>Sorry, the user "' + req.query.product + '" was not found! '); }... });Copy the code
Guess what happens when the customer sends the following code as an ID parameter?
<script>alert(Uh la la, it's me! XSS!!)</script>
Copy the code
Now this is just a trivial tip, but we all know that attackers inject more JavaScript into your code.
The full option for Node addresses this problem by using new middleware. You can choose one, implement it, and the problem won’t bother you anymore.
Unsafe deserialization
This happens mostly when your application accepts a serialized object from an untrusted source, because such a serialized object could be tampered with by an attacker.
For example, imagine a situation where your Node application interacts with a client and returns a serialized object stored in a cookie as the user’s session for a long time after the client logs in, holding information such as the user’s ID and permissions
An attacker could tamper with such a cookie object and then grant himself administrator privileges.
What is happening here is similar to CSRF (Cross-site request forgery). Typically, the server generates a token (also known as a CSRF token) that is then sent to the client with each request and stored as hidden input for form submission.
The form carries this token each time it is submitted, and the server can detect that the token has changed or is missing. If this happens, the server will reject the request. An attacker will use JavaScript code to get tokens, but if your application does not support cross-domain resource sharing, the attacker will be unable to do anything and the threat will be eliminated.
Again, Node has a lot of great middleware to help you get rid of this threat, one of the most famous being CSURF. Using these middleware is quick and easy, and your application will be secure in a matter of time.
Inadequate logging and monitoring
The name shows the meaning. We’ve talked about Splunk before, but it’s just the tip of the iceberg.
A number of different tools integrate and interact with other logging tools to provide your application with sophisticated, information-based protection.
Information is critical to analyzing and detecting intrusions and application defect points. You can define a series of processes in your system based on predefined behaviors to do this detection.
Logs show what is happening inside an application. So when an error is detected, the corresponding information is displayed in the monitor.
We won’t discuss specific tools here, but there are many available.
conclusion
We have looked at the OWASP Top Ten Web security vulnerability. But clearly, we shouldn’t stop there.
This list is a primer for developers, especially new ones, to better understand what security threats look like on the web and how they can affect your app — even if you don’t believe someone is trying to hack your app.
Keep in mind that the bigger and more important your app is, the more vulnerable it is to security threats and the more potential attackers it has.
As an extended reading, I recommend you visit the OWASP web page, as well as its source code analysis tool page. Good luck!
If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.