Skip to content

Instantly share code, notes, and snippets.

@schmich
Last active October 15, 2021 14:44
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save schmich/aeaffac922271a11b70e9a79a5fee19c to your computer and use it in GitHub Desktop.
Save schmich/aeaffac922271a11b70e9a79a5fee19c to your computer and use it in GitHub Desktop.
Check if a password has been pwned with the Pwned Passwords V2 API
# Check a single password interactively.
# Usage: ruby pwned-interactive.rb
require 'io/console'
require 'open-uri'
require 'digest'
puts "The 5-character prefix of the password's SHA-1 hash will be sent."
puts "For details, see https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"
print 'Password (hidden): '
password = $stdin.noecho(&:gets).strip
puts
hash = Digest::SHA1.hexdigest(password).upcase
prefix = hash[0...5]
url = "https://api.pwnedpasswords.com/range/#{prefix}"
print "Requesting #{url}. Continue (y/n)? "
if $stdin.gets.strip.downcase[0] != 'y'
puts 'Canceled.'
exit
end
pwned = open(url) do |response|
Hash[response.each_line.map { |line|
suffix, count = line.strip.split(':')
[(prefix + suffix).upcase, count.to_i]
}]
end
count = pwned[hash]
print "Password with SHA-1 hash #{hash} "
if count
puts "has been pwned. Seen #{count} time#{count == 1 ? '' : 's'}."
else
puts "has not been pwned."
end
# Check multiple passwords in a plain-text file.
# Usage: ruby pwned-multi.rb <file>
require 'open-uri'
require 'digest'
password_filename = ARGV.first
if !password_filename
$stderr.puts 'Password file expected. Exiting.'
exit 1
end
passwords = File.readlines(password_filename).map(&:chomp)
if passwords.empty?
$stderr.puts 'No passwords. Exiting.'
exit 1
end
puts "The 5-character prefix of each password's SHA-1 hash will be sent."
puts "For details, see https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"
hashes = passwords.map { |password| Digest::SHA1.hexdigest(password).upcase }
prefixes = hashes.map { |hash| hash[0...5] }
urls = prefixes.map { |prefix| "https://api.pwnedpasswords.com/range/#{prefix}" }
puts "Requests will be made to the following URLs:\n#{urls.join("\n")}"
print "Continue (y/n)? "
if $stdin.gets.strip.downcase[0] != 'y'
puts 'Canceled.'
exit
end
hashes.zip(prefixes, urls).each.with_index do |(hash, prefix, url), index|
pwned = open(url) do |response|
Hash[response.each_line.map { |line|
suffix, count = line.strip.split(':')
[(prefix + suffix).upcase, count.to_i]
}]
end
print "Password on line #{index + 1} (SHA-1 hash #{hash}) "
count = pwned[hash]
if count
puts "has been pwned. Seen #{count} time#{count == 1 ? '' : 's'}."
else
puts "has not been pwned."
end
end
@rapind
Copy link

rapind commented Feb 22, 2018

Thanks!

@ViktorEvil
Copy link

Cheers, very helpful

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