warden-hmac-authentication 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +15 -5
- data/bin/warden-hmac-authentication +12 -64
- data/lib/faraday/request/hmac.rb +1 -1
- data/lib/hmac/signer.rb +14 -1
- data/lib/hmac/strategies/base.rb +3 -3
- metadata +75 -24
data/README.md
CHANGED
@@ -11,7 +11,7 @@ The gem also provides a small helper class that can be used to generate request
|
|
11
11
|
## Header-Based authentication
|
12
12
|
|
13
13
|
The header-based authentication transports the authentication information in the (misnamed) `Authorization` HTTP-Header. The primary
|
14
|
-
advantage of header-based authentication is that request urls are stable even if authentication information changes.
|
14
|
+
advantage of header-based authentication is that request urls are stable even if authentication information changes. This improves
|
15
15
|
cacheability of the resource.
|
16
16
|
|
17
17
|
Header-based authentication is supported by the `:hmac_header` strategy.
|
@@ -138,9 +138,9 @@ No authentication attempt is made if the scheme name in the `Authorization` head
|
|
138
138
|
## Authentication Header Format
|
139
139
|
|
140
140
|
The format of the Authentication Header can be controlled using the `:auth_header_format` directive. The given format string will be interpolated
|
141
|
-
with all given options and the signature. The default value is `%{
|
141
|
+
with all given options and the signature. The default value is `%{auth_scheme} %{signature}` which will result in an auth header with a format such as `HMAC 539263f4f83878a4917d2f9c1521320c28b926a9`. The format string must contain at least the `scheme` and `signature` components.
|
142
142
|
|
143
|
-
The `:auth_header_format` directive has a companion directive, `:auth_header_parse` which must be a regular expression. Any given regular expression will be evaluated against the authorization header. The results can be retrieved using the `parsed_auth_header` method. The regular expression must at least contain a pattern named `scheme` and pattern named `signature`. The default value for this directive is a regular expression that is auto-generated by translating the `:auth_header_format` setting to a regular expression that contains a named capture group for each named part of the format string. Each capture allows for word characters, plus, dash, underscore and dot. The default :auth_header_format `%{
|
143
|
+
The `:auth_header_format` directive has a companion directive, `:auth_header_parse` which must be a regular expression. Any given regular expression will be evaluated against the authorization header. The results can be retrieved using the `parsed_auth_header` method. The regular expression must at least contain a pattern named `scheme` and pattern named `signature`. The default value for this directive is a regular expression that is auto-generated by translating the `:auth_header_format` setting to a regular expression that contains a named capture group for each named part of the format string. Each capture allows for word characters, plus, dash, underscore and dot. The default :auth_header_format `%{auth_scheme} %{signature}` will be translated to `/(?<auth_scheme>[-_+.\w]+) (?<signature>[-_+.\w]+)/`.
|
144
144
|
|
145
145
|
See the section about multiple authentication secrets for a use-case and a comprehensive example.
|
146
146
|
|
@@ -326,7 +326,7 @@ secrets allows us to implement multiple signing keys:
|
|
326
326
|
access_key_id = strategy.parsed_auth_header["access_key_id"]
|
327
327
|
keys[access_key_id]
|
328
328
|
},
|
329
|
-
:auth_header_format => '%{
|
329
|
+
:auth_header_format => '%{auth_scheme} %{access_key_id} %{signature}' }
|
330
330
|
end
|
331
331
|
|
332
332
|
This combination of settings uses a slightly different Format for the authorization header and transports the secret keys ID in the header of the form `HMAC KEY2 a59456da1f61f86e96622e283780f58b7428c892`
|
@@ -365,8 +365,18 @@ To simplify the generation of such urls, the `HMAC::Signer` accepts an `:extra_a
|
|
365
365
|
|
366
366
|
The library includes a faraday middleware that can be used to sign requests made with the faraday http lib. The middleware accepts the same list of options as the HMAC::Signer class.
|
367
367
|
|
368
|
+
### Example (query based)
|
369
|
+
|
370
|
+
Faraday.new(:url => "http://example.com") do |builder|
|
371
|
+
builder.use Faraday::Request::Hmac, secret, {:query_based => true, :extra_auth_params => {"access_key_id" => "KEY2"}}
|
372
|
+
builder.response :raise_error
|
373
|
+
builder.adapter :net_http
|
374
|
+
end
|
375
|
+
|
376
|
+
### Example (header based with custom scheme name)
|
377
|
+
|
368
378
|
Faraday.new(:url => "http://example.com") do |builder|
|
369
|
-
builder.use Faraday::Request::Hmac, secret, {:
|
379
|
+
builder.use Faraday::Request::Hmac, secret, {:auth_scheme => 'MYSCHEME', :auth_key => 'TESTKEYID', :auth_header_format => '%{auth_scheme} %{auth_key} %{signature}'}}
|
370
380
|
builder.response :raise_error
|
371
381
|
builder.adapter :net_http
|
372
382
|
end
|
@@ -1,68 +1,16 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'warden-hmac-authentication' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
2
8
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
puts ""
|
7
|
-
puts ""
|
8
|
-
puts "============= ERROR ================"
|
9
|
-
puts ""
|
10
|
-
puts "You need trollop installed or in your gemfile to use the signer"
|
11
|
-
puts ""
|
12
|
-
puts "============= ERROR ================"
|
13
|
-
puts ""
|
14
|
-
puts ""
|
15
|
-
exit(-1)
|
16
|
-
end
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
17
12
|
|
18
|
-
require '
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
version "warden-hmac-sign 0.3.0 (c) 2011 Felix Gilcher, Florian Gilcher"
|
23
|
-
banner <<-EOS
|
24
|
-
warden-hmac-authentication is used to create and validate signed urls for
|
25
|
-
usage with the HMAC authentication scheme used by
|
26
|
-
https://github.com/Asquera/warden-hmac-authentication
|
27
|
-
|
28
|
-
Usage:
|
29
|
-
warden-hmac-authentication [options] <command> url
|
30
|
-
|
31
|
-
where command is one of
|
32
|
-
|
33
|
-
sign: signs the given url
|
34
|
-
validate: validates the given url
|
35
|
-
|
36
|
-
and where [options] are:
|
37
|
-
|
38
|
-
EOS
|
39
|
-
|
40
|
-
opt :algorithm, "The hashing algorithm to use for the HMAC", :type => :string, :default => "sha1"
|
41
|
-
opt :secret, "The shared secret for the HMAC", :type => :string, :required => true
|
42
|
-
opt :"auth-param", "The name for the auth param in the url", :default => "auth"
|
43
|
-
opt :"date", "The date to use for the signature (defaults to now)"
|
44
|
-
end
|
45
|
-
|
46
|
-
cmd = ARGV.shift
|
47
|
-
Trollop::die "You must give a command" if cmd.nil?
|
48
|
-
Trollop::die "You command must be one of [sign, validate]" unless ["sign", "validate"].include? cmd
|
49
|
-
Trollop::die "You must provide a URL" if ARGV.empty?
|
50
|
-
url = ARGV.shift
|
51
|
-
|
52
|
-
secret = opts.delete(:secret)
|
53
|
-
algorithm = opts.delete(:algorithm)
|
54
|
-
|
55
|
-
signer = HMAC::Signer.new(algorithm)
|
56
|
-
|
57
|
-
if "sign" == cmd
|
58
|
-
puts signer.sign_url(url, secret, opts)
|
59
|
-
else
|
60
|
-
success = signer.validate_url_signature(url, secret, opts)
|
61
|
-
if success
|
62
|
-
puts "URL #{url} is valid"
|
63
|
-
exit 0
|
64
|
-
else
|
65
|
-
puts "URL #{url} does not contain a valid signature"
|
66
|
-
exit 1
|
67
|
-
end
|
68
|
-
end
|
16
|
+
load Gem.bin_path('warden-hmac-authentication', 'warden-hmac-authentication')
|
data/lib/faraday/request/hmac.rb
CHANGED
@@ -16,7 +16,7 @@ module Faraday
|
|
16
16
|
#
|
17
17
|
# @option options [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
|
18
18
|
# @option options [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
|
19
|
-
# @option options [Hash] :extra_auth_params ({}) Additional parameters to inject in the auth parameter
|
19
|
+
# @option options [Hash] :extra_auth_params ({}) Additional parameters to inject in the auth parameter. This parameter is ignored unless :query_based evaluates to true.
|
20
20
|
# @option options [String] :auth_header ('Authorization') The name of the authorization header to use
|
21
21
|
# @option options [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
|
22
22
|
# @option options [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
|
data/lib/hmac/signer.rb
CHANGED
@@ -105,7 +105,7 @@ module HMAC
|
|
105
105
|
#
|
106
106
|
# @return [Bool] true if the signature matches
|
107
107
|
def validate_signature(signature, params)
|
108
|
-
signature
|
108
|
+
compare_hashes(signature, generate_signature(params))
|
109
109
|
end
|
110
110
|
|
111
111
|
# convienience method to check the signature of a url with query-based authentication
|
@@ -268,5 +268,18 @@ module HMAC
|
|
268
268
|
url
|
269
269
|
end
|
270
270
|
|
271
|
+
private
|
272
|
+
|
273
|
+
# compares two hashes in a manner that's invulnerable to timing sidechannel attacks (see issue #16)
|
274
|
+
# by comparing them characterwise up to the end in all cases, no matter where the mismatch happens
|
275
|
+
# short circuits if the length does not match since this does not allow timing sidechannel attacks.
|
276
|
+
def compare_hashes(presented, computed)
|
277
|
+
if computed.length == presented.length then
|
278
|
+
computed.chars.zip(presented.chars).map {|x,y| x == y}.all?
|
279
|
+
else
|
280
|
+
false
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
271
284
|
end
|
272
285
|
end
|
data/lib/hmac/strategies/base.rb
CHANGED
@@ -188,15 +188,15 @@ module Warden
|
|
188
188
|
end
|
189
189
|
|
190
190
|
def ttl
|
191
|
-
config[:ttl].to_i
|
191
|
+
(config[:ttl] || 900).to_i
|
192
192
|
end
|
193
193
|
|
194
194
|
def check_ttl?
|
195
|
-
!config[:ttl].nil?
|
195
|
+
!config.has_key?(:ttl) || !config[:ttl].nil?
|
196
196
|
end
|
197
197
|
|
198
198
|
def timestamp
|
199
|
-
Time.strptime(request_timestamp, '%a, %e %b %Y %T %z') unless request_timestamp.nil?
|
199
|
+
Time.strptime(request_timestamp, '%a, %e %b %Y %T %z') unless request_timestamp.nil? || request_timestamp.empty?
|
200
200
|
end
|
201
201
|
|
202
202
|
def has_timestamp?
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: warden-hmac-authentication
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-07-09 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: addressable
|
17
|
-
requirement:
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,15 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
26
31
|
- !ruby/object:Gem::Dependency
|
27
32
|
name: rack
|
28
|
-
requirement:
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
29
34
|
none: false
|
30
35
|
requirements:
|
31
36
|
- - ! '>='
|
@@ -33,10 +38,15 @@ dependencies:
|
|
33
38
|
version: '0'
|
34
39
|
type: :runtime
|
35
40
|
prerelease: false
|
36
|
-
version_requirements:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: warden
|
39
|
-
requirement:
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ! '>='
|
@@ -44,10 +54,15 @@ dependencies:
|
|
44
54
|
version: '0'
|
45
55
|
type: :runtime
|
46
56
|
prerelease: false
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
48
63
|
- !ruby/object:Gem::Dependency
|
49
64
|
name: rake
|
50
|
-
requirement:
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
51
66
|
none: false
|
52
67
|
requirements:
|
53
68
|
- - ! '>='
|
@@ -55,10 +70,15 @@ dependencies:
|
|
55
70
|
version: '0'
|
56
71
|
type: :development
|
57
72
|
prerelease: false
|
58
|
-
version_requirements:
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
59
79
|
- !ruby/object:Gem::Dependency
|
60
80
|
name: rack-test
|
61
|
-
requirement:
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
62
82
|
none: false
|
63
83
|
requirements:
|
64
84
|
- - ! '>='
|
@@ -66,10 +86,15 @@ dependencies:
|
|
66
86
|
version: '0'
|
67
87
|
type: :development
|
68
88
|
prerelease: false
|
69
|
-
version_requirements:
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
70
95
|
- !ruby/object:Gem::Dependency
|
71
96
|
name: riot
|
72
|
-
requirement:
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
73
98
|
none: false
|
74
99
|
requirements:
|
75
100
|
- - ! '>='
|
@@ -77,10 +102,15 @@ dependencies:
|
|
77
102
|
version: '0'
|
78
103
|
type: :development
|
79
104
|
prerelease: false
|
80
|
-
version_requirements:
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
81
111
|
- !ruby/object:Gem::Dependency
|
82
112
|
name: timecop
|
83
|
-
requirement:
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
84
114
|
none: false
|
85
115
|
requirements:
|
86
116
|
- - ! '>='
|
@@ -88,10 +118,15 @@ dependencies:
|
|
88
118
|
version: '0'
|
89
119
|
type: :development
|
90
120
|
prerelease: false
|
91
|
-
version_requirements:
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
92
127
|
- !ruby/object:Gem::Dependency
|
93
128
|
name: simplecov
|
94
|
-
requirement:
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
95
130
|
none: false
|
96
131
|
requirements:
|
97
132
|
- - ! '>='
|
@@ -99,10 +134,15 @@ dependencies:
|
|
99
134
|
version: '0'
|
100
135
|
type: :development
|
101
136
|
prerelease: false
|
102
|
-
version_requirements:
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
103
143
|
- !ruby/object:Gem::Dependency
|
104
144
|
name: simplecov-html
|
105
|
-
requirement:
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
106
146
|
none: false
|
107
147
|
requirements:
|
108
148
|
- - ! '>='
|
@@ -110,10 +150,15 @@ dependencies:
|
|
110
150
|
version: '0'
|
111
151
|
type: :development
|
112
152
|
prerelease: false
|
113
|
-
version_requirements:
|
153
|
+
version_requirements: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
114
159
|
- !ruby/object:Gem::Dependency
|
115
160
|
name: trollop
|
116
|
-
requirement:
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
117
162
|
none: false
|
118
163
|
requirements:
|
119
164
|
- - ! '>='
|
@@ -121,7 +166,12 @@ dependencies:
|
|
121
166
|
version: '0'
|
122
167
|
type: :development
|
123
168
|
prerelease: false
|
124
|
-
version_requirements:
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
171
|
+
requirements:
|
172
|
+
- - ! '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
125
175
|
description: ! "This gem provides request authentication via [HMAC](http://en.wikipedia.org/wiki/Hmac).
|
126
176
|
The main usage is request based, noninteractive\n authentication for API implementations.
|
127
177
|
Two strategies are supported that differ mainly in how the authentication information
|
@@ -147,7 +197,8 @@ files:
|
|
147
197
|
- lib/hmac/strategies/header.rb
|
148
198
|
- lib/hmac/strategies/query.rb
|
149
199
|
- lib/hmac/string/jruby.rb
|
150
|
-
-
|
200
|
+
- !binary |-
|
201
|
+
YmluL3dhcmRlbi1obWFjLWF1dGhlbnRpY2F0aW9u
|
151
202
|
homepage: https://github.com/Asquera/warden-hmac-authentication
|
152
203
|
licenses: []
|
153
204
|
post_install_message:
|
@@ -168,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
219
|
version: '0'
|
169
220
|
requirements: []
|
170
221
|
rubyforge_project:
|
171
|
-
rubygems_version: 1.8.
|
222
|
+
rubygems_version: 1.8.24
|
172
223
|
signing_key:
|
173
224
|
specification_version: 3
|
174
225
|
summary: Provides request based, non-interactive authentication for APIs
|