rack-response-signature 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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