http_signature 0.0.7 → 1.0.0
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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +32 -0
- data/.github/workflows/standardrb.yml +15 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -1
- data/Gemfile +3 -2
- data/Gemfile.lock +143 -10
- data/README.md +102 -110
- data/Rakefile +3 -3
- data/bin/standardrb +16 -0
- data/http_signature.gemspec +26 -17
- data/lib/http_signature/faraday.rb +11 -9
- data/lib/http_signature/rack.rb +30 -47
- data/lib/http_signature/rails.rb +67 -0
- data/lib/http_signature/version.rb +5 -0
- data/lib/http_signature.rb +351 -137
- metadata +81 -11
- data/.circleci/config.yml +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0d4b1e73dff1838a018f5e0d5bef1c6a6a14c2b8ef552dbbfd0481ac85cb03b7
|
|
4
|
+
data.tar.gz: 1a087ded86d8f84213984d9bd56fb55462f65feb9abc876514249f12fc5166ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7796499163d70782ea0fd809316c39a4d2a933cf01cc88f6daaa88d673607e9a138643d666fc5080063ea0bdb1068d32c48642fb487c59d6edcb7626fba7ad8e
|
|
7
|
+
data.tar.gz: 1603d71ad33fc087e456949de3e1534f3cf08c894a3fe10a3f456f6751409ddbbda247a53511d201866cf10f629e2c676fbe2eaa7cb0e0535dae81a2cad6e2a2
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Set up Ruby
|
|
16
|
+
uses: ruby/setup-ruby@v1
|
|
17
|
+
with:
|
|
18
|
+
ruby-version: '3.4'
|
|
19
|
+
bundler-cache: true
|
|
20
|
+
|
|
21
|
+
- name: Run tests with coverage
|
|
22
|
+
env:
|
|
23
|
+
COVERAGE: "true"
|
|
24
|
+
run: bundle exec rake test
|
|
25
|
+
|
|
26
|
+
- name: Upload coverage artifact
|
|
27
|
+
uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: coverage-html
|
|
30
|
+
path: coverage/
|
|
31
|
+
if-no-files-found: warn
|
|
32
|
+
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3.4.8
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,26 +1,159 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
http_signature (0.0
|
|
4
|
+
http_signature (1.0.0)
|
|
5
|
+
base64
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
8
9
|
specs:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
actionpack (8.1.1)
|
|
11
|
+
actionview (= 8.1.1)
|
|
12
|
+
activesupport (= 8.1.1)
|
|
13
|
+
nokogiri (>= 1.8.5)
|
|
14
|
+
rack (>= 2.2.4)
|
|
15
|
+
rack-session (>= 1.0.1)
|
|
16
|
+
rack-test (>= 0.6.3)
|
|
17
|
+
rails-dom-testing (~> 2.2)
|
|
18
|
+
rails-html-sanitizer (~> 1.6)
|
|
19
|
+
useragent (~> 0.16)
|
|
20
|
+
actionview (8.1.1)
|
|
21
|
+
activesupport (= 8.1.1)
|
|
22
|
+
builder (~> 3.1)
|
|
23
|
+
erubi (~> 1.11)
|
|
24
|
+
rails-dom-testing (~> 2.2)
|
|
25
|
+
rails-html-sanitizer (~> 1.6)
|
|
26
|
+
activesupport (8.1.1)
|
|
27
|
+
base64
|
|
28
|
+
bigdecimal
|
|
29
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
30
|
+
connection_pool (>= 2.2.5)
|
|
31
|
+
drb
|
|
32
|
+
i18n (>= 1.6, < 2)
|
|
33
|
+
json
|
|
34
|
+
logger (>= 1.4.2)
|
|
35
|
+
minitest (>= 5.1)
|
|
36
|
+
securerandom (>= 0.3)
|
|
37
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
38
|
+
uri (>= 0.13.1)
|
|
39
|
+
ast (2.4.3)
|
|
40
|
+
base64 (0.3.0)
|
|
41
|
+
bigdecimal (4.0.1)
|
|
42
|
+
builder (3.3.0)
|
|
43
|
+
concurrent-ruby (1.3.6)
|
|
44
|
+
connection_pool (3.0.2)
|
|
45
|
+
crass (1.0.6)
|
|
46
|
+
docile (1.4.1)
|
|
47
|
+
drb (2.2.3)
|
|
48
|
+
erubi (1.13.1)
|
|
49
|
+
faraday (2.14.0)
|
|
50
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
51
|
+
json
|
|
52
|
+
logger
|
|
53
|
+
faraday-net_http (3.4.2)
|
|
54
|
+
net-http (~> 0.5)
|
|
55
|
+
i18n (1.14.8)
|
|
56
|
+
concurrent-ruby (~> 1.0)
|
|
57
|
+
json (2.18.0)
|
|
58
|
+
language_server-protocol (3.17.0.5)
|
|
59
|
+
lint_roller (1.1.0)
|
|
60
|
+
logger (1.7.0)
|
|
61
|
+
loofah (2.25.0)
|
|
62
|
+
crass (~> 1.0.2)
|
|
63
|
+
nokogiri (>= 1.12.0)
|
|
64
|
+
minitest (6.0.0)
|
|
65
|
+
prism (~> 1.5)
|
|
66
|
+
net-http (0.9.1)
|
|
67
|
+
uri (>= 0.11.1)
|
|
68
|
+
nokogiri (1.19.0-arm64-darwin)
|
|
69
|
+
racc (~> 1.4)
|
|
70
|
+
nokogiri (1.19.0-x86_64-linux-gnu)
|
|
71
|
+
racc (~> 1.4)
|
|
72
|
+
parallel (1.27.0)
|
|
73
|
+
parser (3.3.10.0)
|
|
74
|
+
ast (~> 2.4.1)
|
|
75
|
+
racc
|
|
76
|
+
prism (1.7.0)
|
|
77
|
+
racc (1.8.1)
|
|
78
|
+
rack (3.2.4)
|
|
79
|
+
rack-session (2.1.1)
|
|
80
|
+
base64 (>= 0.1.0)
|
|
81
|
+
rack (>= 3.0.0)
|
|
82
|
+
rack-test (2.2.0)
|
|
83
|
+
rack (>= 1.3)
|
|
84
|
+
rails-dom-testing (2.3.0)
|
|
85
|
+
activesupport (>= 5.0.0)
|
|
86
|
+
minitest
|
|
87
|
+
nokogiri (>= 1.6)
|
|
88
|
+
rails-html-sanitizer (1.6.2)
|
|
89
|
+
loofah (~> 2.21)
|
|
90
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
91
|
+
rainbow (3.1.1)
|
|
92
|
+
rake (13.3.1)
|
|
93
|
+
regexp_parser (2.11.3)
|
|
94
|
+
rubocop (1.81.7)
|
|
95
|
+
json (~> 2.3)
|
|
96
|
+
language_server-protocol (~> 3.17.0.2)
|
|
97
|
+
lint_roller (~> 1.1.0)
|
|
98
|
+
parallel (~> 1.10)
|
|
99
|
+
parser (>= 3.3.0.2)
|
|
100
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
101
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
102
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
103
|
+
ruby-progressbar (~> 1.7)
|
|
104
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
105
|
+
rubocop-ast (1.49.0)
|
|
106
|
+
parser (>= 3.3.7.2)
|
|
107
|
+
prism (~> 1.7)
|
|
108
|
+
rubocop-performance (1.26.1)
|
|
109
|
+
lint_roller (~> 1.1)
|
|
110
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
111
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
112
|
+
ruby-progressbar (1.13.0)
|
|
113
|
+
securerandom (0.4.1)
|
|
114
|
+
simplecov (0.22.0)
|
|
115
|
+
docile (~> 1.1)
|
|
116
|
+
simplecov-html (~> 0.11)
|
|
117
|
+
simplecov_json_formatter (~> 0.1)
|
|
118
|
+
simplecov-html (0.13.2)
|
|
119
|
+
simplecov_json_formatter (0.1.4)
|
|
120
|
+
standard (1.52.0)
|
|
121
|
+
language_server-protocol (~> 3.17.0.2)
|
|
122
|
+
lint_roller (~> 1.0)
|
|
123
|
+
rubocop (~> 1.81.7)
|
|
124
|
+
standard-custom (~> 1.0.0)
|
|
125
|
+
standard-performance (~> 1.8)
|
|
126
|
+
standard-custom (1.0.2)
|
|
127
|
+
lint_roller (~> 1.0)
|
|
128
|
+
rubocop (~> 1.50)
|
|
129
|
+
standard-performance (1.9.0)
|
|
130
|
+
lint_roller (~> 1.1)
|
|
131
|
+
rubocop-performance (~> 1.26.0)
|
|
132
|
+
tzinfo (2.0.6)
|
|
133
|
+
concurrent-ruby (~> 1.0)
|
|
134
|
+
unicode-display_width (3.2.0)
|
|
135
|
+
unicode-emoji (~> 4.1)
|
|
136
|
+
unicode-emoji (4.2.0)
|
|
137
|
+
uri (1.1.1)
|
|
138
|
+
useragent (0.16.11)
|
|
14
139
|
|
|
15
140
|
PLATFORMS
|
|
16
|
-
|
|
141
|
+
arm64-darwin-25
|
|
142
|
+
x86_64-linux
|
|
17
143
|
|
|
18
144
|
DEPENDENCIES
|
|
145
|
+
actionpack (>= 6.1)
|
|
19
146
|
bundler
|
|
20
|
-
faraday
|
|
147
|
+
faraday (>= 2.7)
|
|
21
148
|
http_signature!
|
|
22
|
-
minitest
|
|
149
|
+
minitest (>= 5.24)
|
|
150
|
+
rack
|
|
23
151
|
rake
|
|
152
|
+
simplecov
|
|
153
|
+
standard
|
|
154
|
+
|
|
155
|
+
RUBY VERSION
|
|
156
|
+
ruby 3.4.8
|
|
24
157
|
|
|
25
158
|
BUNDLED WITH
|
|
26
|
-
|
|
159
|
+
4.0.2
|
data/README.md
CHANGED
|
@@ -1,162 +1,156 @@
|
|
|
1
1
|
# HTTP Signature
|
|
2
|
-
[](https://circleci.com/gh/bolmaster2/http-signature)
|
|
3
2
|
|
|
4
|
-
Create and validate HTTP
|
|
3
|
+
Create and validate HTTP Message Signatures per [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) using the `Signature-Input` and `Signature` headers.
|
|
5
4
|
|
|
6
|
-
Aims to only implement the creation and validation of
|
|
7
|
-
The idea is to implement adapters to popular http libraries to make it easy to use.
|
|
5
|
+
Aims to only implement the creation and validation of signatures without any external dependencies. Adapters are provided for common HTTP libraries.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
__NOTE__: RFC 9421 signs components via two headers:
|
|
8
|
+
```
|
|
9
|
+
Signature-Input: sig1=("@method" "@authority" "@target-uri" "date");created=...
|
|
10
|
+
Signature: sig1=:BASE64_SIGNATURE_BYTES:
|
|
10
11
|
```
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```shell
|
|
11
16
|
gem install http_signature
|
|
12
17
|
```
|
|
13
18
|
|
|
14
19
|
## Usage
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
### Create signature
|
|
22
|
+
|
|
23
|
+
`HTTPSignature.create` returns both `Signature-Input` and `Signature` headers that you can include in your request.
|
|
24
|
+
|
|
19
25
|
|
|
20
|
-
### Basic
|
|
21
|
-
The most basic usage without any extra headers. The default algorithm is `hmac-sha256`. This create the `Signature` header value. Next step is to add the value to the header and 💥 you're done!
|
|
22
26
|
```ruby
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
headers = { 'date' => 'Tue, 20 Apr 2021 02:07:55 GMT' }
|
|
28
|
+
|
|
29
|
+
sig_headers = HTTPSignature.create(
|
|
30
|
+
url: 'https://example.com/foo?pet=dog',
|
|
31
|
+
method: :get,
|
|
32
|
+
headers: headers,
|
|
25
33
|
key_id: 'Test',
|
|
26
|
-
key: 'secret
|
|
34
|
+
key: 'secret',
|
|
35
|
+
covered_components: %w[@method @target-uri date],
|
|
27
36
|
)
|
|
28
|
-
|
|
37
|
+
|
|
38
|
+
request['Signature-Input'] = sig_headers['Signature-Input']
|
|
39
|
+
request['Signature'] = sig_headers['Signature']
|
|
29
40
|
```
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
|
|
43
|
+
### Validate signature
|
|
44
|
+
|
|
45
|
+
Call `valid?` with the incoming request headers (including `Signature-Input` and `Signature`)
|
|
35
46
|
|
|
36
47
|
```ruby
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
body = '{"hello": "world"}'
|
|
43
|
-
|
|
44
|
-
headers = {
|
|
45
|
-
'date': 'Thu, 05 Jan 2014 21:31:40 GMT',
|
|
46
|
-
'content-type': 'application/json',
|
|
47
|
-
'digest': HTTPSignature.create_digest(body),
|
|
48
|
-
'content-length': body.length
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
HTTPSignature.create(
|
|
52
|
-
url: 'https://example.com/foo',
|
|
53
|
-
method: :post,
|
|
54
|
-
query_string_params: params,
|
|
48
|
+
HTTPSignature.valid?(
|
|
49
|
+
url: "https://example.com/foo",
|
|
50
|
+
method: :get,
|
|
55
51
|
headers: headers,
|
|
56
|
-
|
|
57
|
-
algorithm: 'rsa-sha256',
|
|
58
|
-
key: File.read('key.pem'),
|
|
59
|
-
body: body
|
|
52
|
+
key: "secret"
|
|
60
53
|
)
|
|
61
54
|
```
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
## Outgoing request examples
|
|
57
|
+
|
|
58
|
+
### NET::HTTP
|
|
65
59
|
|
|
66
60
|
```ruby
|
|
67
|
-
|
|
61
|
+
require 'net/http'
|
|
62
|
+
require 'http_signature'
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
url: 'https://example.com/foo',
|
|
71
|
-
key_id: 'Test',
|
|
72
|
-
key: 'secret 🙈',
|
|
73
|
-
body: body
|
|
74
|
-
)
|
|
75
|
-
# 'keyId="Test",algorithm="hmac-sha256",headers="(request-target) digest",signature="3Jm5jnCSKX3fYLd58RqRdafZKeuSbUEPhn7grCGx4vg="'
|
|
76
|
-
```
|
|
64
|
+
uri = URI('http://example.com/hello')
|
|
77
65
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
check out, because you need the private key to do that and because the one validating
|
|
81
|
-
the signature should only have access to the public key, you need to validate it with that.
|
|
66
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
67
|
+
request = Net::HTTP::Get.new(uri)
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
{"hello": "world"}
|
|
94
|
-
```
|
|
69
|
+
sig_headers = HTTPSignature.create(
|
|
70
|
+
url: request.uri,
|
|
71
|
+
method: request.method,
|
|
72
|
+
headers: request.each_header.map { |k, v| [k, v] }.to_h,
|
|
73
|
+
key: 'MYSECRETKEY',
|
|
74
|
+
key_id: 'KEY_1',
|
|
75
|
+
algorithm: 'hmac-sha256',
|
|
76
|
+
body: request.body ? request.body : ''
|
|
77
|
+
)
|
|
95
78
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
headers: request.headers,
|
|
102
|
-
body: request.body,
|
|
103
|
-
key: OpenSSL::PKey::RSA.new('public_key.pem'),
|
|
104
|
-
algorithm: 'rsa-sha256'
|
|
105
|
-
)
|
|
79
|
+
request['Signature-Input'] = sig_headers['Signature-Input']
|
|
80
|
+
request['Signature'] = sig_headers['Signature']
|
|
81
|
+
|
|
82
|
+
response = http.request(request) # Net::HTTPResponse
|
|
83
|
+
end
|
|
106
84
|
```
|
|
107
85
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
86
|
+
### Faraday
|
|
87
|
+
|
|
88
|
+
As a faraday middleware
|
|
89
|
+
|
|
111
90
|
```ruby
|
|
112
91
|
require 'http_signature/faraday'
|
|
113
92
|
|
|
114
|
-
HTTPSignature::Faraday.key = '
|
|
115
|
-
HTTPSignature::Faraday.key_id = 'key-1'
|
|
93
|
+
HTTPSignature::Faraday.key = 'secret'
|
|
94
|
+
HTTPSignature::Faraday.key_id = 'key-1'
|
|
116
95
|
|
|
117
|
-
|
|
118
|
-
# Tell faraday to use the middleware. Read more about it here: https://github.com/lostisland/faraday#advanced-middleware-usage
|
|
119
96
|
Faraday.new('http://example.com') do |faraday|
|
|
120
97
|
faraday.use(HTTPSignature::Faraday)
|
|
121
98
|
faraday.adapter(Faraday.default_adapter)
|
|
122
99
|
end
|
|
123
100
|
|
|
124
|
-
# Now this request will contain the `Signature`
|
|
101
|
+
# Now this request will contain the `Signature-Input` and `Signature` headers
|
|
125
102
|
response = conn.get('/')
|
|
126
103
|
|
|
127
104
|
# Request looking like:
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# Signature: keyId="key-1",algorithm="hmac-sha256",headers="(request-target) date",signature="EzFa4vb0z+VFF8VYt9qQlzF9MTf5Izptc02OJ7aajnU="
|
|
105
|
+
# Signature-Input: sig1=("@method" "@authority" "@target-uri" "date");created=...
|
|
106
|
+
# Signature: sig1=:BASE64_SIGNATURE:
|
|
131
107
|
```
|
|
132
108
|
|
|
133
|
-
|
|
134
|
-
|
|
109
|
+
## Incoming request examples
|
|
110
|
+
|
|
111
|
+
### Rack middleware
|
|
112
|
+
Rack middlewares sits in between your app and the HTTP request and validate the signature before hitting your app. Read more about [rack middlewares here](https://codenoble.com/blog/understanding-rack-middleware/).
|
|
113
|
+
|
|
114
|
+
Here is how it could be used with sinatra:
|
|
135
115
|
|
|
136
|
-
#### General rack application
|
|
137
|
-
Sinatra for example
|
|
138
116
|
```ruby
|
|
139
117
|
require 'http_signature/rack'
|
|
140
118
|
|
|
141
|
-
HTTPSignature.
|
|
142
|
-
|
|
143
|
-
|
|
119
|
+
HTTPSignature.configure do |config|
|
|
120
|
+
config.keys = [
|
|
121
|
+
{id: 'key-1', value: 'MySecureKey'}
|
|
122
|
+
]
|
|
123
|
+
end
|
|
124
|
+
HTTPSignature::Rack.exclude_paths = ['/', '/hello/*']
|
|
144
125
|
|
|
145
126
|
use HTTPSignature::Rack
|
|
146
127
|
run MyApp
|
|
147
128
|
```
|
|
148
129
|
|
|
149
|
-
|
|
150
|
-
|
|
130
|
+
### Rails
|
|
131
|
+
Opt-in per controller/action using a before_action. It responds with `401 Unauthorized` if the signature is invalid
|
|
132
|
+
|
|
151
133
|
```ruby
|
|
152
|
-
|
|
153
|
-
|
|
134
|
+
# app/controllers/api/base_controller.rb
|
|
135
|
+
|
|
136
|
+
require 'http_signature/rails'
|
|
137
|
+
|
|
138
|
+
class Api::BaseController < ApplicationController
|
|
139
|
+
include HTTPSignature::Rails::Controller
|
|
140
|
+
|
|
141
|
+
before_action :verify_http_signature!
|
|
142
|
+
end
|
|
154
143
|
```
|
|
155
144
|
|
|
156
|
-
|
|
157
|
-
are supported to be able to easily be rotated.
|
|
145
|
+
Set the keys in an initializer
|
|
158
146
|
```ruby
|
|
159
|
-
|
|
147
|
+
# config/initializers/http_signature.rb
|
|
148
|
+
|
|
149
|
+
HTTPSignature.configure do |config|
|
|
150
|
+
config.keys = [
|
|
151
|
+
{id: 'key-1', value: 'MySecureKey'}
|
|
152
|
+
]
|
|
153
|
+
end
|
|
160
154
|
```
|
|
161
155
|
|
|
162
156
|
|
|
@@ -179,11 +173,9 @@ rake test TEST=test/http_signature_test.rb TESTOPTS="--name=/appends\ the\ query
|
|
|
179
173
|
## License
|
|
180
174
|
This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
|
|
181
175
|
|
|
182
|
-
##
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
https://tools.ietf.org/html/draft-cavage-http-signatures-08#section-2.3,
|
|
189
|
-
e.g, concatenate multiple instances of the same headers and remove surrounding whitespaces
|
|
176
|
+
## Why/when should I use this?
|
|
177
|
+
When you need to make sure that the request or response has not been tampered with (_integrity_). And you can be sure that the request was sent by someone that had the key (_authenticity_). Don't confuse this with encryption, the signed message is not encrypted. It's just _signed_. You could add a layer of encryption on top of this. Or just use HTTPS and you're _kinda safe_ for not that much hassle, which is totally fine in most cases.
|
|
178
|
+
|
|
179
|
+
[Read more about HMAC here](https://security.stackexchange.com/questions/20129/how-and-when-do-i-use-hmac/20301), even though you can sign your messages with RSA as well, but it's the same principle.
|
|
180
|
+
|
|
181
|
+
Beware that this has not been audited and should be used at your own risk!
|
data/Rakefile
CHANGED
data/bin/standardrb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'standardrb' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
|
12
|
+
|
|
13
|
+
require "rubygems"
|
|
14
|
+
require "bundler/setup"
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path("standard", "standardrb")
|
data/http_signature.gemspec
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
1
|
-
lib = File.expand_path(
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "http_signature/version"
|
|
3
4
|
|
|
4
5
|
Gem::Specification.new do |spec|
|
|
5
|
-
spec.name
|
|
6
|
-
spec.version
|
|
7
|
-
spec.authors
|
|
8
|
-
spec.email
|
|
6
|
+
spec.name = "http_signature"
|
|
7
|
+
spec.version = HTTPSignature::VERSION
|
|
8
|
+
spec.authors = ["Joel Larsson"]
|
|
9
|
+
spec.email = ["bolmaster2@gmail.com"]
|
|
9
10
|
|
|
10
|
-
spec.summary
|
|
11
|
-
spec.description
|
|
12
|
-
spec.homepage
|
|
13
|
-
spec.license
|
|
11
|
+
spec.summary = "Create and validate HTTP Message Signatures"
|
|
12
|
+
spec.description = "Create and validate HTTP Message Signatures according to RFC 9421: https://www.rfc-editor.org/rfc/rfc9421.html"
|
|
13
|
+
spec.homepage = "https://github.com/bolmaster2/http-signature"
|
|
14
|
+
spec.license = "MIT"
|
|
14
15
|
|
|
15
|
-
spec.files
|
|
16
|
-
spec.bindir
|
|
17
|
-
spec.executables
|
|
18
|
-
spec.require_paths = [
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
|
+
spec.bindir = "exe"
|
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
19
20
|
|
|
20
|
-
spec.
|
|
21
|
-
|
|
22
|
-
spec.add_development_dependency
|
|
23
|
-
spec.add_development_dependency
|
|
21
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "bundler"
|
|
24
|
+
spec.add_development_dependency "rake"
|
|
25
|
+
spec.add_development_dependency "minitest", ">= 5.24"
|
|
26
|
+
spec.add_development_dependency "rack"
|
|
27
|
+
spec.add_development_dependency "faraday", ">= 2.7"
|
|
28
|
+
spec.add_development_dependency "standard"
|
|
29
|
+
spec.add_development_dependency "simplecov"
|
|
30
|
+
spec.add_development_dependency "actionpack", ">= 6.1"
|
|
31
|
+
|
|
32
|
+
spec.add_dependency "base64"
|
|
24
33
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "http_signature"
|
|
4
|
+
require "faraday"
|
|
5
5
|
|
|
6
6
|
class HTTPSignature::Faraday < Faraday::Middleware
|
|
7
7
|
class << self
|
|
@@ -9,10 +9,10 @@ class HTTPSignature::Faraday < Faraday::Middleware
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def call(env)
|
|
12
|
-
raise
|
|
12
|
+
raise "key and key_id needs to be set" if self.class.key.nil? || self.class.key_id.nil?
|
|
13
13
|
|
|
14
14
|
body =
|
|
15
|
-
if env[:body]
|
|
15
|
+
if env[:body]&.respond_to?(:read)
|
|
16
16
|
string = env[:body].read
|
|
17
17
|
env[:body].rewind
|
|
18
18
|
string
|
|
@@ -21,20 +21,22 @@ class HTTPSignature::Faraday < Faraday::Middleware
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# Choose which headers to sign
|
|
24
|
-
filtered_headers = %w
|
|
25
|
-
headers_to_sign = env[:request_headers].select { |k,
|
|
24
|
+
filtered_headers = %w[Host Date Content-Digest]
|
|
25
|
+
headers_to_sign = env[:request_headers].select { |k, _v| filtered_headers.include?(k.to_s) }
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
signature_headers = HTTPSignature.create(
|
|
28
28
|
url: env[:url],
|
|
29
29
|
method: env[:method],
|
|
30
30
|
headers: headers_to_sign,
|
|
31
31
|
key: self.class.key,
|
|
32
32
|
key_id: self.class.key_id,
|
|
33
|
-
algorithm:
|
|
33
|
+
algorithm: "hmac-sha256",
|
|
34
34
|
body: body
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
signature_headers.each do |header, value|
|
|
38
|
+
env[:request_headers][header] = value
|
|
39
|
+
end
|
|
38
40
|
|
|
39
41
|
@app.call(env)
|
|
40
42
|
end
|