Categories
Security

Best Security Practices for Node.js Apps

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices to avoid creating security holes in our Node.js apps.

noAssert Flag in Buffer

We shouldn’t set noAsserr to true to skip validation of the offset .

Otherwise, we may be able to set the offset to be beyond the end of the Buffer .

Dynamic Code in exec

We shouldn’t dynamic code in exec .

This way, we avoid attackers being able to inject commands they want to run on our server.

For instance, we should write:

child_process.exec('ls', (err, data) => {
  console.log(data);
});

But we shouldn’t write:

const path = "user input";
child_process.exec(`ls -l ${path}`, (err, data) => {
  console.log(data);
});

We shouldn’t have any dynamic expression in the first argument.

This way, there;’s no way that attackers can run commands on our server.

To run commands with arguments, we can do it with execFile :

const child_process = require('child_process');

const path = "./foo/bar"
child_process.execFile('/bin/ls', ['-l', path], (err, result) => {
  console.log(result)
});

We only inject the arguments that we want to add in the place we want it rather than letting anyone enter anything.

Likewise, we can also use spawn :

const child_process = require('child_process');

const path = "."
const ls = child_process.spawn('/bin/ls', ['-l', path])
ls.stdout.on('data', (data) => {
  console.log(data.toString());
});

It’s the same as execFile .

Users can’t run subcommand in the shell.

However, running things like /bin/find may still let users take over our system.

No Mustache Escape

We shouldn’t set object.escapeMarkup to false .

This can let some template engines to disable escaping in HTML entities.

Therefore, this value may allow attackers to do cross-site scripting on our app.

We shouldn’t let users enter something like:

<body onload=alert('foo')>

end run it directly.

Don’t use eval

eval is definitely a bad function that we shouldn’t use.

It lets us run expressions from a string.

Therefore, it leads to attackers being able to run code that they want as well.

Allowing anyone to run code they want if definitely not good.

Use the CSRF Middleware to Prevent CSRF Attacks

Cross-site request fogery our CSRF is an attack where attackers can run requests on a site without authorization.

This can happen when a user goes from a legit site to a malicious site but still has credentials for the legit site.

We should the csrf middleware to prevent CSRF attacks.

When we use it, we shouldn’t use the methodOverride middleware method in our Express app to over the method of the requests with the post key or with the x-http-method-override .

The method override functionality can bypass the CSRF token check provided by the csrf middleware.

Therefore, we shouldn’t call express.methodOverride in our Express app.

However, we should note that with the same-origin policy for Ajax requests, using the x-http-method-override header will be much harder.

No Dynamic File Paths when using fs Methods

We shouldn’t use dynamic file paths when using fs methods.

This way, attackers can’t access different paths in our system which we don’t want people to access.

We don’t want people to traverse paths with ../ or ./ .

No Non-Literal Regex

A regex shouldn’t be dynamic. This way, no one can change them.

Some regex may hang our Node apps.

They include regex with lots of groups and also checking against long strings.

No Nonliteral require

We shouldn’t have dynamic arguments in our require function calls.

This way, attackers can’t pass in their own string to look up modules and use them.

require looks up modules by their path and also in the node_modules folder.

No Insecure Comparisons

We shouldn’t use insecure comparisons that check input sequentially.

They include == , != , !== , and === .

These can lead to timing attacks.

Strings are checked one character at a time.

So these operators would iterate through each character and compare each of them.

Attackers can use the timing of comparisons to work backward to see how many characters match, making cracking passwords easier.

Conclusion

In addition to getting apps to work, we should also make sure that they’re secure.

We got to think about security when comparing string and evaluating dynamic expressions.

Categories
Security

More Security Issues to Consider When Building Apps

When building apps, security should always be a consideration since attackers will take advantage of any flaw to attack our systems.

In this article, we’ll look at some common security issues that we all have to consider when building our apps.

Cross-Site Scripting

Cross-site scripting (XSS) is always a popular attack where attackers inject JavaScript code into our apps so that they can run their malicious code with our app.

Most libraries and frameworks that are used to build apps have sanitization measures to preventing XSS by cleaning up malicious code by escaping characters that may be in code so that they can’t run.

The lesson is that we can’t trust users to always input things that aren’t malicious code, so we should escape all inputs that can potentially be code.

Cross-Site Request Forgery (CSRF)

CSRF is an attack that lets attackers make unauthorized commands from a trusted origin.

This can happen if we’re logged into a site and then go into a malicious site that makes the request to the original site but with malicious code added to the request.

The attack can be made by embedding code into images or JavaScript code for example, in order for attackers to make requests that they want.

They’re typically used for state-changing requests rather than the theft of data since attackers have no way to see the response of the forged request.

State changing requests include things like transferring money, changing emails and passwords, etc.

If the victim has an admin account, then the whole app can be compromised.

To prevent this from happening, we should add a CSRF token, which is generated on the server-side, and then transmitted to client, so that the client can use it to make requests.

If the token is missing or invalid, then the request fails. This way, we know that the request is made from a trusted source.

Insecure Deserialization

The serialization of data is preparing for data to be transmitted and deserialization is when data is decoded after transmission.

Attackers can exploit the deserialization of serialized objects to run malicious code on the system that’s receiving data.

To avoid this, we should prevent hostile code from being run by escaping deserialized objects.

We can also enforce type constraints so that executable code can’t be deserialized into their original form.

Because if we let executable code run after they’re deserialized then all kinds of attacks can happen.

Any systems that deserializes object should be run in a low privilege environment is possible to prevent deserialized code from wreaking havoc on our systems even if it has security flaws.

Using Components With Known Vulnerabilities

We should be aware of the security vulnerabilities that are present in frameworks and libraries so that we can avoid using versions with those vulnerabilities.

Most packages aren’t updated after they’re first installed, so we should make sure that they’re updated so that we use versions that have vulnerabilities fixed.

APIs can also have vulnerabilities like any other piece of software so we should be aware of them so that we can avoid using ones with vulnerabilities that we already have known about.

Logging and Monitoring

Logging and monitoring are important since we have to check whether any malicious activities have occurred in our systems.

To do that, we have to log activities so we can watch them. To watch them, we have to log them.

Any critical tasks have to have logging and an audit trail so we know who has changed what activity.

This way, we can detect any attacks early on.

Denial of Service

Denial of Service (DoS) attacks are always an issue. Attackers can easily use botnets to bring down our systems.

So we should make sure that we have rate limiting by origin so that we can blacklist IPs that are making requests that are overwhelming our systems.

Better yet, we can put our code behind a CDN like Cloudflare so that we can let them detect any suspicious activity and protect us against any of these kinds of attacks.

Conclusion

Cross-site scripting and cross-site request forgery are all attacks that we can avoid easily with some precautions.

Deserialization should be secure so no malicious code can run after deserialization.

Finally, we need logging and monitor to detect suspicious activity. Also, we have to prevent denial of service attacks so that we keep our servers up.

Categories
Security

Security Issues to Consider When Building Apps

When building apps, security should always be a consideration since attackers will take advantage of any flaw to attack our systems.

In this article, we’ll look at some common security issues that we all have to consider when building our apps.

Code Injection

We should always consider this attack when building any systems and takes steps to prevent them.

A code injection attack is where attackers can run malicious code with our app by inputting their own code into our systems and run them through flaws within our app.

There’re many kinds of these attacks, including cross-site scripting, SQL injection and more.

Anywhere code can be run on the fly in our app, these attacks can happen. For instance, in JavaScript, there’s the eval function to run any code from a string.

If we let users add anything and run them with eval , then anyone can run anything in our app. This means that attackers can use to run anything to attack our systems.

Likewise, SQL injection attacks are ones that let attackers run SQL code to steal information or do other malicious things to them like corrupt them or delete them.

They work the same way. Our app lets users enter SQL commands and run them by passing them straight through to our back end app without any sanitization.

In either case, we can prevent this by doing some sanitization of our data to make sure that users can’t enter scripts and run time directly in our system.

Flaws With Authentication

Authentication can come with many flaws. When we create our own authentication system, we may be inadvertently exposing secret keys and passwords to other users.

To make our authentication systems more secure, we can add features like two-factor authentication to prevent brute-force password guessing attacks either directly or with something like rainbow tables.

Rainbow tables are pre-computed tables or reversing cryptographic hash functions by cracking password hashes.

Also, we can implement weak password checks when we let users set passwords so that we can prevent weak passwords from being entered into our systems.

Exposing Sensitive Data

We should be careful not to display sensitive data to users that aren’t supposed to see them.

Many systems have many kinds of users and we should make sure that only the right users see the right things.

This is something that we have to check with our tests, but we can automate them so that we don’t have to check all of them ourselves.

Displaying the wrong data to the wrong kinds of users can have bad consequences.

For instance, if someone sees that their compensation is lower than their colleagues, then the person that saw it will be mad.

Not only we have to worry about displaying data wrong users, but we also have to worry about other kinds of attacks that may expose information through social engineering like phishing attacks.

We have to be sure that we take measures against those by making sure that we alert our users to those issues.

Also, exposing data to clear text is also a problem. We should make sure we don’t have any secrets exposed as clear text.

Broken Authorization

Authorization is the privileges that users can have. We don’t want to mess this up by letting users do more than they supposed to.

This means that we have to check that users can do something and also can’t do something.

Fortunately, we can also automate that with end-to-end tests so that we don’t have to test each test case ourselves.

This way, we don’t have to think about every case. We just let the automated test do the testing for us.

We should restrict people by default so that we don’t give them too many privileges. Giving them too much power when they shouldn’t have it is a big risk that none of us want to bear.

Conclusion

Code injection is something that we want to prevent attackers from doing. We definitely don’t want to let users do whatever they want by secreting running their malicious code on our systems.

Also, we shouldn’t let users enter a weak password, and we may want to consider 2-factor authentication for extra security,

Finally, we should be careful with exposing data and giving extra privileges to users.

Categories
Security

More Security Best Practices for Backend Developers

As a back end developer, we have a big job. We make apps that manage people’s data and we can make code that does many things to them whether they’re good or not.

In this article, we’ll look at some security best practices to take note of when we’re developing back end apps.

Suspicious Action Throttling or Blocking

We should have a way to detect suspicious activities and throttle or block them to prevent various kinds of attacks.

We can use content delivery networks like Cloudflare to detect suspicious traffic and block them at their source.

Also, we shouldn’t let people do things that’ll overload our systems by slowing down downloads and doing other things that won’t let them do too many operations at once.

The number of requests that can be made should also be throttled so that clients can’t make too many requests at a time and overwhelm our systems.

If the activities that are overloading our systems are persistent, then blocking the source that commits those actions are required.

For instance, we can’t let users make 100 requests a second to get customer information.

Also, other suspicious activities include deleting or editing lots of data at once.

Use Anonymized Data for Development and Analysis

We should only use as much data as we need for the purpose that we’re using them for.

For instance, if we’re developing our app with production-quality data, then we take data from production and then anonymize them so that we can’t see all of our customers’ private data.

Likewise, we should anonymize data that are used for statistical analysis. This is very important since we don’t want to show everyone’s private data when we’re analyzing them internally.

If we don’t need to see it, then we shouldn’t see it.

Temporary File Storage

We should know where we’re storing a temporary file. If we’re using publically accessible directories that we should make sure that they’re made read-only for users other than the user the app is running it as.

We can also use the protected directory to store temporary files for our app.

Security for Shared Server Environment

We should be aware of the security implications for hosting our apps on a shared server environment.

It’s possible that other people can have access to our app’s files if we host it on a shared environment.

We should check if our app’s logs, temporary files, etc. are accessible to the outside.

Of course, it’s not good that we expose those files to other virtual servers that share the same host machine for example.

Therefore, we should think about all the things that are stored on those virtual servers including:

  • source code
  • data
  • temporary files
  • configuration files
  • version control directories
  • startup scripts
  • log files
  • crash dumps
  • private keys

and more.

They all have valuable information, so we should make sure that they’re secured from the outside.

We should make sure that our file permissions or set up correctly so that only authorized users can do things to the files that we want them to.

Application Monitoring

Monitoring is definitely important from the security perspective.

We have to check that nothing suspicious is showing on our app’s logs.

However, we should control how logs are displayed to different users.

We don’t want all users to have access to all log data, which may have private data in it.

Therefore, we should just have a subset of users that can access the logs in ways that they’re needed.

We may want to restrict log access by users or IP addresses. Those are usually good candidates to restrict access by.

Risks of APIs

APIs can do powerful things if we let them. Therefore, we should carefully restrict any powerful actions to users that have access to those APIs.

We got to have property authentication and authorization mechanisms so that we can make sure that only the people that we allow can access resources that they should access.

Otherwise, we give attackers a chance to do anything with our systems.

Usually, external APIs are secured with an API key or OAuth. They’re both good for securing our APIs.

For internal APIs, we may also secure them with session stores and a JSON web token to check if a user is allowed to do a given action.

Conclusion

We should make sure that we don’t expose too much data to the public.

APIs should be acknowledged for the power that they can bring. We should secure them.

Also, we should detect any suspicious activity and throttle or block them.

Categories
Security

Security Best Practices for Backend Developers

As a back end developer, we have a big job. We make apps that manage people’s data and we can make code that does many things to them whether they’re good or not.

In this article, we’ll look at some best practices to take note of when we’re developing back end apps.

Use Docker to Sandbox Apps

Docker is a great program for letting us run sandboxed containers. Therefore, we should use them to host and run our back end apps.

This way, all apps run in their own container so they can run in their own environment. This means that any binaries that we don’t trust can’t run outside the Docker container.

Also, any changes that we do to the Docker container are removed if we rebuild the Docker container, so if we or some attack mess up the container, we can just rebuild it and we put it back to a working state.

The libraries and dependencies of the different app won’t interfere with each other.

Also, multiple containers running on the same ost will have some level of access to other containers and the host itself.

Therefore, we should secure all hosts and run containers with a minimum set of capabilities.

We can do things like disable network access to a container if we need to.

Credentials Should Never Be Sent Unencrypted Over a Public Network

We should never send credentials over like SSH keys and passwords over a public network unencrypted.

There’re many ways to secure this. We can encrypt it, or send it through a password manager or something like that.

Never Store Secrets in Source Control

Secrets should never be checked into source control. It’s very easy to do that and then we have to reset them if they end up there.

Checking them in increases the risk of exposure to malicious people so we shouldn’t take that risk.

Even if we change the file permissions of the secret file, source control systems often unwrite those file permissions so that they may be made publically readable again.

Therefore, we should put our stuff in a file that won’t be checked into source control.

Whatever the name is, it should be ignored.

Login Throttling

In our back end app, we should limit the number of login attempts that users can make per a unit of time.

If the user fails to log in after a few tries, for instance, like 10 failed attempts, then we should lock them for a short period of time like 5 minutes.

This way, attackers can’t do brute force attacks easily.

User Password Storage

Passwords should never be stored in plain text. If we need to record passwords, then they should be stored in something that doesn’t show the password.

For instance, we can use a password manager to store our passwords.

We can use asymmetric encryption to store our passwords so that we can store them encrypted with a public key and then decrypt them back to plain text with a private key.

Passwords should be stored as a one way hash in most cases soo that they can’t be decrypted even if attackers log on to our database.

We encrypt each password with a secure hash and salt and just check against the hashed password if we need to check against it for authentication.

Audit Log

Our app got to keep an audit log to know who did what. The more critical the activity, the more logging that we need to do.

This way, we can check for suspicious activities if needed.

This way people can know what people did at what time and what’s the previous value before the change and the value after.

Each log entry should have the time, user who did the action, the action, and the old and new values.

It can be stored as a separate log file or part of the regular application log. Or it can be stored in the app database so that we can query the entries.

Conclusion

We need an audit log to check for suspicious activities. Also, we need to throttle login attempts so that attackers can’t use brute force attacks to log into in an authorized manner.

Also, we should never expose passwords to the public.

Finally, Docker is great for sandboxing apps and minimizing attack surface.