api-auth 2.0.1 → 2.1.0

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: 92c46c40ca59ec931c02893f62155ea3efdfba48
4
- data.tar.gz: ac4c5514140b61f5df1b81408898413aef553cf7
3
+ metadata.gz: 9ee2a233ac1efcbf1af8b4028fde060ab2de42d5
4
+ data.tar.gz: 32dc7e2f7fb53759ff4d043e03f53914e4944345
5
5
  SHA512:
6
- metadata.gz: 38370d16d830ae325f862f972b20d267da8135750e923520df5902662cae46b4244a79d1520ec15b9ca4722f76018f88bfbb874bc4b64fd44f70b36f8402d018
7
- data.tar.gz: c3a16015aa11cd3da2b296d5729b3c7f0fccdea1e5a8005a98a4b7465e95944a951afdb9a474f182e1ff3df996d5d36e369f37f2cc60ed9e9eaf6c9e82c04985
6
+ metadata.gz: 5264828dfb7ec5743f4f74bb2b13ac6e8e002f1addfb448bb2e6ded240fe7ef13e0d1d2bc4646c3f568a33d25a32ff32ea4b070ce59c30d6431a8a451350cb2c
7
+ data.tar.gz: ffed44efe3245f7c74e9a6b76057ec1de088c16805d7f96fd10061d9babb940680e3b4927bc9aa480cd9aa8d03955c60c8fce1c6f596528f2bc4aaa9d2289c13
@@ -1,3 +1,9 @@
1
+ # 2.1.0 (2016-12-22)
2
+ - Fixed a NoMethodError that might occur when using the NetHttp Driver (#130 grahamkenville)
3
+ - More securely compare signatures in a way that prevents timing attacks (#56 leishman, #133 will0)
4
+ - Remove support for MD2 and MD4 hashing algorithms since they are insecure (#134 will0)
5
+ - Disallow requests that are too far in the future to limit the time available for a brute force signature guess (#119 fwininger)
6
+
1
7
  # 2.0.1 (2016-07-25)
2
8
  - Support of `api_auth_options` in ActiveResource integration (#102 fwininger)
3
9
  - Replace use of `#blank?` with `#nil?` to not depend on ActiveSupport (#114 packrat386)
data/README.md CHANGED
@@ -72,6 +72,7 @@ Here is the current list of supported request objects:
72
72
  * Curb (Curl::Easy)
73
73
  * RestClient
74
74
  * Faraday
75
+ * Httpi
75
76
 
76
77
  ### HTTP Client Objects
77
78
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.1
1
+ 2.1.0
@@ -20,7 +20,8 @@ Gem::Specification.new do |s|
20
20
  s.add_development_dependency 'rest-client', '~> 1.6.0'
21
21
  s.add_development_dependency 'curb', '~> 0.8.1'
22
22
  s.add_development_dependency 'httpi'
23
- s.add_development_dependency 'faraday'
23
+ faraday_version = RUBY_VERSION == '1.8.7' ? '< 0.10' : '>= 0.10'
24
+ s.add_development_dependency 'faraday', faraday_version
24
25
  s.add_development_dependency 'multipart-post', '~> 2.0'
25
26
 
26
27
  s.files = `git ls-files`.split("\n")
@@ -41,7 +41,7 @@ module ApiAuth
41
41
  false
42
42
  elsif !signatures_match?(headers, secret_key, options)
43
43
  false
44
- elsif request_too_old?(headers)
44
+ elsif !request_within_time_window?(headers)
45
45
  false
46
46
  else
47
47
  true
@@ -69,14 +69,15 @@ module ApiAuth
69
69
 
70
70
  private
71
71
 
72
- AUTH_HEADER_PATTERN = /APIAuth(?:-HMAC-(MD[245]|SHA(?:1|224|256|384|512)?))? ([^:]+):(.+)$/
72
+ AUTH_HEADER_PATTERN = /APIAuth(?:-HMAC-(MD5|SHA(?:1|224|256|384|512)?))? ([^:]+):(.+)$/
73
73
 
74
- def request_too_old?(headers)
74
+ def request_within_time_window?(headers)
75
75
  # 900 seconds is 15 minutes
76
76
 
77
- Time.httpdate(headers.timestamp).utc < (Time.now.utc - 900)
77
+ Time.httpdate(headers.timestamp).utc > (Time.now.utc - 900) &&
78
+ Time.httpdate(headers.timestamp).utc < (Time.now.utc + 900)
78
79
  rescue ArgumentError
79
- true
80
+ false
80
81
  end
81
82
 
82
83
  def signatures_match?(headers, secret_key, options)
@@ -91,7 +92,16 @@ module ApiAuth
91
92
  header_sig = match_data[3]
92
93
  calculated_sig = hmac_signature(headers, secret_key, options)
93
94
 
94
- header_sig == calculated_sig
95
+ secure_equals?(header_sig, calculated_sig, secret_key)
96
+ end
97
+
98
+ def secure_equals?(m1, m2, key)
99
+ sha1_hmac(key, m1) == sha1_hmac(key, m2)
100
+ end
101
+
102
+ def sha1_hmac(key, message)
103
+ digest = OpenSSL::Digest.new('sha1')
104
+ OpenSSL::HMAC.digest(digest, key, message)
95
105
  end
96
106
 
97
107
  def hmac_signature(headers, secret_key, options)
@@ -1,3 +1,4 @@
1
+ require 'time'
1
2
  module ApiAuth
2
3
  module RequestDrivers # :nodoc:
3
4
  class NetHttpRequest # :nodoc:
@@ -75,37 +75,44 @@ describe 'ApiAuth' do
75
75
 
76
76
  describe '.authentic?' do
77
77
  let(:request) do
78
- new_request = Net::HTTP::Put.new('/resource.xml?foo=bar&bar=foo',
79
- 'content-type' => 'text/plain',
80
- 'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
81
- 'date' => Time.now.utc.httpdate)
78
+ Net::HTTP::Put.new('/resource.xml?foo=bar&bar=foo',
79
+ 'content-type' => 'text/plain',
80
+ 'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
81
+ 'date' => Time.now.utc.httpdate)
82
+ end
82
83
 
83
- signature = hmac('123', new_request)
84
- new_request['Authorization'] = "APIAuth 1044:#{signature}"
85
- new_request
84
+ let(:signed_request) do
85
+ signature = hmac('123', request)
86
+ request['Authorization'] = "APIAuth 1044:#{signature}"
87
+ request
86
88
  end
87
89
 
88
90
  it 'validates that the signature in the request header matches the way we sign it' do
89
- expect(ApiAuth.authentic?(request, '123')).to eq true
91
+ expect(ApiAuth.authentic?(signed_request, '123')).to eq true
90
92
  end
91
93
 
92
94
  it 'fails to validate a non matching signature' do
93
- expect(ApiAuth.authentic?(request, '456')).to eq false
95
+ expect(ApiAuth.authentic?(signed_request, '456')).to eq false
94
96
  end
95
97
 
96
98
  it 'fails to validate non matching md5' do
97
99
  request['content-md5'] = '12345'
98
- expect(ApiAuth.authentic?(request, '123')).to eq false
100
+ expect(ApiAuth.authentic?(signed_request, '123')).to eq false
99
101
  end
100
102
 
101
103
  it 'fails to validate expired requests' do
102
104
  request['date'] = 16.minutes.ago.utc.httpdate
103
- expect(ApiAuth.authentic?(request, '123')).to eq false
105
+ expect(ApiAuth.authentic?(signed_request, '123')).to eq false
106
+ end
107
+
108
+ it 'fails to validate far future requests' do
109
+ request['date'] = 16.minutes.from_now.utc.httpdate
110
+ expect(ApiAuth.authentic?(signed_request, '123')).to eq false
104
111
  end
105
112
 
106
113
  it 'fails to validate if the date is invalid' do
107
114
  request['date'] = '٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠'
108
- expect(ApiAuth.authentic?(request, '123')).to eq false
115
+ expect(ApiAuth.authentic?(signed_request, '123')).to eq false
109
116
  end
110
117
 
111
118
  it 'fails to validate if the request method differs' do
@@ -73,7 +73,7 @@ describe 'Rails integration' do
73
73
  expect(response.code).to eq('200')
74
74
  end
75
75
 
76
- it 'should forbid a request with properly signed headers but timestamp > 15 minutes' do
76
+ it 'should forbid a request with properly signed headers but timestamp > 15 minutes ago' do
77
77
  request = if ActionController::TestRequest.respond_to?(:create)
78
78
  ActionController::TestRequest.create
79
79
  else
@@ -85,6 +85,18 @@ describe 'Rails integration' do
85
85
  expect(response.code).to eq('401')
86
86
  end
87
87
 
88
+ it 'should forbid a request with properly signed headers but timestamp > 15 minutes in the future' do
89
+ request = if ActionController::TestRequest.respond_to?(:create)
90
+ ActionController::TestRequest.create
91
+ else
92
+ ActionController::TestRequest.new
93
+ end
94
+ request.env['DATE'] = 'Mon, 23 Jan 2100 03:29:56 GMT'
95
+ ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
96
+ response = generated_response(request, :index)
97
+ expect(response.code).to eq('401')
98
+ end
99
+
88
100
  it "should insert a DATE header in the request when one hasn't been specified" do
89
101
  request = if ActionController::TestRequest.respond_to?(:create)
90
102
  ActionController::TestRequest.create
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mauricio Gomes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-26 00:00:00.000000000 Z
11
+ date: 2016-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -168,14 +168,14 @@ dependencies:
168
168
  requirements:
169
169
  - - ">="
170
170
  - !ruby/object:Gem::Version
171
- version: '0'
171
+ version: '0.10'
172
172
  type: :development
173
173
  prerelease: false
174
174
  version_requirements: !ruby/object:Gem::Requirement
175
175
  requirements:
176
176
  - - ">="
177
177
  - !ruby/object:Gem::Version
178
- version: '0'
178
+ version: '0.10'
179
179
  - !ruby/object:Gem::Dependency
180
180
  name: multipart-post
181
181
  requirement: !ruby/object:Gem::Requirement
@@ -265,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
265
265
  version: '0'
266
266
  requirements: []
267
267
  rubyforge_project:
268
- rubygems_version: 2.5.1
268
+ rubygems_version: 2.5.2
269
269
  signing_key:
270
270
  specification_version: 4
271
271
  summary: Simple HMAC authentication for your APIs