warden-hmac-authentication 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|