Uma maneira fácil e Ruby de usar a API Pwned Passwords.
Documentos da API | Repositório GitHub
A API Pwned Passwords de Troy Hunt permite que você verifique se uma senha foi encontrada em alguma das grandes violações de dados.
Pwned
é uma biblioteca Ruby que usa o modelo k-Anonymity da API Pwned Passwords para testar uma senha na API sem enviar a senha inteira para o serviço.
Os dados desta API são fornecidos por Have I been pwned?. Antes de usar a API, verifique os usos aceitáveis e a licença da API.
Aqui está uma postagem no blog que escrevi sobre como usar essa joia em seus aplicativos Ruby para melhorar as senhas dos usuários.
Adicione esta linha ao Gemfile da sua aplicação:
gem 'pwned'
E então execute:
$ bundle
Ou instale você mesmo como:
$ gem install pwned
Existem algumas maneiras de usar esta joia:
Para testar uma senha na API, instancie um objeto Pwned::Password
e pergunte se ele é pwned?
.
password = Pwned :: Password . new ( "password" )
password . pwned?
#=> true
password . pwned_count
#=> 3303003
Você também pode verificar quantas vezes a senha aparece no conjunto de dados.
password = Pwned :: Password . new ( "password" )
password . pwned_count
#=> 3303003
Como você provavelmente está usando isso como parte de um fluxo de inscrição, é recomendável resgatar erros para que, se o serviço cair, a jornada do usuário não seja perturbada.
begin
password = Pwned :: Password . new ( "password" )
password . pwned?
rescue Pwned :: Error => e
# Ummm... don't worry about it, I guess?
end
Na maioria das vezes você só se importa se a senha foi descoberta antes ou não. Você pode usar acessadores simplificados para verificar se a senha foi hackeada ou quantas vezes ela foi hackeada:
Pwned . pwned? ( "password" )
#=> true
Pwned . pwned_count ( "password" )
#=> 3303003
Você pode definir opções de solicitação HTTP a serem usadas com Net::HTTP.start
ao fazer a solicitação à API. Essas opções estão documentadas na documentação do Net::HTTP.start
.
Você pode passar as opções para o construtor:
password = Pwned :: Password . new ( "password" , read_timeout : 10 )
Você também pode especificar padrões globais:
Pwned . default_request_options = { read_timeout : 10 }
A opção :headers
define cabeçalhos HTTP. Esses cabeçalhos devem ser chaves de string.
password = Pwned :: Password . new ( "password" , headers : {
'User-Agent' => 'Super fun new user agent'
} )
Um proxy HTTP pode ser configurado usando a variável de ambiente http_proxy
ou HTTP_PROXY
. Esta é a mesma maneira que Net::HTTP
lida com proxies HTTP se nenhuma opção de proxy for fornecida. Veja URI::Generic#find_proxy
para detalhes completos sobre como Ruby detecta um proxy do ambiente.
# Set in the environment
ENV [ "http_proxy" ] = "https://username:[email protected]:12345"
# Will use the above proxy
password = Pwned :: Password . new ( "password" )
Você pode especificar um proxy HTTP personalizado com a opção :proxy
:
password = Pwned :: Password . new (
"password" ,
proxy : "https://username:[email protected]:12345"
)
Se você não quiser definir um proxy e não quiser que um proxy seja inferido do ambiente, defina a chave :ignore_env_proxy
:
password = Pwned :: Password . new ( "password" , ignore_env_proxy : true )
Existe um validador personalizado disponível para seus modelos ActiveRecord:
class User < ApplicationRecord
validates :password , not_pwned : true
# or
validates :password , not_pwned : { message : "has been pwned %{count} times" }
end
Você pode alterar a mensagem de erro usando I18n (use %{count}
para interpolar o número de vezes que a senha foi vista nas violações de dados):
en :
errors :
messages :
not_pwned : has been pwned %{count} times
pwned_error : might be pwned
Se você concordar com o fato de a senha aparecer um certo número de vezes antes de decidir que ela é inválida, você pode definir um limite. O validador verificará se pwned_count
é maior que o limite.
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
Por padrão, o registro será tratado como válido quando não conseguirmos acessar os servidores haveibeenpwned.com. Isso pode ser alterado com o parâmetro validador :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
Você pode configurar solicitações de rede feitas a partir do validador usando :request_options
(consulte Net::HTTP.start para obter a lista de opções disponíveis).
validates :password , not_pwned : {
request_options : {
read_timeout : 5 ,
open_timeout : 1
}
}
Estas opções substituem as opções padrão definidas globalmente (veja acima).
Além dessas opções, você também pode definir o seguinte:
Os cabeçalhos HTTP podem ser especificados com a chave :headers
(por exemplo, "User-Agent"
)
validates :password , not_pwned : {
request_options : {
headers : { "User-Agent" => "Super fun user agent" }
}
}
Um proxy HTTP pode ser configurado usando a variável de ambiente http_proxy
ou HTTP_PROXY
. Esta é a mesma maneira que Net::HTTP
lida com proxies HTTP se nenhuma opção de proxy for fornecida. Veja URI::Generic#find_proxy
para detalhes completos sobre como Ruby detecta um proxy do ambiente.
# Set in the environment
ENV [ "http_proxy" ] = "https://username:[email protected]:12345"
validates :password , not_pwned : true
Você pode especificar um proxy HTTP personalizado com a chave :proxy
:
validates :password , not_pwned : {
request_options : {
proxy : "https://username:[email protected]:12345"
}
}
Se você não quiser definir um proxy e não quiser que um proxy seja inferido do ambiente, defina a chave :ignore_env_proxy
:
validates :password , not_pwned : {
request_options : {
ignore_env_proxy : true
}
}
Você pode ter um caso de uso para fazer hash da senha antecipadamente e, em seguida, fazer a chamada para a API Pwned Passwords posteriormente (por exemplo, se desejar enfileirar um trabalho sem armazenar a senha em texto simples). Para fazer isso, você pode fazer o hash da senha com o método Pwned.hash_password
e então inicializar a classe Pwned::HashedPassword
com o hash, assim:
hashed_password = Pwned . hash_password ( password )
# some time later
Pwned :: HashedPassword . new ( hashed_password , request_options ) . pwned?
O construtor Pwned::HashedPassword
usa todas as mesmas opções que o construtor Pwned::Password
normal.
Se você estiver usando o Devise, recomendo que você use a extensão devise-pwned_password que agora é alimentada por esta joia.
Se você estiver usando Rodauth, poderá usar o recurso rodauth-pwned que é alimentado por esta joia.
A gem fornece um utilitário de linha de comando para verificar senhas. Você pode chamá-lo do seu aplicativo de terminal assim:
$ pwned password
Pwned !
The password has been found in public breaches 3645804 times.
Se você não deseja que a senha que está verificando fique visível, ligue para:
$ pwned --secret
A senha será solicitada, mas ela não será exibida.
Para reduzir solicitações de rede desnecessárias, o projeto unpwn usa uma lista do milhão de senhas principais para verificar as senhas. Somente se uma senha não estiver incluída no milhão principal ela será verificada na API Pwned Passwords.
@daz compartilhou um exemplo fantástico de uso desta joia para mostrar quantas vezes os dígitos do Pi foram usados como senhas e vazaram.
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
Os resultados podem ou não surpreendê-lo.
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
Depois de verificar o repositório, execute bin/setup
para instalar as dependências. Em seguida, execute rake spec
para executar os testes. Você também pode executar bin/console
para obter um prompt interativo que permitirá experimentar.
Para instalar esta jóia em sua máquina local, execute bundle exec rake install
. Para lançar uma nova versão, atualize o número da versão em version.rb
e, em seguida, execute bundle exec rake release
, que criará uma tag git para a versão, enviará commits e tags git e enviará o arquivo .gem
para rubygems.org.
Relatórios de bugs e solicitações pull são bem-vindos no GitHub em https://github.com/philnash/pwned. Este projeto pretende ser um espaço seguro e acolhedor para colaboração, e espera-se que os contribuidores sigam o código de conduta do Contributor Covenant.
A gema está disponível como código aberto sob os termos da licença MIT.
Espera-se que todos que interagem nas bases de código, rastreadores de problemas, salas de bate-papo e listas de discussão do projeto Pwned sigam o código de conduta.