Testing Hacked Passwords

Secure passwords must be guaranteed to not be exposed to hackers and dictionary attacks. With a webservice, you can test any password safely for compromises.

For a long time, passwords were considered safe when they were complex enough. Meanwhile, these recommendations have been changed because it turned out that complex passwords can easily be completely unsafe (for example P@ssw0rd).

What’s way more important is that passwords are secret and not exposed by previous hacker attacks.

  • Once a password - however complex it may be - is part of a hacker dictionary and regularly used in automated dictionary attacks, it must be changed immediately.
  • Even the most simple password can be highly secure if it is not used in attacks, and it stays this way until this changes. Forcing users to change passwords in regular intervals is nonsense. What’s more important is that users can change their passwords immediately and as often as they wish if they feel their password has been compromised.

How Do I Detect Compromises?

Most of us are not part of the hacker community so we can’t simply ask your friendly hacker from around the corner if he knows your password.

And even if you did know a hacker personally: would you really tell him your password to find out if it is on his list? If you did, you could at least be certain that he’d put it on his list right away.

HaveIBeenPwned is a trustworthy website that can check email addresses and passwords for compromises. This website also provides free web services that you can use to test passwords in an automated way.

Using the webservice is safe, and it is way safer than testing passwords manually on the website because the PowerShell code clearly tells you what is going on and that you are not exposing the password to the webservice:

  • If you enter a password manually into a website, you never know what the website does with it. You may actually compromise it by your submission.
  • The webservice, in contrast, only takes the first five bytes of your password hash. This way it is technically impossible that your password can get exposed: you only submit five bytes, and you submit five bytes of a hash, not the password. So there is not enough information in your submission to expose your password.

Testing Passwords

Here is a PowerShell function that takes passwords and submits a fraction of its hash to the webservice. In return, you receive the remainder of the hash and can compare it to your hash. This way, it becomes easy to test passwords for compromises without any risk.

function Test-Password
{
  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory, Position=0)]
    [System.Security.SecureString]
    $Password
  )
  
  # take securestring and get the entered plain text password
  # we are using a securestring only to get a masked input box:
  $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
  $plain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    
  # hash the password:
  $bytes = [Text.Encoding]::UTF8.GetBytes($plain)
  $stream = [IO.MemoryStream]::new($bytes)
  $hash = Get-FileHash -Algorithm 'SHA1' -InputStream $stream
  $stream.Close()
  $stream.Dispose()
  
  # separate the first 5 hash characters from the rest:
  $first5hashChars,$remainingHashChars = $hash.Hash -split '(?<=^.{5})'
  
  # send the first 5 hash characters to the webservice:
  $url = "https://api.pwnedpasswords.com/range/$first5hashChars"
  [Net.ServicePointManager]::SecurityProtocol = 'Tls12'
  $response = Invoke-RestMethod -Uri $url -UseBasicParsing
  
  # split result into individual lines...
  $lines = $response -split '\r\n'
  # ...and get the line where the returned hash matches your
  # remainder of the hash that you kept private:
  $filteredLines = $lines -like "$remainingHashChars*"
  
  # return the number of compromises:
  [int]($filteredLines -split ':')[-1]
}

Test-Password is super easy to use: simply run it. You get prompted for your password. Once you enter your password, the function returns the number of registered compromises, i.e. the number of known attacks where your password surfaced.

A password is safe when it returns 0 compromises. Any password with more than 0 compromises must be changed immediately.

If the function returns compromises for your password, this does not mean that you have been hacked. It just means that someone was hacked that coincidentally used the same password as you.

Compromised passwords end up in dictionaries that hacker and script kiddies use to brute force attack sensitive systems. So if your password is on these lists, regardless of whether it was your password or the password from someone else, it is now highly insecure, and if you haven’t been hacked then it is likely that you will in the near future.