一種使用 Pwned Passwords API 的簡單 Ruby 方式。
API 文件 | GitHub 儲存庫
Troy Hunt 的 Pwned Passwords API 可讓您檢查是否在任何大規模資料外洩中發現了密碼。
Pwned
是一個 Ruby 函式庫,用於使用 Pwned Passwords API 的 k-Anonymity 模型根據 API 測試密碼,而無需將整個密碼傳送到服務。
此 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
大多數時候你只在乎密碼之前是否被破解過。您可以使用簡化的存取器來檢查密碼是否已被破解,或被破解了多少次:
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_proxy
或HTTP_PROXY
環境變數設定 HTTP 代理。如果沒有給予代理選項,這與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_proxy
或HTTP_PROXY
環境變數設定 HTTP 代理。如果沒有給予代理選項,這與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,我建議您使用 devise-pwned_password 擴展,該擴展現在由該 gem 提供支援。
如果您正在使用 Rodauth,那麼您可以使用由該 gem 提供支援的 rodauth-pwned 功能。
gem 提供了一個用於檢查密碼的命令列實用程式。您可以從終端應用程式中呼叫它,如下所示:
$ pwned password
Pwned !
The password has been found in public breaches 3645804 times.
如果您不希望正在檢查的密碼可見,請致電:
$ pwned --secret
系統將提示您輸入密碼,但不會顯示該密碼。
為了減少不必要的網路請求,unpwn 項目使用前一百萬個密碼的清單來檢查密碼。只有當密碼未包含在前一百萬中時,才會根據 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 專案的程式碼庫、問題追蹤器、聊天室和郵件清單中進行互動的每個人都應該遵守行為準則。