Skip to main content

A word about my Have I Been Pwned package

hibp

Yesterday evening I posted a Tweet about improving user entered passwords using Troy Hunt's service Have I Been Pwnd.

Improve password security
A promotional tweet about my dragonbe/hibp package
It went viral over night with many likes and retweets. But I also got a ton of questions regarding the usage and the security of this package. It turns out many people are scared to send passwords over the internet and are afraid to just use a package (like mine) for password checking.

secure question 1

secure question 2


Just to give you an idea of how Have I Been Pwnd service works, I created this little diagram to visualise how I'm using the service.

Visual representation of using HIBP service
A user enters their password in our registration or password renewal form. We create a SHA1 hash from it and send the first 5 characters of that hash to HIBP. We get a list of hashes back and a count of how many times this hash has been found in the HIBP database. On our server we lookup the remaining hash against the list we received from HIBP and if there's a match, we return the count back to the user.

No passwords are sent in the clear over the internet.
No full hashes are exposed to Have I Been Pwned.
All verification happens on the server where the user enters their password.

To give you an idea how this looks in PHP code, here's a real simple example. BEWARE: this is just code used as an example! Do not copy/paste it and use it in production as it has no filtering and validation of input values!!!

<?php

header('Content-Type: application/json');
$uri = 'https://api.pwnedpasswords.com/range';
$string = $_GET['string'];
$hash = strtoupper(sha1($string, false));
$subHash = substr($hash, 0, 5);

if (3 > strlen($string)) {
    echo json_encode(['hash' => $hash, 'count' =>  0]);
    exit(0);
}

$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_HTTPGET => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 20,
    CURLOPT_USERAGENT => 'In2qa/0.0.1-Alpha',
    CURLOPT_URL => sprintf('%s/%s', $uri, $subHash),
    CURLOPT_HTTPHEADER => [
        'Accept' => 'application/vnd.haveibeenpwned.v2+json',
        'Access-Control-Allow-Origin' => 'https://api.pwnedpasswords.com',
        'Access-Control-Max-Age' => '3628800',
        'Access-Control-Allow-Methods' => 'GET',
    ],
]);

$response = curl_exec($ch);
$response = str_replace("\r\n", PHP_EOL, $response);
$data = explode(PHP_EOL, $response);
$result = array_filter($data, function ($value) use ($hash, $subHash) {
    list ($sha1, $count) = explode(':', $value);
    $lookup = $subHash . $sha1;
    return ($hash === $lookup);
});

if ([] === $result) {
    echo json_encode(['hash' => $hash, 'count' =>  0]);
    exit(0);
}
$response = array_pop($result);
list ($sha1, $count) = explode(':', $response);
echo json_encode(['hash' => $hash, 'count' => $count]);
exit(0);

To facilitate the usage of this service with added filtering and validation of input values, I've created a simple Composer package: "dragonbe/hibp", which you can find on GitHub as well.

To summarise:

  • No passwords are shared with HIBP
  • All checks occur within your own web application
  • You can do things manually, I just provided a package to make it easier
I am not in a business of stealing passwords or hijacking your good password processes and policies. The composer package "dragonbe/hibp" was build to make it easy for everyone to implement this very powerful service provided by Troy Hunt. I was doing my bit to make the internet a safer place for everyone.

Why would you implement Have I Been Pwnd service on your web application?
Not everyone who uses a computer is aware that strong passwords is a hard job and password managers are not mandatory by law or installed by default by OS vendors: don't expect everyone to apply good password hygiene.

By ensuring your users have passwords that are strong enough and not yet found in earlier breaches (see Have I Been Pwnd), you can at least ensure that if someone's user account and password are compromised, it cannot be used against your own application or service. It's not a 100% guarantee bad guys aren't exploiting your user's accounts, but at least it makes it harder to compromise based on earlier breaches.

Comments

Popular posts from this blog

Deploy Docker containers fast to Microsoft Azure

DEPLOY DOCKER CONTAINERS FAST TO MICROSOFT AZURE It’s hard to ignore the fact thatDockeris a way to move forward for rapid application development, distributed architectures and microservices. For developersDockeroffers great advantages as they can build their containers specifically for the task they work on. They grab a base image of a container, modify it for their purpose and prepare the functionality inside the container. Quality, testing and security teams now have a single instance to look at and ensure all functional and regulatory requirements are met. System engineers now don’t have to worry about providing a system with the required specs as the container is already provisioned for that purpose. But where do you deploy yourDockercontainers? You can set up your existing bare metal infrastructure to allow them to run containers, but this also means you need to learn about securing your container infrastructure, which is not an easy task. Luckily “the cloud” offers container …

Speeding up database calls with PDO and iterators

When you review lots of code, you often wonder why things were written the way they were. Especially when making expensive calls to a database, I still see things that could and should be improved.
No framework development When working with a framework, mostly these database calls are optimized for the developer and abstract the complex logic to improve and optimize the retrieval and usage of data. But then developers need to build something without a framework and end up using the basics of PHP in a sub-optimal way.

$pdo = new \PDO( $config['db']['dsn'], $config['db']['username'], $config['db']['password'] ); $sql = 'SELECT * FROM `gen_contact` ORDER BY `contact_modified` DESC'; $stmt = $pdo->prepare($sql); $stmt->execute(); $data = $stmt->fetchAll(\PDO::FETCH_OBJ); echo 'Getting the contacts that changed the last 3 months' . PHP_EOL; foreach ($data as $row) { $dt = new \DateTime('2015-04-…

PHP 7 and Apache on macOS Sierra

I posted several talks about compiling PHP from source, but everyone was trying to convince me that a package manager like Homebrew was a more convenient way to install. The purpose of Homebrew is simple: a package manager for macOS that will allow you to set up and install common packages easily and allows you to update frequently using simple commands. I used a clean installation of macOS Sierra to ensure all steps could be recorded and tested. In most cases you already have done work on your Mac, so chances are you can skip a few steps in this tutorial. APACHE AND PHP WITH HOMEBREW I’ve made this according to the installation instructions given on GetGrav. The installation procedures These installation procedures will set up your macOS Sierra with PHP 7.1 and Apache 2.4. Install Xcode command line tools (if not done yet)xcode-select --install Install Homebrew/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" Set up for in…