Aug 15 2023
Jun 28 2023
Resource
BlogTopic
Edge ComputeDate
Jan 21, 2021One of the most effective ways to secure your users’ data is to ensure that they select a strong password. Unfortunately, putting the burden on your users rarely works:
“The bad news is that most users don’t pick strong passwords. This has been proven time and time again…Even worse, most users re-use these bad passwords across multiple websites.
Jeff Atwood, Founder of StackOverflow
Many application developers implement password rules that require a particular password length, a combination of characters, or various forms of capitalization to address this issue. Some of these commonly-used password rules are not that helpful from a security standpoint, but long, hard to guess passwords are generally good for security. Checking password length is easy enough, but how can you ensure that the user’s password is hard to guess?
One method is to check if they’re using a password that’s been compromised before.
If you’ve worked in web development for long, you may have heard of the NIST Guidelines. In their Digital Identity Guidelines, they specify:
“When processing requests to establish and change memorized secrets, verifiers SHALL compare the prospective secrets against a list that contains values known to be commonly-used, expected, or compromised. For example, the list MAY include, but is not limited to: Passwords obtained from previous breach corpuses.
NIST Digital Identity Guidelines
When users set their passwords, NIST recommends that you ensure users don’t use a password previously exposed in a data breach. While you might hear about the massive data breaches on the news, there’s no way you can keep up with all the data breaches happening around the world. If you tried, you’d never have time to get any coding done!
Have I Been Pwned?
A few years ago, Troy Hunt – a well-known security researcher – set up a website to address this very issue. Users can enter their password on HaveIBeenPwned.com to see if it’s been part of a data breach and, therefore, not safe to use anymore.
Unfortunately, this isn’t something that most people do, but Have I Been Pwned also has an API. Using the REST API, you can check that a user signing up for your application does not use a compromised password before you let them register. When used in addition to requiring a long, sufficiently random password, this can significantly increase your platform’s data security and minimize brute force password attacks.
Checking for Pwned Passwords on the Edge
If you’re using an authentication service, you will need to figure out the best way to implement password strength checking into your signup flow. While you could modify your authentication flow, you can also host your pwned password checking service on the edge with StackPath.
Enforcing strict password rules will annoy some users, but you can minimize this annoyance by making responses as fast as possible. In this tutorial, you’ll see how to use StackPath’s edge hosting and the Pwned Password API to ensure that users are signing up with uncompromised passwords.
To demonstrate the use of the Pwned Password API on StackPath, I’ve created an Express application in a Docker image. This project’s code is available on Github if you’d like to follow along. It uses the same Dockerfile and Express server outlined here, so you may want to start by reading that tutorial if you’re not familiar with Node or Docker.
Signup requests from a web browser will be sent to the Node application. When the client posts a password
, the Node app will call the Pwned Password API. The whole flow can be visualized here:
The Pwned Password API takes the first five characters of a SHA1 hash of the password and returns a list of hashed password suffixes to the Node application. This use of a partial hash minimizes any risk in posting secure data to a third-party service.
If you want to see the raw results that the Pwned Password API returns, visit https://api.pwnedpasswords.com/range/21BD1
in your browser. You will see a list of hashes followed by the number of times they’ve been seen in plain text on the internet:
0018A45C4D1DEF81644B54AB7F969B88D65:1
00D4F6E8FA6EECAD2A3AA415EEC418D38EC:2
011053FD0102E94D6AE2F8B83D76FAF94F6:1
...
In order to create the partial hash, prefixes, and suffixes in Node, you can use the sha1 library and built-in substring function:
// Shah-1 hash the password and break it into a prefix and suffix
const hashedPassword = sha1(password).toUpperCase();
const prefix = hashedPassword.substring(0, 5);
const suffix = hashedPassword.substring(5);
Next, you can call the Pwned Password API using the prefix
and check for the presence of suffix
in the response. In this case, I’m using the node-fetch library, but you can use whichever HTTP request package you prefer:
// Send the first 5 chars to pwned password API
fetch('https://api.pwnedpasswords.com/range/' + prefix)
.then(res => res.text())
.then(body => {
// Check if the response includes the suffix of the hash
if (body.includes(suffix)) {
// If so, return an error
res.status(400).json({message: 'Please select a more secure password. This one has already been Pwned.'});
} else {
// If not, the user is good to go.
res.status(200).json({message: 'Your password is secure and you may use it to register.'});
}
});
This demo code simply lets the user know if their password has been pwned or not, but in production, you probably want to pass valid usernames and passwords along to your authentication service to create the user’s account and log them in.
Next, you need to build your application as a Docker image. Open your terminal and navigate to the repository. Build the Dockerfile using your DockerHub username:
docker build -t /stackpath-pwned .
And push the image to Docker Hub:
docker push /stackpath-pwned:latest
Now your image is publicly available. In the next step, you’ll deploy the application to the edge using StackPath.
Once you’ve created the Node application that calls the Pwned Password API and responds appropriately, you can deploy it to StackPath. StackPath’s edge hosting network runs your Docker containers on servers worldwide, so you get the fastest response times possible.
First, create a new Workload and enter the Docker image you just pushed to Docker Hub above:
You don’t need any environment variables for this project, but you should check the box to “Add Anycast IP Address” and make sure port 443 is open.
Next, choose the PoPs (points of presence) where you want your application to be hosted. I wanted to spread my nodes across the world for benchmarking, but you should select locations according to your use case. I used:
Within a couple of minutes, your StackPath application will be running, and you’ll be able to access it from the Anycast IP address shown on your Workload’s Overview.
Using Curl
curl http:///auth -H "Content-Type: application/json" -d '{"password":"xyz"}'
Generally, the closer your code is to your users, the faster it will run. To demonstrate the performance benefits that StackPath offers over traditional hosting in the case of pwned password checking, I set up a DigitalOcean droplet in the London region with similar specs to the StackPath Workload (2GB, 1vCPU).
I used Pingdom to test the response time in five locations around the world. While not necessarily scientific, it’s clear that edge hosting is typically the better option for performance. On average, StackPath was 22% faster than traditional hosting.
Because all requests must go to the Pwned Password API, some variability can’t be controlled. Still, StackPath offers faster response times for users in general if you’re concerned with serving global traffic.
Passwords are just one piece in the quest for application security, but they’re a cornerstone of implementing strong security practices. By enforcing strong passwords and requiring users to select passwords that haven’t been compromised, you can lower their accounts’ risk. To combat any delay in checking passwords, you can utilize edge hosting with StackPath, as demonstrated above.