-
Notifications
You must be signed in to change notification settings - Fork 0
/
is-my-password-pwned.sh
executable file
·201 lines (164 loc) · 4.22 KB
/
is-my-password-pwned.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env bash
set -euo pipefail
readonly script_name="${BASH_SOURCE[0]##*/}"
function usage()
{
cat << EOF
Usage: ${script_name} [OPTIONS] [PASSWORD]
Check, if a password is pwned or compromized, with then help of
https://haveibeenpwned.com api.
With no PASSWORD, or when the PASSWORD is -, read from the standard input.
Caveat, when providing PASSWORD as an argument, it leaves the PASSWORD in the
shell's history buffer.
-h display this help and exit
-p input plain text password, default
-q quiet mode
-s input password's sha1sum
-v output version information and exit
EOF
}
function version()
{
cat << EOF
${script_name} 1.0
Copyright (0) 2018 public domain.
License CC0 or public domain: Creative Commons Zero version 1.0 or later
<https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt>.
EOF
}
function log()
{
local message=
message="$*"
if [[ "${quiet}" != "true" ]]; then
echo "${message}"
fi
}
function read_input()
{
local argument=
argument="$*"
local input=
if [[ -n "${argument}" ]] && [[ "${argument}" != "-" ]]; then
log >&2 "Please note that the ${input_type} remains visible in the shell's" \
"history. Consider clearing it from there."
input="${argument}"
else
log >&2 "Please enter the ${input_type}:"
read -a input -r -s -t 10
fi
if [[ -z "${input:-}" ]]; then
log >&2 "No input."
exit 1
fi
echo "${input}"
}
function validate_input()
{
local input_type=
input_type=$1
if [[ "${input_type}" == "password" ]]; then
return
fi
local input=
input=$2
readonly sha1_length=40
if [[ "${#input}" != "${sha1_length}" ]]; then
log >&2 "The input length ${#input} for ${input_type} does not match" \
"the expected length "${sha1_length}"."
exit 1
fi
local input_pattern="^[a-fA-F0-9]+$"
if ! [[ "${input}" =~ ${input_pattern} ]]; then
log >&2 "The input contains illegal characters to be ${input_type}."
exit 1
fi
}
function get_sha1()
{
local input=
input=$1
local sha1=
if [[ "${input_type}" == "password" ]]; then
sha1=$(echo -n "${input}" | sha1sum -)
sha1="${sha1/[[:space:]]*}"
else
sha1="${input}"
fi
echo -n "${sha1}"
}
function get_sha1_sums_from_server()
{
local hash_key=
hash_key="$1"
local user_agent=
user_agent=$2
local api="https://api.pwnedpasswords.com/range"
curl \
--fail \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--include \
--request GET "${api}/${hash_key}" \
--show-error \
--silent \
--user-agent "${user_agent}"
}
function check_if_pwned()
{
local input=
input="$1"
local user_agent=
user_agent="$2"
local hash_key=
hash_key=$(get_sha1 "${input}")
local hash_key_first_5=
hash_key_first_5="${hash_key:0:5}"
local hash_key_rest=
hash_key_rest="${hash_key:5}"
# Temporary variable is used to avoid grep cutting the pipe too early for
# curl.
local output=
output=$(get_sha1_sums_from_server "${hash_key_first_5}" "${user_agent}")
if echo "${output}" \
| grep --quiet --ignore-case "${hash_key_rest}"; then
log >&2 "This password is pwned! Please, consider changing it for" \
"the accounts it is currently used, and avoid using it in the" \
"future for any other account."
exit 1
else
log "This password is not pwned! Feel free to keep using it."
fi
}
quiet=false
input_type=password
while getopts ":hpqsv" options; do
case "${options}" in
h)
usage
exit
;;
p)
input_type=password
;;
q)
quiet=true
;;
s)
input_type=sha1
;;
v)
version
exit
;;
\?)
usage >&2
exit 1
;;
esac
done
shift "$((OPTIND - 1))"
readonly user_input=$(read_input "$@")
shift "$#"
validate_input "${input_type}" "${user_input}"
check_if_pwned "${user_input}" "${script_name}"