Una forma sencilla en Ruby de utilizar la API de Pwned Passwords.
Documentos API | repositorio de GitHub
La API Pwned Passwords de Troy Hunt le permite comprobar si se ha encontrado una contraseña en alguna de las grandes violaciones de datos.
Pwned
es una biblioteca Ruby para utilizar el modelo k-Anonymity de la API Pwned Passwords para probar una contraseña con la API sin enviar la contraseña completa al servicio.
Los datos de esta API los proporciona Have I been pwned?. Antes de utilizar la API, verifique los usos aceptables y la licencia de la API.
Aquí hay una publicación de blog que escribí sobre cómo usar esta joya en sus aplicaciones Ruby para mejorar las contraseñas de sus usuarios.
Agregue esta línea al Gemfile de su aplicación:
gem 'pwned'
Y luego ejecuta:
$ bundle
O instálelo usted mismo como:
$ gem install pwned
Hay algunas maneras en que puedes usar esta gema:
Para probar una contraseña con la API, cree una instancia de un objeto Pwned::Password
y luego pregunte si está pwned?
.
password = Pwned :: Password . new ( "password" )
password . pwned?
#=> true
password . pwned_count
#=> 3303003
También puede comprobar cuántas veces aparece la contraseña en el conjunto de datos.
password = Pwned :: Password . new ( "password" )
password . pwned_count
#=> 3303003
Dado que probablemente esté utilizando esto como parte de un flujo de registro, se recomienda rescatar los errores para que, si el servicio falla, su recorrido como usuario no se vea afectado.
begin
password = Pwned :: Password . new ( "password" )
password . pwned?
rescue Pwned :: Error => e
# Ummm... don't worry about it, I guess?
end
La mayoría de las veces solo te importa si la contraseña se ha ingresado antes o no. Puede utilizar descriptores de acceso simplificados para verificar si la contraseña se ha ingresado o cuántas veces se ha ingresado:
Pwned . pwned? ( "password" )
#=> true
Pwned . pwned_count ( "password" )
#=> 3303003
Puede configurar las opciones de solicitud HTTP para usar con Net::HTTP.start
al realizar la solicitud a la API. Estas opciones están documentadas en la documentación Net::HTTP.start
.
Puedes pasar las opciones al constructor:
password = Pwned :: Password . new ( "password" , read_timeout : 10 )
También puede especificar valores predeterminados globales:
Pwned . default_request_options = { read_timeout : 10 }
La opción :headers
define encabezados HTTP. Estos encabezados deben ser claves de cadena.
password = Pwned :: Password . new ( "password" , headers : {
'User-Agent' => 'Super fun new user agent'
} )
Se puede configurar un proxy HTTP utilizando la variable de entorno http_proxy
o HTTP_PROXY
. Esta es la misma forma en que Net::HTTP
maneja los servidores proxy HTTP si no se proporcionan opciones de proxy. Consulte URI::Generic#find_proxy
para obtener detalles completos sobre cómo Ruby detecta un proxy del entorno.
# Set in the environment
ENV [ "http_proxy" ] = "https://username:[email protected]:12345"
# Will use the above proxy
password = Pwned :: Password . new ( "password" )
Puede especificar un proxy HTTP personalizado con la opción :proxy
:
password = Pwned :: Password . new (
"password" ,
proxy : "https://username:[email protected]:12345"
)
Si no desea configurar un proxy y no desea que se infiera un proxy del entorno, configure la clave :ignore_env_proxy
:
password = Pwned :: Password . new ( "password" , ignore_env_proxy : true )
Hay un validador personalizado disponible para sus modelos ActiveRecord:
class User < ApplicationRecord
validates :password , not_pwned : true
# or
validates :password , not_pwned : { message : "has been pwned %{count} times" }
end
Puede cambiar el mensaje de error usando I18n (use %{count}
para interpolar la cantidad de veces que se vio la contraseña en las violaciones de datos):
en :
errors :
messages :
not_pwned : has been pwned %{count} times
pwned_error : might be pwned
Si está de acuerdo con que la contraseña aparezca una cierta cantidad de veces antes de decidir que no es válida, puede establecer un umbral. El validador comprobará si pwned_count
es mayor que el umbral.
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 defecto, el registro se considerará válido cuando no podamos acceder a los servidores de haveibeenpwned.com. Esto se puede cambiar con el parámetro del 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
Puede configurar las solicitudes de red realizadas desde el validador usando :request_options
(consulte Net::HTTP.start para obtener la lista de opciones disponibles).
validates :password , not_pwned : {
request_options : {
read_timeout : 5 ,
open_timeout : 1
}
}
Estas opciones anulan las opciones predeterminadas definidas globalmente (ver arriba).
Además de estas opciones, también puede configurar lo siguiente:
Los encabezados HTTP se pueden especificar con la clave :headers
(por ejemplo, "User-Agent"
)
validates :password , not_pwned : {
request_options : {
headers : { "User-Agent" => "Super fun user agent" }
}
}
Se puede configurar un proxy HTTP utilizando la variable de entorno http_proxy
o HTTP_PROXY
. Esta es la misma forma en que Net::HTTP
maneja los servidores proxy HTTP si no se proporcionan opciones de proxy. Consulte URI::Generic#find_proxy
para obtener detalles completos sobre cómo Ruby detecta un proxy del entorno.
# Set in the environment
ENV [ "http_proxy" ] = "https://username:[email protected]:12345"
validates :password , not_pwned : true
Puede especificar un proxy HTTP personalizado con la clave :proxy
:
validates :password , not_pwned : {
request_options : {
proxy : "https://username:[email protected]:12345"
}
}
Si no desea configurar un proxy y no desea que se infiera un proxy del entorno, configure la clave :ignore_env_proxy
:
validates :password , not_pwned : {
request_options : {
ignore_env_proxy : true
}
}
Es posible que tenga un caso de uso para codificar la contraseña por adelantado y luego realizar la llamada a la API de Pwned Passwords más tarde (por ejemplo, si desea poner en cola un trabajo sin almacenar la contraseña en texto sin formato). Para hacer esto, puede hacer un hash de la contraseña con el método Pwned.hash_password
y luego inicializar la clase Pwned::HashedPassword
con el hash, así:
hashed_password = Pwned . hash_password ( password )
# some time later
Pwned :: HashedPassword . new ( hashed_password , request_options ) . pwned?
El constructor Pwned::HashedPassword
toma las mismas opciones que el constructor Pwned::Password
normal.
Si está utilizando Devise, le recomiendo que utilice la extensión devise-pwned_password que ahora funciona con esta gema.
Si está utilizando Rodauth, puede utilizar la función rodauth-pwned que funciona con esta gema.
La gema proporciona una utilidad de línea de comando para verificar contraseñas. Puedes llamarlo desde tu aplicación de terminal de esta manera:
$ pwned password
Pwned !
The password has been found in public breaches 3645804 times.
Si no desea que la contraseña que está verificando sea visible, llame al:
$ pwned --secret
Se le solicitará la contraseña, pero no se mostrará.
Para reducir las solicitudes de red innecesarias, el proyecto unpwn utiliza una lista del millón de contraseñas principales para compararlas. Solo si una contraseña no está incluida entre el millón principal, se compara con la API de Pwned Passwords.
@daz compartió un ejemplo fantástico del uso de esta gema para mostrar cuántas veces se han utilizado y filtrado los dígitos de Pi como contraseñas.
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
Los resultados pueden sorprenderle o no.
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
Después de revisar el repositorio, ejecute bin/setup
para instalar las dependencias. Luego, ejecute rake spec
para ejecutar las pruebas. También puede ejecutar bin/console
para obtener un mensaje interactivo que le permitirá experimentar.
Para instalar esta joya en su máquina local, ejecute bundle exec rake install
. Para lanzar una nueva versión, actualice el número de versión en version.rb
y luego ejecute bundle exec rake release
, que creará una etiqueta git para la versión, enviará confirmaciones y etiquetas de git y enviará el archivo .gem
a rubygems.org.
Los informes de errores y las solicitudes de extracción son bienvenidos en GitHub en https://github.com/philnash/pwned. Este proyecto pretende ser un espacio seguro y acogedor para la colaboración, y se espera que los contribuyentes cumplan con el código de conducta del Pacto de Colaboradores.
La gema está disponible como código abierto según los términos de la licencia MIT.
Se espera que todos los que interactúan en las bases de código, rastreadores de problemas, salas de chat y listas de correo del proyecto Pwned sigan el código de conducta.