http_signature 0.1.0 → 1.0.1
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/push_gem.yml +25 -0
- data/.github/workflows/standardrb.yml +15 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -1
- data/AGENTS.md +4 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +143 -10
- data/README.md +79 -118
- data/Rakefile +3 -3
- data/bin/standardrb +16 -0
- data/http_signature.gemspec +26 -18
- data/lib/http_signature/faraday.rb +10 -8
- data/lib/http_signature/rack.rb +30 -47
- data/lib/http_signature/rails.rb +67 -0
- data/lib/http_signature/version.rb +1 -1
- data/lib/http_signature.rb +364 -132
- metadata +82 -12
- data/.circleci/config.yml +0 -0
- data/.rubocop.yml +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a052e0252607d934ef7221c6aa3ea32fb21644af4f1af77985677db0cb2e2bb3
|
|
4
|
+
data.tar.gz: 843effaf6aac8b647b29d2d19f5414513be7efb76a0a1eb837b9cc2872fe3727
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5aedcedf0056a4f98414a599a51ec89484500c3447f124c4f070b11d7567ec2c8b5f682aa17e8f1e6c1dcf09f0c1e796530ada79bca633e228e3cfe98154f575
|
|
7
|
+
data.tar.gz: 61e44809bd68c3d072b94faee4f1ba7915aa49e2acfde2131b55c2442982a14cd199e288eff6cf34533440bebfb3b788997a395f618830da4a4c7d96407e7e01
|
|
@@ -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
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
tags:
|
|
4
|
+
- 'v*.*.*'
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
push:
|
|
8
|
+
name: Push gem to RubyGems.org
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
13
|
+
contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
with:
|
|
18
|
+
persist-credentials: false
|
|
19
|
+
- name: Set up Ruby
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
bundler-cache: true
|
|
23
|
+
ruby-version: '3.4'
|
|
24
|
+
|
|
25
|
+
- uses: rubygems/release-gem@v1
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3.4.8
|
data/AGENTS.md
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,26 +1,159 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
http_signature (
|
|
4
|
+
http_signature (1.0.1)
|
|
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,114 +1,62 @@
|
|
|
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
|
-
|
|
7
|
-
|
|
5
|
+
TL;DR: You specify what should be signed in `Signature-Input` with [components](https://www.rfc-editor.org/rfc/rfc9421#name-derived-components) and lowercase headers. And then the signature is in the `Signature` header
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
8
|
|
|
9
|
-
## Installation
|
|
10
9
|
```
|
|
11
|
-
|
|
10
|
+
Signature-Input: sig1=("@method" "@target-uri" "date");created=1767816111;keyid="Test";alg="hmac-sha256"
|
|
11
|
+
Signature: sig1=:7a1ajkE2rOu+gnW3WLZ4ZEcgCm3TfExmypM/giIgdM0=:
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Installation
|
|
15
15
|
|
|
16
|
-
```
|
|
17
|
-
|
|
16
|
+
```shell
|
|
17
|
+
bundle add http_signature
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
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
|
-
```ruby
|
|
23
|
-
HTTPSignature.create(
|
|
24
|
-
url: 'https://example.com/foo',
|
|
25
|
-
key_id: 'Test',
|
|
26
|
-
key: 'secret 🙈'
|
|
27
|
-
)
|
|
28
|
-
# 'keyId="Test",algorithm="hmac-sha256",headers="(request-target)",signature="OQ/dHqRW9vFmrW/RCHg7O2Fqx+3uqxJw81p6k9Rcyo4="'
|
|
29
|
-
```
|
|
20
|
+
## Usage
|
|
30
21
|
|
|
31
|
-
###
|
|
32
|
-
Uses both query parameters (in query string) and a `json` body as a `POST` request.
|
|
33
|
-
Also shows how to set `rsa-sha256` as algorithm. The `digest` is as you see basically
|
|
34
|
-
a `sha-256` digest of the request body.
|
|
22
|
+
### Create signature
|
|
35
23
|
|
|
36
|
-
|
|
37
|
-
params = {
|
|
38
|
-
param: 'value',
|
|
39
|
-
pet: 'dog'
|
|
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,
|
|
55
|
-
headers: headers,
|
|
56
|
-
key_id: 'Test',
|
|
57
|
-
algorithm: 'rsa-sha256',
|
|
58
|
-
key: File.read('key.pem'),
|
|
59
|
-
body: body
|
|
60
|
-
)
|
|
61
|
-
```
|
|
24
|
+
`HTTPSignature.create` returns both `Signature-Input` and `Signature` headers that you can include in your request.
|
|
62
25
|
|
|
63
|
-
### With digest header auto-added
|
|
64
|
-
When digest header is omitted it's auto added as last header generated from the `body`:
|
|
65
26
|
|
|
66
27
|
```ruby
|
|
67
|
-
|
|
28
|
+
headers = { 'date' => 'Tue, 20 Apr 2021 02:07:55 GMT' }
|
|
68
29
|
|
|
69
|
-
HTTPSignature.create(
|
|
70
|
-
url: 'https://example.com/foo',
|
|
30
|
+
sig_headers = HTTPSignature.create(
|
|
31
|
+
url: 'https://example.com/foo?pet=dog',
|
|
32
|
+
method: :get,
|
|
33
|
+
headers: headers,
|
|
71
34
|
key_id: 'Test',
|
|
72
|
-
key: 'secret
|
|
73
|
-
|
|
35
|
+
key: 'secret',
|
|
36
|
+
covered_components: %w[@method @target-uri date],
|
|
74
37
|
)
|
|
75
|
-
|
|
38
|
+
|
|
39
|
+
request['Signature-Input'] = sig_headers['Signature-Input']
|
|
40
|
+
request['Signature'] = sig_headers['Signature']
|
|
76
41
|
```
|
|
77
42
|
|
|
78
|
-
### Validate asymmetric signature
|
|
79
|
-
With an asymmetric algorithm you can't just recreate the same header and see if they
|
|
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.
|
|
82
43
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Host: example.com
|
|
87
|
-
Date: Thu, 05 Jan 2014 21:31:40 GMT
|
|
88
|
-
Content-Type: application/json
|
|
89
|
-
Content-Length: 18
|
|
90
|
-
Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
|
|
91
|
-
Signature: keyId="Test-1",algorithm="rsa-sha256",headers="(request-target) host date content-type content-length digest",signature="YGPVM1tGHD7CHgTmroy9apLtVazdESzMl4vj1koYHNCMmTEDor4Om5TDZDFaJdny5dF3gq+PQQuPwyknNEvACmSjwVXzljPFxaY/JMZTqAdD0yHTP2Rx0Y/J4GwgKARWTZUmccfVYsXp86PhIlCymzleZzYCzj6shyg9NB7Ht+k="
|
|
92
|
-
|
|
93
|
-
{"hello": "world"}
|
|
94
|
-
```
|
|
44
|
+
### Validate signature
|
|
45
|
+
|
|
46
|
+
Call `valid?` with the incoming request headers (including `Signature-Input` and `Signature`)
|
|
95
47
|
|
|
96
|
-
Let's assume we have this request ☝️ in a `request` object for the sake of the example:
|
|
97
48
|
```ruby
|
|
98
49
|
HTTPSignature.valid?(
|
|
99
|
-
url:
|
|
100
|
-
method:
|
|
101
|
-
headers:
|
|
102
|
-
|
|
103
|
-
key: OpenSSL::PKey::RSA.new('public_key.pem'),
|
|
104
|
-
algorithm: 'rsa-sha256'
|
|
50
|
+
url: "https://example.com/foo",
|
|
51
|
+
method: :get,
|
|
52
|
+
headers: headers,
|
|
53
|
+
key: "secret"
|
|
105
54
|
)
|
|
106
55
|
```
|
|
107
56
|
|
|
108
|
-
##
|
|
57
|
+
## Outgoing request examples
|
|
58
|
+
|
|
109
59
|
### NET::HTTP
|
|
110
|
-
Example of using it with `NET::HTTP`. There's no real integration written so it's basically just
|
|
111
|
-
getting the request object's data and create the signature and adding it to the headers.
|
|
112
60
|
|
|
113
61
|
```ruby
|
|
114
62
|
require 'net/http'
|
|
@@ -119,7 +67,7 @@ uri = URI('http://example.com/hello')
|
|
|
119
67
|
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
120
68
|
request = Net::HTTP::Get.new(uri)
|
|
121
69
|
|
|
122
|
-
|
|
70
|
+
sig_headers = HTTPSignature.create(
|
|
123
71
|
url: request.uri,
|
|
124
72
|
method: request.method,
|
|
125
73
|
headers: request.each_header.map { |k, v| [k, v] }.to_h,
|
|
@@ -129,66 +77,81 @@ Net::HTTP.start(uri.host, uri.port) do |http|
|
|
|
129
77
|
body: request.body ? request.body : ''
|
|
130
78
|
)
|
|
131
79
|
|
|
132
|
-
request['Signature'] =
|
|
80
|
+
request['Signature-Input'] = sig_headers['Signature-Input']
|
|
81
|
+
request['Signature'] = sig_headers['Signature']
|
|
133
82
|
|
|
134
83
|
response = http.request(request) # Net::HTTPResponse
|
|
135
84
|
end
|
|
136
85
|
```
|
|
137
86
|
|
|
138
|
-
### Faraday
|
|
139
|
-
|
|
140
|
-
|
|
87
|
+
### Faraday
|
|
88
|
+
|
|
89
|
+
As a faraday middleware
|
|
141
90
|
|
|
142
91
|
```ruby
|
|
143
92
|
require 'http_signature/faraday'
|
|
144
93
|
|
|
145
|
-
HTTPSignature::Faraday.key = '
|
|
146
|
-
HTTPSignature::Faraday.key_id = 'key-1'
|
|
94
|
+
HTTPSignature::Faraday.key = 'secret'
|
|
95
|
+
HTTPSignature::Faraday.key_id = 'key-1'
|
|
147
96
|
|
|
148
|
-
# Tell faraday to use the middleware. Read more about it here: https://github.com/lostisland/faraday#advanced-middleware-usage
|
|
149
97
|
Faraday.new('http://example.com') do |faraday|
|
|
150
98
|
faraday.use(HTTPSignature::Faraday)
|
|
151
99
|
faraday.adapter(Faraday.default_adapter)
|
|
152
100
|
end
|
|
153
101
|
|
|
154
|
-
# Now this request will contain the `Signature`
|
|
102
|
+
# Now this request will contain the `Signature-Input` and `Signature` headers
|
|
155
103
|
response = conn.get('/')
|
|
156
104
|
|
|
157
105
|
# Request looking like:
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
# Signature: keyId="key-1",algorithm="hmac-sha256",headers="(request-target) date",signature="EzFa4vb0z+VFF8VYt9qQlzF9MTf5Izptc02OJ7aajnU="
|
|
106
|
+
# Signature-Input: sig1=("@method" "@authority" "@target-uri" "date");created=...
|
|
107
|
+
# Signature: sig1=:BASE64_SIGNATURE:
|
|
161
108
|
```
|
|
162
109
|
|
|
163
|
-
|
|
164
|
-
|
|
110
|
+
## Incoming request examples
|
|
111
|
+
|
|
112
|
+
### Rack middleware
|
|
113
|
+
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/).
|
|
114
|
+
|
|
115
|
+
Here is how it could be used with sinatra:
|
|
165
116
|
|
|
166
|
-
#### General Rack application
|
|
167
|
-
Sinatra for example
|
|
168
117
|
```ruby
|
|
169
118
|
require 'http_signature/rack'
|
|
170
119
|
|
|
171
|
-
HTTPSignature.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
120
|
+
HTTPSignature.configure do |config|
|
|
121
|
+
config.keys = [
|
|
122
|
+
{id: 'key-1', value: 'MySecureKey'}
|
|
123
|
+
]
|
|
124
|
+
end
|
|
175
125
|
HTTPSignature::Rack.exclude_paths = ['/', '/hello/*']
|
|
176
126
|
|
|
177
127
|
use HTTPSignature::Rack
|
|
178
128
|
run MyApp
|
|
179
129
|
```
|
|
180
130
|
|
|
181
|
-
|
|
182
|
-
|
|
131
|
+
### Rails
|
|
132
|
+
Opt-in per controller/action using a before_action. It responds with `401 Unauthorized` if the signature is invalid
|
|
133
|
+
|
|
183
134
|
```ruby
|
|
184
|
-
|
|
185
|
-
|
|
135
|
+
# app/controllers/api/base_controller.rb
|
|
136
|
+
|
|
137
|
+
require 'http_signature/rails'
|
|
138
|
+
|
|
139
|
+
class Api::BaseController < ApplicationController
|
|
140
|
+
include HTTPSignature::Rails::Controller
|
|
141
|
+
|
|
142
|
+
before_action :verify_http_signature!
|
|
143
|
+
end
|
|
186
144
|
```
|
|
187
145
|
|
|
188
|
-
|
|
189
|
-
are supported to be able to easily be rotated.
|
|
146
|
+
Set the keys in an initializer
|
|
190
147
|
```ruby
|
|
191
|
-
|
|
148
|
+
# config/initializers/http_signature.rb
|
|
149
|
+
|
|
150
|
+
HTTPSignature.configure do |config|
|
|
151
|
+
config.keys = [
|
|
152
|
+
{id: 'key-1', value: 'MySecureKey'}
|
|
153
|
+
]
|
|
154
|
+
end
|
|
192
155
|
```
|
|
193
156
|
|
|
194
157
|
|
|
@@ -211,11 +174,9 @@ rake test TEST=test/http_signature_test.rb TESTOPTS="--name=/appends\ the\ query
|
|
|
211
174
|
## License
|
|
212
175
|
This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
|
|
213
176
|
|
|
214
|
-
##
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
https://tools.ietf.org/html/draft-cavage-http-signatures-08#section-2.3,
|
|
221
|
-
e.g, concatenate multiple instances of the same headers and remove surrounding whitespaces
|
|
177
|
+
## Why/when should I use this?
|
|
178
|
+
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.
|
|
179
|
+
|
|
180
|
+
[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.
|
|
181
|
+
|
|
182
|
+
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,25 +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
|
|
3
|
+
require "http_signature/version"
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name
|
|
7
|
-
spec.version
|
|
8
|
-
spec.authors
|
|
9
|
-
spec.email
|
|
6
|
+
spec.name = "http_signature"
|
|
7
|
+
spec.version = HTTPSignature::VERSION
|
|
8
|
+
spec.authors = ["Joel Larsson"]
|
|
9
|
+
spec.email = ["bolmaster2@gmail.com"]
|
|
10
10
|
|
|
11
|
-
spec.summary
|
|
12
|
-
spec.description
|
|
13
|
-
spec.homepage
|
|
14
|
-
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"
|
|
15
15
|
|
|
16
|
-
spec.files
|
|
17
|
-
spec.bindir
|
|
18
|
-
spec.executables
|
|
19
|
-
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"]
|
|
20
20
|
|
|
21
|
-
spec.
|
|
22
|
-
|
|
23
|
-
spec.add_development_dependency
|
|
24
|
-
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"
|
|
25
33
|
end
|