Skip to content

Instantly share code, notes, and snippets.

@IcyApril
Last active May 7, 2023 04:55
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save IcyApril/56c3fdacb3a640f37c245e5813b98b99 to your computer and use it in GitHub Desktop.
Save IcyApril/56c3fdacb3a640f37c245e5813b98b99 to your computer and use it in GitHub Desktop.
#!/bin/bash
echo -n Password:
read -s password
echo
hash="$(echo -n $password | openssl sha1)"
upperCase="$(echo $hash | tr '[a-z]' '[A-Z]')"
prefix="${upperCase:0:5}"
response=$(curl -s https://api.pwnedpasswords.com/range/$prefix)
while read -r line; do
lineOriginal="$prefix$line"
if [ "${lineOriginal:0:40}" == "$upperCase" ]; then
echo "Password breached."
exit 1
fi
done <<< "$response"
echo "Password not found in breached database."
exit 0
@tdwalton
Copy link

Thanks for this. Hope you don't mind, I borrowed the meat of the script and mashed it up with @agilebits' 1Password CLI tool, to check your 1Password entries against the breached password list. Admittedly, 1Password users are probably not the right audience for such a tool, as users of password managers are probably (hopefully?) using randomly generated, strong passwords, but it was a fun exercise.

I left it largely untouched, as I didn't experience any of the issues other commenters mentioned above.

1passwordpwnedcheck.sh

@stephane-chazelas
Copy link

There are a number of issues with that script:

  1. without -r and IFS=, read would fail to preserve leading and
    trailing space or tab characters in the input or backslashes.
    See
    https://unix.stackexchange.com/questions/209123/understanding-ifs-read-r-line
    For instance, it would say that "test\123" is breached.

  2. with echo -n, that would not work properly for passwords like
    -nenene, and possibly (depending on the environment) some that
    contain backslashes. See
    https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo

  3. leaving a variable unquoted has a very special meaning in
    shells like bash. See
    https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells
    With echo $password, $password would undergo split+glob. So
    for instance, it could say that the "***" password is not
    breached, because echo -n $password would output the list of
    non-hidden files in the current directory.

  4. On my system,

  $ echo -n | openssl sha1
  (stdin)= da39a3ee5e6b4b0d3255bfef95601890afd80709

see the leading "(stdin)= " which needs to be removed

  1. tr '[a-z]' '[A-Z]' only makes sense in the POSIX/C locale.
    There's not much guarantee what you'll get in other locales.
    tr '[:lower:]' '[:upper:]' or tr abcdef ABCDEF are better for
    that. Recent versions of bash now also have builtin operators
    for case conversion (hash=${hash^^})

How about:

IFS= read -rsp 'Password: ' password
echo
hash=$(printf %s "$password" | openssl sha1 | tr abcdef ABCDEF)
hash=${hash##* }
prefix=${hash:0:5}
suffix=${hash:5}
if
  curl -s "https://api.pwnedpasswords.com/range/$prefix" |
    grep "^$suffix" > /dev/null
then
  echo "Password breached."
  exit 1
else
  echo "Password not found in breached database."
  exit 0
fi

@croose
Copy link

croose commented May 31, 2018

For fun, a two-line version (after input checking) that uses AWK for the dirty work:

#!/bin/sh

if [ -z "$1" ]
then
    echo "Usage: ${0##*/} <password>"
    exit 1
fi

HASH="$(printf "$1" | openssl sha1)"
curl -s "https://api.pwnedpasswords.com/range/${HASH:0:5}" | 
    awk -F":" -v SUFFIX="${HASH:5}" '$1 == toupper(SUFFIX) { print $2 }'

@wavesailor
Copy link

@croose using your version I get the following error Bad substitution

@stephane-chazelas yours worked perfectly - thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment