rack-response-signature 0.2.0 → 0.3.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d05e1b43b17481e66cfa413e96ba3afee5dd09f8
4
+ data.tar.gz: e18caf974ab9ab97656f0f89729dcee3c37219c7
5
+ SHA512:
6
+ metadata.gz: bacd6f52898d6449220124eb42384550343eef1f16011d50bfcff0147be5692dae289fbf714ebf62c33caa83bb03a18833678bafbaca6ee6f47949852e92e412
7
+ data.tar.gz: 260bb0b15b9785f8cd637426a6c661f1aa49d0fd0a0ce03984c56437817a37227ad7fcc91cb55e44527bc44dfa47921dbfd3ec29a5b1a267975b2949033b07c4
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ /.rvmrc
20
+ /.rbenv-version
21
+ /.ruby-version
22
+ /.ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Nathaniel Bibler.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc CHANGED
@@ -58,24 +58,3 @@ to `config/environments/production.rb`, or to an initializer:
58
58
  You should now see `Rack::ResponseSignature` listed in the middleware stack:
59
59
 
60
60
  $ rake middleware
61
-
62
- === License
63
-
64
- Copyright (c) 2010 Nathaniel Bibler <http://www.nathanielbibler.com/>
65
-
66
- Permission is hereby granted, free of charge, to any person obtaining a copy
67
- of this software and associated documentation files (the "Software"), to
68
- deal in the Software without restriction, including without limitation the
69
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
70
- sell copies of the Software, and to permit persons to whom the Software is
71
- furnished to do so, subject to the following conditions:
72
-
73
- The above copyright notice and this permission notice shall be included in
74
- all copies or substantial portions of the Software.
75
-
76
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
77
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
78
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
79
- THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
80
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
81
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -3,52 +3,51 @@ require 'base64'
3
3
  require 'cgi'
4
4
 
5
5
  module Rack
6
-
7
6
  ##
8
7
  # Rack::ResponseSignature is a middleware which will manipulate the server
9
- # response by signing the response body against an RSA private key. Your
8
+ # response by signing the response body against an RSA private key. Your
10
9
  # clients may then validate the response against a known-good public key
11
10
  # to verify server authenticity against a man-in-the-middle attack.
12
- #
11
+ #
13
12
  # The signature, if generated, is placed in a "X-Response-Signature" HTTP
14
- # header. Currently, signatures are only generated for HTTP SUCCESS (200)
13
+ # header. Currently, signatures are only generated for HTTP SUCCESS (200)
15
14
  # responses.
16
- #
17
- # Obviously, it would be more straightforward to simply use an SSL
15
+ #
16
+ # Obviously, it would be more straightforward to simply use an SSL
18
17
  # certificate provided by a trusted CA and just enable SSL verification
19
18
  # per request. However, the use of SSL accrues significate overhead both for
20
19
  # the server, the client, and the network in general. Not only that, but
21
20
  # in some cases (like Heroku) using a custom, verifiable SSL certificate
22
21
  # is either not reasonable or not possible.
23
- #
22
+ #
24
23
  # === Using Rack::ResponseSignature
25
- #
24
+ #
26
25
  # To use this middleware, simply:
27
- #
26
+ #
28
27
  # use Rack::ResponseSignature, "--- BEGIN PRIVATE KEY ----\nabc123....."
29
- #
28
+ #
30
29
  # Or, for Rails,
31
- #
30
+ #
32
31
  # config.middleware.use "Rack::ResponseSignature", "--- BEGIN PRIVATE KEY ----\nabc123....."
33
- #
34
- # Or, a somewhat more secure approach would be to utilize environment
35
- # variables on the production system to define your private key. This
32
+ #
33
+ # Or, a somewhat more secure approach would be to utilize environment
34
+ # variables on the production system to define your private key. This
36
35
  # keeps your private keys out of your source code manager and away from
37
36
  # prying eyes:
38
- #
37
+ #
39
38
  # config.middleware.use "Rack::ResponseSignature", ENV['PRIVATE_RESPONSE_KEY']
40
- #
39
+ #
41
40
  # This is especially useful for Heroku deployments.
42
- #
41
+ #
43
42
  # === Manual Verification of Signature
44
- #
45
- # Using curl, you can manually inspect to be sure that your signatures are
43
+ #
44
+ # Using curl, you can manually inspect to be sure that your signatures are
46
45
  # being generated with:
47
- #
46
+ #
48
47
  # $ curl -is http://myserver.com
49
- #
48
+ #
50
49
  # Which would return something similar to:
51
- #
50
+ #
52
51
  # HTTP/1.1 200 OK
53
52
  # Server: nginx/0.6.39
54
53
  # Date: Tue, 23 Feb 2010 05:15:25 GMT
@@ -58,85 +57,85 @@ module Rack
58
57
  # ETag: "54a2096d2c361907b3f9cc7ec9a2231d"
59
58
  # X-Response-Signature: JywymlJfA90Q4x52LKt4J8Tb8p4rXI%2BptKDNm3NC7F495...
60
59
  # Cache-Control: private, max-age=0, must-revalidate
61
- #
60
+ #
62
61
  # === Client Verification
63
- #
62
+ #
64
63
  # To verify your signatures on the client, simply share your public RSA key
65
64
  # with your client and verify the response:
66
- #
65
+ #
67
66
  # require 'net/http'
68
67
  # require 'base64'
69
68
  # require 'cgi'
70
- #
69
+ #
71
70
  # uri = URI.parse("http://myserver.com/")
72
71
  # response = nil
73
72
  # Net::HTTP.start(uri.host, uri.port) do |http|
74
73
  # response = http.get(uri.path)
75
74
  # end
76
- #
75
+ #
77
76
  # puts "Response valid? %s" % [OpenSSL::PKey::RSA.new(PublicKey).
78
- # verify(OpenSSL::Digest::SHA256.new,
79
- # Base64.decode64(CGI.unescape(response['X-Response-Signature'])),
77
+ # verify(OpenSSL::Digest::SHA256.new,
78
+ # Base64.decode64(CGI.unescape(response['X-Response-Signature'])),
80
79
  # response.body.strip)]
81
- #
80
+ #
82
81
  # === Options
83
- #
82
+ #
84
83
  # You may pass an optional, third, hash argument into the middleware. This
85
84
  # argument allows you to override defaults.
86
- #
85
+ #
87
86
  # digest::
88
87
  # Set the digest to use when generating the signature (Default: OpenSSL::Digest::SHA256)
89
- #
88
+ #
90
89
  class ResponseSignature
91
-
92
- VERSION = '0.2.0'
93
-
90
+ VERSION = '0.3.0'
91
+
94
92
  def initialize(app, private_key, options = {})
95
93
  options[:digest] ||= OpenSSL::Digest::SHA256
96
94
  @app = app
97
95
  @private_key = private_key && private_key != '' ? private_key : nil
98
96
  @options = options
99
97
  end
100
-
98
+
99
+
101
100
  def call(env)
102
101
  status, headers, response = @app.call(env)
103
-
102
+
104
103
  if set_signature_header?(status)
105
- [status, add_signature(headers, value_of(response)), value_of(response)]
104
+ [status, add_signature(headers, value_of(response)), response]
106
105
  else
107
106
  [status, headers, response]
108
107
  end
109
108
  end
110
-
111
-
109
+
110
+
112
111
  private
113
-
114
-
115
- def set_signature_header?(status)
116
- @private_key && status.to_i == 200
117
- end
118
-
112
+
113
+
119
114
  def add_signature(headers, body)
120
115
  headers['X-Response-Signature'] = CGI.escape(Base64.encode64(sign(body)))
121
116
  headers
122
117
  end
123
-
118
+
119
+ def digest
120
+ @options.has_key?(:digest) ? @options[:digest].new : OpenSSL::Digest::SHA256.new
121
+ end
122
+
124
123
  def rsa
125
124
  @rsa ||= OpenSSL::PKey::RSA.new(@private_key)
126
125
  end
127
-
126
+
127
+ def set_signature_header?(status)
128
+ @private_key && status.to_i == 200
129
+ end
130
+
128
131
  def sign(data)
129
132
  rsa.sign(digest, data)
130
133
  end
131
-
132
- def digest
133
- @options.has_key?(:digest) ? @options[:digest].new : OpenSSL::Digest::SHA256.new
134
- end
135
-
134
+
136
135
  def value_of(response)
137
- (response.respond_to?(:body) ? response.body : response).strip
136
+ body = (response.respond_to?(:body) ? response.body : response)
137
+ body = body.inject("") { |content, sum| sum += content }
138
+ body.strip
138
139
  end
139
-
140
140
  end
141
-
142
141
  end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/response_signature'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rack-response-signature'
8
+ spec.version = Rack::ResponseSignature::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.authors = ['Nathaniel Bibler']
11
+ spec.email = ['gem@nathanielbibler.com']
12
+ spec.description = 'Rack::ResponseSignature uses RSA key pairs to transparently sign the outgoing responses from any Rack-compliant application.'
13
+ spec.summary = 'Rack middleware to add transparent response signing'
14
+ spec.homepage = 'http://github.com/nbibler/rack_response_signature'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'rack', '~> 1.0'
23
+
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rspec', '~> 2.11'
27
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ require 'rack/lint'
4
+ require 'rack/mock'
5
+
6
+ describe Rack::ResponseSignature do
7
+ context 'response header' do
8
+ it 'is added on a 200 response' do
9
+ app = success_app("Success")
10
+ expect(response_headers(app)).to have_key('X-Response-Signature')
11
+ end
12
+
13
+ it 'is not added on non-200 responses' do
14
+ [ 302 , 404 , 503 ].each do |response_code|
15
+ app = new_app(response_code, response_code.to_s)
16
+ expect(response_headers(app)).not_to have_key('X-Response-Signature')
17
+ end
18
+ end
19
+ end
20
+
21
+ it 'does not modify the response body' do
22
+ body = [" Whitespace "]
23
+ app = new_app(200, body)
24
+ expect(response_body(app)).to equal(body)
25
+ end
26
+
27
+ it 'ignores leading and trailing whitespace when generating signature' do
28
+ expect(response_headers(success_app('test string'))).
29
+ to include({'X-Response-Signature' => 't4IMaguaZTUFQ9LtPm7E4ijhVJ53OPe9a7wBujteaHQvHuacYgdSteQVTzxn%0AC%2BGoH8%2BdefNSTJa4fKl0PDXVUA%3D%3D%0A'})
30
+ expect(response_headers(success_app(' test string'))).
31
+ to include({'X-Response-Signature' => 't4IMaguaZTUFQ9LtPm7E4ijhVJ53OPe9a7wBujteaHQvHuacYgdSteQVTzxn%0AC%2BGoH8%2BdefNSTJa4fKl0PDXVUA%3D%3D%0A'})
32
+ expect(response_headers(success_app('test string '))).
33
+ to include({'X-Response-Signature' => 't4IMaguaZTUFQ9LtPm7E4ijhVJ53OPe9a7wBujteaHQvHuacYgdSteQVTzxn%0AC%2BGoH8%2BdefNSTJa4fKl0PDXVUA%3D%3D%0A'})
34
+ end
35
+
36
+
37
+ private
38
+
39
+
40
+ def new_app(response_code, response_body)
41
+ ->(env) { [response_code, {"Content-Type" => "test/plain"}, response_body] }
42
+ end
43
+
44
+ def private_key
45
+ @private_key ||= <<-KEY.gsub(/^\s+/, '')
46
+ -----BEGIN RSA PRIVATE KEY-----
47
+ MIIBOgIBAAJBAMtIiebDa7r1Y+dD/avJpYAqkLMUwoRCrQSIdnG7LA1hFc9/r5JR
48
+ jEtUSLA+eg0Fh72enu9+CT/q3Q4sg9h5s3UCAwEAAQJACAif2ozSjxrvjc40Ejvv
49
+ 3HbSLSGe5lc0Oz+hXrFE9mpTyFI7l/KYsMB/6JNEfY8LUNTQXz6fet4obIh9STIj
50
+ 6QIhAOVbthtvJK28o5V76ssZ8BkkYWQS9IUCxFXKIzbzJ3NHAiEA4uV0MBd+QHme
51
+ QAviG5f/ZxlTQGQMtOtYaiNUCuaQaWMCIDjc2/FBRN6t/gB5kGR6McSJ+HtPF8BC
52
+ R1rdmo1tC0LRAiBI7CPufO53vF6vCOKvqadNNGd8T2uCDg2JdzdAlZ+eLwIhALud
53
+ aKQMQPo8Ie44nsCIT2FzI4YaNf1RvF+8E8FRXZBJ
54
+ -----END RSA PRIVATE KEY-----
55
+ KEY
56
+ end
57
+
58
+ def public_key
59
+ @public_key ||= <<-KEY.gsub(/^\s+/, '')
60
+ -----BEGIN PUBLIC KEY-----
61
+ MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMtIiebDa7r1Y+dD/avJpYAqkLMUwoRC
62
+ rQSIdnG7LA1hFc9/r5JRjEtUSLA+eg0Fh72enu9+CT/q3Q4sg9h5s3UCAwEAAQ==
63
+ -----END PUBLIC KEY-----
64
+ KEY
65
+ end
66
+
67
+ def request
68
+ Rack::MockRequest.env_for
69
+ end
70
+
71
+ def response(app, *args)
72
+ Rack::Lint.new(described_class.new(app, private_key, *args)).call(request)
73
+ end
74
+
75
+ def response_headers(app, *args)
76
+ response(app, *args)[1]
77
+ end
78
+
79
+ def response_body(app, *args)
80
+ response(app, *args)[2].instance_variable_get("@body")
81
+ end
82
+
83
+ def success_app(body_content)
84
+ new_app(200, [body_content])
85
+ end
86
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
metadata CHANGED
@@ -1,68 +1,113 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rack-response-signature
3
- version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 2
9
- - 0
10
- version: 0.2.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Nathaniel Bibler
14
8
  autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2010-12-08 00:00:00 -05:00
19
- default_executable:
20
- dependencies: []
21
-
22
- description: Rack::ResponseSignature uses RSA key pairs to transparently sign the outgoing responses from any Rack-compliant application.
23
- email: gem@nathanielbibler.com
11
+ date: 2013-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.11'
69
+ description: Rack::ResponseSignature uses RSA key pairs to transparently sign the
70
+ outgoing responses from any Rack-compliant application.
71
+ email:
72
+ - gem@nathanielbibler.com
24
73
  executables: []
25
-
26
74
  extensions: []
27
-
28
75
  extra_rdoc_files: []
29
-
30
- files:
31
- - lib/rack/response_signature.rb
76
+ files:
77
+ - .gitignore
78
+ - .rspec
79
+ - Gemfile
80
+ - LICENSE.txt
32
81
  - README.rdoc
33
- has_rdoc: true
82
+ - Rakefile
83
+ - lib/rack/response_signature.rb
84
+ - rack-response-signature.gemspec
85
+ - spec/lib/rack/response_signature_spec.rb
86
+ - spec/spec_helper.rb
34
87
  homepage: http://github.com/nbibler/rack_response_signature
35
- licenses: []
36
-
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
37
91
  post_install_message:
38
92
  rdoc_options: []
39
-
40
- require_paths:
93
+ require_paths:
41
94
  - lib
42
- required_ruby_version: !ruby/object:Gem::Requirement
43
- none: false
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- hash: 3
48
- segments:
49
- - 0
50
- version: "0"
51
- required_rubygems_version: !ruby/object:Gem::Requirement
52
- none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- hash: 3
57
- segments:
58
- - 0
59
- version: "0"
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
60
105
  requirements: []
61
-
62
106
  rubyforge_project:
63
- rubygems_version: 1.3.7
107
+ rubygems_version: 2.0.6
64
108
  signing_key:
65
- specification_version: 3
109
+ specification_version: 4
66
110
  summary: Rack middleware to add transparent response signing
67
- test_files: []
68
-
111
+ test_files:
112
+ - spec/lib/rack/response_signature_spec.rb
113
+ - spec/spec_helper.rb