devise-pwned_password 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d25ddf45c7eddaf3e5334a1fc1dde9dfd0c1299
4
- data.tar.gz: 105d467a316fd317e76abb0226b0debecac123cc
3
+ metadata.gz: 9ef262f2244c9c92bd96259982b7fdcdf9b69118
4
+ data.tar.gz: c409c259481057b56aea0773b49cbe4b192a8847
5
5
  SHA512:
6
- metadata.gz: cdad317bb782d686f9f0734dd5d519b0d1b0329e89d272466fe57ea9737d890b58f8a2e99951fa1c55af34088697ee0a0bb54796e9eb60c646048fbb7f8821dc
7
- data.tar.gz: 005647a67db66a184ca7c53280c1743b985b15ca3a4968700de3c7e8aa549767d4880462219d7c02624f09887d58a299956f088c9399584fe9808ccf80b3f9d1
6
+ metadata.gz: 927ccc527b90c8b04e84e4a0d727a83a06ac97b715390c80a12cd511fd9d3645be3aec4ddc7dcc8c2507503a0a9cef60ae9871cd5a4d408727bcc30eb41d7760
7
+ data.tar.gz: 4eafd2132c7ceb6727e0efd5d059ceb3640988749e11fc471490f4c97efb8bfea4951b2b04dada6c1734e3d1dc7e54790c647d78838fbea0c91df92880fb1e24
data/README.md CHANGED
@@ -16,13 +16,24 @@ class AdminUser < ApplicationRecord
16
16
  end
17
17
  ```
18
18
 
19
-
20
- Users will receive the following error message if they use a password from the PwnedPasswords dataset
19
+ Users will receive the following error message if they use a password from the
20
+ PwnedPasswords dataset:
21
21
 
22
22
  ```
23
23
  This password has previously appeared in a data breach and should never be used. Please choose something harder to guess.
24
24
  ```
25
25
 
26
+ By default passwords are rejected if they appear at all in the data set.
27
+ Optionally, you can add the following snippet to `config/initializers/devise.rb`
28
+ if you want the error message to be displayed only when the password is present
29
+ a certain number of times in the data set:
30
+
31
+ ```ruby
32
+ # Minimum number of times a pwned password must exist in the data set in order
33
+ # to be reject.
34
+ config.min_password_matches = 10
35
+ ```
36
+
26
37
  ## Installation
27
38
  Add this line to your application's Gemfile:
28
39
 
@@ -35,6 +46,22 @@ And then execute:
35
46
  $ bundle install
36
47
  ```
37
48
 
49
+
50
+ ## Considerations
51
+
52
+ A few things to consider/understand when using this gem:
53
+
54
+ * User passwords are hashed using SHA-1 and then truncated to 5 characters,
55
+ implementing the k-Anonymity model described in
56
+ https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange
57
+ Neither the clear-text password nor the full password hash is ever transmitted
58
+ to a third party. More implementation details and important caveats can be
59
+ found in https://blog.cloudflare.com/validating-leaked-passwords-with-k-anonymity/
60
+
61
+ * This puts an external API in the request path of users signing up to your
62
+ application. This could potentially add some latency to this operation. The
63
+ gem is designed to fail silently if the PwnedPasswords service is unavailable.
64
+
38
65
  ## Contributing
39
66
 
40
67
  To contribute
data/Rakefile CHANGED
@@ -1,17 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
- require 'bundler/setup'
4
+ require "bundler/setup"
5
+ require 'bundler/gem_tasks'
3
6
  rescue LoadError
4
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
8
  end
6
9
 
7
- require 'rdoc/task'
10
+ require "rdoc/task"
8
11
 
9
12
  RDoc::Task.new(:rdoc) do |rdoc|
10
- rdoc.rdoc_dir = 'rdoc'
11
- rdoc.title = 'Devise::PwnedPassword'
12
- rdoc.options << '--line-numbers'
13
- rdoc.rdoc_files.include('README.md')
14
- rdoc.rdoc_files.include('lib/**/*.rb')
13
+ rdoc.rdoc_dir = "rdoc"
14
+ rdoc.title = "Devise::PwnedPassword"
15
+ rdoc.options << "--line-numbers"
16
+ rdoc.rdoc_files.include("README.md")
17
+ rdoc.rdoc_files.include("lib/**/*.rb")
15
18
  end
16
19
 
17
20
 
@@ -19,13 +22,13 @@ end
19
22
 
20
23
 
21
24
 
22
- require 'bundler/gem_tasks'
25
+ require "bundler/gem_tasks"
23
26
 
24
- require 'rake/testtask'
27
+ require "rake/testtask"
25
28
 
26
29
  Rake::TestTask.new(:test) do |t|
27
- t.libs << 'test'
28
- t.pattern = 'test/**/*_test.rb'
30
+ t.libs << "test"
31
+ t.pattern = "test/**/*_test.rb"
29
32
  t.verbose = false
30
33
  end
31
34
 
@@ -1,4 +1,6 @@
1
- require 'net/http'
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
2
4
 
3
5
  module Devise
4
6
  module Models
@@ -14,49 +16,49 @@ module Devise
14
16
  validate :not_pwned_password
15
17
  end
16
18
 
17
- private
19
+ module ClassMethods
20
+ Devise::Models.config(self, :min_password_matches)
21
+ end
18
22
 
23
+ private
19
24
 
20
- # Returns true if password is present in the PwnedPasswords dataset
21
- # Implement retry behaviour described here https://haveibeenpwned.com/API/v2#RateLimiting
22
- def is_password_pwned(password)
25
+ def usage_count(response, suffix)
26
+ count = 0
27
+ response.each_line do |line|
28
+ if line.start_with? suffix
29
+ count = line.strip.split(":").last.to_i
30
+ break
31
+ end
32
+ end
33
+ count
34
+ end
23
35
 
24
- sha1Hash = Digest::SHA1.hexdigest password
36
+ # Returns true if password is present in the PwnedPasswords dataset
37
+ # Implement retry behaviour described here https://haveibeenpwned.com/API/v2#RateLimiting
38
+ def password_pwned?(password)
39
+ hash = Digest::SHA1.hexdigest(password).upcase
40
+ prefix, suffix = hash.slice!(0..4), hash
25
41
 
26
- userAgent = "devise_pwned_password"
42
+ userAgent = "devise_pwned_password"
27
43
 
28
- uri = URI.parse("https://haveibeenpwned.com/api/v2/pwnedpassword/#{sha1Hash}")
44
+ uri = URI.parse("https://api.pwnedpasswords.com/range/#{prefix}")
29
45
 
30
- Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
31
- request = Net::HTTP::Get.new(uri.request_uri, {'User-Agent' => userAgent})
32
- 3.times {
46
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
47
+ request = Net::HTTP::Get.new(uri.request_uri, "User-Agent" => userAgent)
33
48
  response = http.request request
34
- if response.code != '429'
35
- return response.code == '200'
36
- end
49
+ return usage_count(response.read_body, suffix) >= self.class.min_password_matches
50
+ end
37
51
 
38
- retryAfter = response.get_fields('Retry-After')[0].to_i
39
-
40
- if retryAfter > 10
41
- #Exit early if the throttling is too high
42
- return false
43
- end
44
-
45
- sleep retryAfter
46
- }
52
+ false
47
53
  end
48
54
 
49
- return false
50
- end
51
-
52
- def not_pwned_password
53
-
54
- #This deliberately fails silently on 500's etc. Most apps wont want to tie the ability to sign up customers to the availability of a third party API
55
- if is_password_pwned(password)
56
- # Error message taken from https://haveibeenpwned.com/Passwords
57
- errors.add(:password, "This password has previously appeared in a data breach and should never be used. Please choose something harder to guess.")
55
+ def not_pwned_password
56
+ # This deliberately fails silently on 500's etc. Most apps wont want to tie the ability to sign up customers to the availability of a third party API
57
+ if password_pwned?(password)
58
+ # Error message taken from https://haveibeenpwned.com/Passwords
59
+ errors.add(:password, "This password has previously appeared in a data breach and should never be used. Please choose something harder to guess.")
60
+ end
58
61
  end
59
- end
60
62
  end
61
63
  end
62
- end
64
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module PwnedPassword
3
- VERSION = '0.1.1'
5
+ VERSION = "0.1.2"
4
6
  end
5
7
  end
@@ -1,9 +1,14 @@
1
- require 'devise'
2
- require 'devise/pwned_password/model'
1
+ # frozen_string_literal: true
2
+
3
+ require "devise"
4
+ require "devise/pwned_password/model"
3
5
 
4
6
  module Devise
7
+ mattr_accessor :min_password_matches
8
+ @@min_password_matches = 1
9
+
5
10
  module PwnedPassword
6
11
  end
7
12
  end
8
13
 
9
- Devise.add_module :pwned_password, model: "devise_pwned_password/model"
14
+ Devise.add_module :pwned_password, model: "devise_pwned_password/model"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # desc "Explaining what the task does"
2
3
  # task :devise_pwned_password do
3
4
  # # Task goes here
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-pwned_password
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Banfield
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-05 00:00:00.000000000 Z
11
+ date: 2018-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.52.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.52.1
55
69
  description: Devise extension that checks user passwords against the PwnedPasswords
56
70
  dataset https://haveibeenpwned.com/Passwords.
57
71
  email:
@@ -87,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
101
  version: '0'
88
102
  requirements: []
89
103
  rubyforge_project:
90
- rubygems_version: 2.6.12
104
+ rubygems_version: 2.5.2
91
105
  signing_key:
92
106
  specification_version: 4
93
107
  summary: Devise extension that checks user passwords against the PwnedPasswords dataset.