breach-mitigation-rails 0.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in breach-mitigation-rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 BBA, Inc.
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.md ADDED
@@ -0,0 +1,58 @@
1
+ # breach-mitigation-rails
2
+
3
+ Makes Rails applications less susceptible to the BREACH / CRIME
4
+ attacks. See [breachattack.com](http://breachattack.com/) for details.
5
+
6
+ ## How it works
7
+
8
+ This gem implements two of the suggestion mitigation strategies from
9
+ the paper:
10
+
11
+ *Masking Secrets*: The Rails CSRF token is 'masked' by encrypting it
12
+ with a 32-byte one-time pad, and the pad and encrypted token are
13
+ returned to the browser, instead of the "real" CSRF token. This only
14
+ protects the CSRF token from an attacker; it does not protect other
15
+ data on your pages (see the paper for details on this).
16
+
17
+ *Length Hiding*: The BreachMitigation::LengthHiding middleware
18
+ appends an HTML comment up to 2k in length to the end of all HTML
19
+ documents served by your app. As noted in the paper, this does not
20
+ prevent plaintext recovery, but it can slow the attack and it's
21
+ relatively inexpensive to implement. Unlike the CSRF token masking,
22
+ length hiding protects the entire page body from recovery.
23
+
24
+ ## Warning!
25
+
26
+ BREACH and CRIME are **complicated and wide-ranging attacks**, and this
27
+ gem offers only partial protection for Rails applications. If you're
28
+ concerned about the security of your web app, you should review the
29
+ BREACH paper and look for other, application-specific things you can
30
+ do to prevent or mitigate this class of attacks.
31
+
32
+ ## Installation
33
+
34
+ Add this line to your Rails Gemfile:
35
+
36
+ gem 'breach-mitigation-rails'
37
+
38
+ And then execute:
39
+
40
+ $ bundle
41
+
42
+ TODO And then?
43
+
44
+ ## Gotchas
45
+
46
+ length padding can screw up etags
47
+ length padding increases your bandwidth, could increase page load
48
+ times
49
+ if you have overridden verified_request? in your app, need to make sure you call +super+
50
+
51
+ ## Contributing
52
+
53
+ Pull requests are welcome, either to enhance the existing mitigation
54
+ strategies or to add new ways to mitigate against the attack.
55
+
56
+ ## License
57
+
58
+ MIT - see LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'breach_mitigation/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "breach-mitigation-rails"
8
+ spec.version = BreachMitigation::VERSION
9
+ spec.authors = ["Bradley Buda"]
10
+ spec.email = ["bradleybuda@gmail.com"]
11
+ spec.description = %q{Mitigates the BREACH and CRIME attacks on TLS in Rails applications}
12
+ spec.summary = %q{Uses length-hiding and CSRF token masking to make it more difficult for an attacker to recover plaintext from HTTP responses. See README.md for details.}
13
+ spec.homepage = "https://github.com/meldium/breach-mitigation-rails"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1 @@
1
+ require 'breach_mitigation/railtie' if defined?(Rails)
@@ -0,0 +1,50 @@
1
+ module BreachMitigation
2
+ class LengthHiding
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ status, headers, body = @app.call(env)
9
+
10
+ # Only pad HTML documents
11
+ if headers['Content-Type'] =~ /text\/html/
12
+ # Copy the existing response to a new object
13
+ response = Rack::Response.new(body, status, headers)
14
+
15
+ # Append to that response
16
+ response.write random_html_comment
17
+
18
+ body.close if body.respond_to? :close
19
+ response.finish
20
+ else
21
+ [status, headers, body]
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # Append a comment from 0 to MAX_LENGTH bytes in size to the
28
+ # response body. See section 3.1 of "BREACH: Reviving the CRIME
29
+ # attack". This should make BREACH attacks take longer, but does
30
+ # not fully protect against them. The longer MAX_LENGTH is, the
31
+ # more effective the mitigation is, however longer lengths mean
32
+ # more time spent in this middleware and more data on the wire.
33
+
34
+ MAX_LENGTH = 2048
35
+ ALPHABET = ('a'..'z').to_a
36
+
37
+ def random_html_comment
38
+ # The length of the padding should be strongly random, but the
39
+ # data itself doesn't need to be strongly random; it just needs
40
+ # to be resistant to compression
41
+ length = SecureRandom.random_number(1024)
42
+
43
+ # TODO make this faster
44
+ junk = ''
45
+ length.times { junk << ALPHABET.sample }
46
+
47
+ "\n<!-- This is a random-length HTML comment: #{junk} -->"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ module BreachMitigation
2
+ class MaskingSecrets
3
+ class << self
4
+ AUTHENTICITY_TOKEN_LENGTH = 32
5
+
6
+ # Sets the token value for the current session and returns it in
7
+ # a masked form that's safe to send to the client. See section
8
+ # 3.4 of "BREACH: Reviving the CRIME attack".
9
+ def masked_authenticity_token(session)
10
+ one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
11
+ encrypted_csrf_token = xor_byte_strings(one_time_pad, real_csrf_token(session))
12
+ masked_token = one_time_pad + encrypted_csrf_token
13
+ Base64.strict_encode64(masked_token)
14
+ end
15
+
16
+ # Checks the client's masked token to see if it matches the
17
+ # session token. Essentially the inverse of
18
+ # +masked_authenticity_token+.
19
+ def valid_authenticity_token?(session, encoded_masked_token)
20
+ return false if encoded_masked_token.nil? || encoded_masked_token.empty?
21
+
22
+ masked_token = Base64.strict_decode64(encoded_masked_token)
23
+
24
+ return false if masked_token.length != AUTHENTICITY_TOKEN_LENGTH * 2
25
+
26
+ # Split the token into the one-time pad and the encrypted
27
+ # value and decrypt it
28
+ one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
29
+ encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
30
+ csrf_token = xor_byte_strings(one_time_pad, encrypted_csrf_token)
31
+
32
+ csrf_token == real_csrf_token(session)
33
+ end
34
+
35
+ private
36
+
37
+ def real_csrf_token(session)
38
+ session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
39
+ Base64.strict_decode64(session[:_csrf_token])
40
+ end
41
+
42
+ def xor_byte_strings(s1, s2)
43
+ s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,28 @@
1
+ require 'breach_mitigation/length_hiding'
2
+ require 'breach_mitigation/masking_secrets'
3
+
4
+ module BreachMitigation
5
+ class Railtie < Rails::Railtie
6
+ initializer "breach-mitigation-rails.insert_middleware" do |app|
7
+ app.config.middleware.use "BreachMitigation::LengthHiding"
8
+ end
9
+ end
10
+ end
11
+
12
+ # Monkey-patch ActionController::RequestForgeryProtection to use
13
+ # masked CSRF tokens
14
+ module ActionController
15
+ module RequestForgeryProtection
16
+ protected
17
+
18
+ def verified_request?
19
+ !protect_against_forgery? || request.get? || request.head? ||
20
+ BreachMitigation::MaskingSecrets.valid_authenticity_token?(session, params[request_forgery_protection_token]) ||
21
+ BreachMitigation::MaskingSecrets.valid_authenticity_token?(session, request.headers['X-CSRF-Token'])
22
+ end
23
+
24
+ def form_authenticity_token
25
+ BreachMitigation::MaskingSecrets.masked_authenticity_token(session)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module BreachMitigation
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: breach-mitigation-rails
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Bradley Buda
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ none: false
21
+ name: bundler
22
+ type: :development
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ version: '1.3'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ none: false
37
+ name: rake
38
+ type: :development
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ none: false
46
+ description: Mitigates the BREACH and CRIME attacks on TLS in Rails applications
47
+ email:
48
+ - bradleybuda@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - breach-mitigation-rails.gemspec
59
+ - lib/breach-mitigation-rails.rb
60
+ - lib/breach_mitigation/length_hiding.rb
61
+ - lib/breach_mitigation/masking_secrets.rb
62
+ - lib/breach_mitigation/railtie.rb
63
+ - lib/breach_mitigation/version.rb
64
+ homepage: https://github.com/meldium/breach-mitigation-rails
65
+ licenses:
66
+ - MIT
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ none: false
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ none: false
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 1.8.23
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Uses length-hiding and CSRF token masking to make it more difficult for an
89
+ attacker to recover plaintext from HTTP responses. See README.md for details.
90
+ test_files: []
91
+ has_rdoc: