Pwned Passwords API を使用する Ruby の簡単な方法。
API ドキュメント | GitHubリポジトリ
Troy Hunt の Pwned Passwords API を使用すると、大規模なデータ侵害でパスワードが見つかったかどうかを確認できます。
Pwned
Pwned Passwords API の k-Anonymity モデルを使用して、パスワード全体をサービスに送信せずに API に対してパスワードをテストするための Ruby ライブラリです。
この API からのデータは、Have I been pwned? によって提供されます。 APIを使用する前に、APIの使用許諾およびライセンスをご確認ください。
これは、Ruby アプリケーションでこの gem を使用してユーザーのパスワードを改善する方法について私が書いたブログ投稿です。
次の行をアプリケーションの Gemfile に追加します。
gem 'pwned'
そして、以下を実行します。
$ bundle
または、次のように自分でインストールします。
$ gem install pwned
この gem を使用するにはいくつかの方法があります。
API に対してパスワードをテストするには、 Pwned::Password
オブジェクトをインスタンス化し、それがpwned?
。
password = Pwned :: Password . new ( "password" )
password . pwned?
#=> true
password . pwned_count
#=> 3303003
データセット内にパスワードが何回出現したかを確認することもできます。
password = Pwned :: Password . new ( "password" )
password . pwned_count
#=> 3303003
これはサインアップ フローの一部として使用される可能性が高いため、サービスがダウンした場合でもユーザー ジャーニーが妨げられないように、エラーをレスキューすることをお勧めします。
begin
password = Pwned :: Password . new ( "password" )
password . pwned?
rescue Pwned :: Error => e
# Ummm... don't worry about it, I guess?
end
ほとんどの場合、気にするのはパスワードが以前に pwn されたかどうかだけです。簡素化されたアクセサーを使用して、パスワードが pwn されたかどうか、または pwn された回数を確認できます。
Pwned . pwned? ( "password" )
#=> true
Pwned . pwned_count ( "password" )
#=> 3303003
API にリクエストを行うときにNet::HTTP.start
で使用する HTTP リクエスト オプションを設定できます。これらのオプションは、 Net::HTTP.start
ドキュメントに記載されています。
オプションをコンストラクターに渡すことができます。
password = Pwned :: Password . new ( "password" , read_timeout : 10 )
グローバルなデフォルトを指定することもできます。
Pwned . default_request_options = { read_timeout : 10 }
:headers
オプションは HTTP ヘッダーを定義します。これらのヘッダーは文字列キーである必要があります。
password = Pwned :: Password . new ( "password" , headers : {
'User-Agent' => 'Super fun new user agent'
} )
HTTP プロキシは、 http_proxy
またはHTTP_PROXY
環境変数を使用して設定できます。これは、プロキシ オプションが指定されていない場合にNet::HTTP
HTTP プロキシを処理するのと同じ方法です。 Ruby が環境からプロキシを検出する方法の詳細については、 URI::Generic#find_proxy
を参照してください。
# Set in the environment
ENV [ "http_proxy" ] = "https://username:[email protected]:12345"
# Will use the above proxy
password = Pwned :: Password . new ( "password" )
:proxy
オプションを使用してカスタム HTTP プロキシを指定できます。
password = Pwned :: Password . new (
"password" ,
proxy : "https://username:[email protected]:12345"
)
プロキシを設定したくない場合、およびプロキシが環境から推論されることを望まない場合は、 :ignore_env_proxy
キーを設定します。
password = Pwned :: Password . new ( "password" , ignore_env_proxy : true )
ActiveRecord モデルで使用できるカスタム バリデーターがあります。
class User < ApplicationRecord
validates :password , not_pwned : true
# or
validates :password , not_pwned : { message : "has been pwned %{count} times" }
end
I18n を使用してエラー メッセージを変更できます ( %{count}
使用して、データ侵害でパスワードが確認された回数を補間します)。
en :
errors :
messages :
not_pwned : has been pwned %{count} times
pwned_error : might be pwned
パスワードが一定回数出現しても問題ないと判断される場合は、しきい値を設定できます。バリデータは、 pwned_count
がしきい値より大きいかどうかをチェックします。
class User < ApplicationRecord
# The record is marked as valid if the password has been used once in the breached data
validates :password , not_pwned : { threshold : 1 }
end
デフォルトでは、haveibeenpwned.com サーバーにアクセスできない場合、レコードは有効なものとして扱われます。これは:on_error
バリデーター パラメーターを使用して変更できます。
class User < ApplicationRecord
# The record is marked as valid on network errors.
validates :password , not_pwned : true
validates :password , not_pwned : { on_error : :valid }
# The record is marked as invalid on network errors
# (error message "could not be verified against the past data breaches".)
validates :password , not_pwned : { on_error : :invalid }
# The record is marked as invalid on network errors with custom error.
validates :password , not_pwned : { on_error : :invalid , error_message : "might be pwned" }
# We will raise an error on network errors.
# This means that `record.valid?` will raise `Pwned::Error`.
# Not recommended to use in production.
validates :password , not_pwned : { on_error : :raise_error }
# Call custom proc on error. For example, capture errors in Sentry,
# but do not mark the record as invalid.
validates :password , not_pwned : {
on_error : -> ( record , error ) { Raven . capture_exception ( error ) }
}
end
:request_options
を使用して、バリデーターから行われるネットワーク要求を構成できます (使用可能なオプションのリストについては、Net::HTTP.start を参照してください)。
validates :password , not_pwned : {
request_options : {
read_timeout : 5 ,
open_timeout : 1
}
}
これらのオプションは、グローバルに定義されたデフォルト オプション (上記を参照) をオーバーライドします。
これらのオプションに加えて、次のことも設定できます。
HTTP ヘッダーは:headers
キーで指定できます (例: "User-Agent"
)
validates :password , not_pwned : {
request_options : {
headers : { "User-Agent" => "Super fun user agent" }
}
}
HTTP プロキシは、 http_proxy
またはHTTP_PROXY
環境変数を使用して設定できます。これは、プロキシ オプションが指定されていない場合にNet::HTTP
HTTP プロキシを処理するのと同じ方法です。 Ruby が環境からプロキシを検出する方法の詳細については、 URI::Generic#find_proxy
を参照してください。
# Set in the environment
ENV [ "http_proxy" ] = "https://username:[email protected]:12345"
validates :password , not_pwned : true
:proxy
キーを使用してカスタム HTTP プロキシを指定できます。
validates :password , not_pwned : {
request_options : {
proxy : "https://username:[email protected]:12345"
}
}
プロキシを設定したくない場合、およびプロキシが環境から推論されることを望まない場合は、 :ignore_env_proxy
キーを設定します。
validates :password , not_pwned : {
request_options : {
ignore_env_proxy : true
}
}
事前にパスワードをハッシュし、後で Pwned Passwords API を呼び出すユースケースが考えられます (たとえば、プレーンテキストのパスワードを保存せずにジョブをキューに入れたい場合)。これを行うには、次のようにPwned.hash_password
メソッドでパスワードをハッシュし、そのハッシュでPwned::HashedPassword
クラスを初期化します。
hashed_password = Pwned . hash_password ( password )
# some time later
Pwned :: HashedPassword . new ( hashed_password , request_options ) . pwned?
Pwned::HashedPassword
コンストラクターは、通常のPwned::Password
コンストラクターと同じオプションをすべて受け取ります。
Devise を使用している場合は、この gem を利用している Device-pwned_password 拡張機能を使用することをお勧めします。
Rodauth を使用している場合は、この gem を利用した Rodauth-pwned 機能を使用できます。
gem は、パスワードをチェックするためのコマンド ライン ユーティリティを提供します。次のようにターミナル アプリケーションから呼び出すことができます。
$ pwned password
Pwned !
The password has been found in public breaches 3645804 times.
チェックしているパスワードを表示したくない場合は、次のように呼び出します。
$ pwned --secret
パスワードの入力を求められますが、パスワードは表示されません。
不必要なネットワーク要求を削減するために、unpwn プロジェクトは上位 100 万個のパスワードのリストを使用してパスワードをチェックします。パスワードが上位 100 万件に含まれていない場合にのみ、Pwned Passwords API に対してチェックされます。
@daz は、この gem を使用して、Pi の数字が何回パスワードとして使用され、漏洩したかを示す素晴らしい例を共有しました。
require 'pwned'
PI = '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111'
for n in 1 .. 40
password = Pwned :: Password . new PI [ 0 .. ( n + 1 ) ]
str = [ n . to_s . rjust ( 2 ) ]
str << ( password . pwned? ? '?' : '?' )
str << password . pwned_count . to_s . rjust ( 4 )
str << password . password
puts str . join ' '
end
その結果は驚くかもしれませんし、驚かないかもしれません。
1 ? 16 3.1
2 ? 238 3.14
3 ? 34 3.141
4 ? 1345 3.1415
5 ? 2552 3.14159
6 ? 791 3.141592
7 ? 9582 3.1415926
8 ? 1591 3.14159265
9 ? 637 3.141592653
10 ? 873 3.1415926535
11 ? 137 3.14159265358
12 ? 103 3.141592653589
13 ? 65 3.1415926535897
14 ? 201 3.14159265358979
15 ? 41 3.141592653589793
16 ? 57 3.1415926535897932
17 ? 28 3.14159265358979323
18 ? 29 3.141592653589793238
19 ? 1 3.1415926535897932384
20 ? 7 3.14159265358979323846
21 ? 5 3.141592653589793238462
22 ? 2 3.1415926535897932384626
23 ? 2 3.14159265358979323846264
24 ? 0 3.141592653589793238462643
25 ? 3 3.1415926535897932384626433
26 ? 0 3.14159265358979323846264338
27 ? 0 3.141592653589793238462643383
28 ? 0 3.1415926535897932384626433832
29 ? 0 3.14159265358979323846264338327
30 ? 0 3.141592653589793238462643383279
31 ? 0 3.1415926535897932384626433832795
32 ? 0 3.14159265358979323846264338327950
33 ? 0 3.141592653589793238462643383279502
34 ? 0 3.1415926535897932384626433832795028
35 ? 0 3.14159265358979323846264338327950288
36 ? 0 3.141592653589793238462643383279502884
37 ? 0 3.1415926535897932384626433832795028841
38 ? 0 3.14159265358979323846264338327950288419
39 ? 0 3.141592653589793238462643383279502884197
40 ? 0 3.1415926535897932384626433832795028841971
リポジトリをチェックアウトした後、 bin/setup
実行して依存関係をインストールします。次に、 rake spec
実行してテストを実行します。 bin/console
実行して対話型プロンプトを表示し、実験することもできます。
この gem をローカル マシンにインストールするには、 bundle exec rake install
実行します。新しいバージョンをリリースするには、 version.rb
のバージョン番号を更新してから、 bundle exec rake release
実行します。これにより、そのバージョンの git タグが作成され、git のコミットとタグがプッシュされ、 .gem
ファイルが Rubygems.org にプッシュされます。
バグレポートとプルリクエストは、GitHub (https://github.com/philnash/pwned) で歓迎されます。このプロジェクトは、安全で居心地の良いコラボレーションの場となることを目的としており、貢献者は貢献者規約の行動規範を遵守することが期待されます。
この gem は、MIT ライセンスの条件に基づいてオープン ソースとして利用できます。
Pwned プロジェクトのコードベース、問題トラッカー、チャット ルーム、メーリング リストでやり取りするすべての人は、行動規範に従うことが期待されます。