-
-
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 |
I tried this script as-is from the article, which gives a false sense of password not being breached if the output from openssl is not in the format expected. Once I tried "password" and it said even that was not found, I knew something was up. Maybe it could have a sanity self-check added for safety sake.
In any case, I note a possible fix for this above, but here was mine:
hash="$(echo -n $password | openssl sha1 -hex -r | cut -d\ -f1)"
Note the necessary extra space after the backslash.
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.
There are a number of issues with that script:
-
without
-r
andIFS=
,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. -
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 -
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
Withecho $password
, $password would undergo split+glob. So
for instance, it could say that the "***" password is not
breached, becauseecho -n $password
would output the list of
non-hidden files in the current directory. -
On my system,
$ echo -n | openssl sha1
(stdin)= da39a3ee5e6b4b0d3255bfef95601890afd80709
see the leading "(stdin)= " which needs to be removed
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:]'
ortr 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
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 }'
@croose using your version I get the following error Bad substitution
@stephane-chazelas yours worked perfectly - thanks
Quick & dirty caching: