safe_cookies 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 safe_cookies.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Dominik Schöler
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,44 @@
1
+ # SafeCookies
2
+
3
+ This Gem brings a Middleware that will make all cookies secure. In detail, it will
4
+
5
+ * set all new cookies 'HttpOnly', unless specified otherwise
6
+ * set all new cookies 'secure', if the request came via HTTPS and not specified otherwise
7
+ * rewrite existing cookies, setting both flags as above
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'safe_cookies'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install safe_cookies
22
+
23
+ ## Usage
24
+
25
+ In config/environment.rb:
26
+
27
+ config.middleware.use SafeCookies::Middleware,
28
+ :remember_token => 1.year,
29
+ :last_action => 30.days,
30
+ :non_secure => %w[default_language],
31
+ :non_http_only => %w[javascript_data]
32
+
33
+ This will have the `default_language` cookie not made secure, the `javascript_data` cookie
34
+ not made HttpOnly. It will update the `remember_token` with an expiry of one year and the
35
+ `last_action` cookie with an expiry of 30 days, making both of them secure and HttpOnly.
36
+
37
+ ## About Rails and Cookies
38
+
39
+ Cookie syntax example:
40
+
41
+ Set-Cookie: cookie1=value; secure,cookie2=value; path=/
42
+
43
+ Actually, there should be one cookie per Set-Cookie header, but since Rails headers
44
+ are implemented as Hash, it is not possible to have several Set-Cookie fields.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ desc 'Default: Run all tests.'
5
+ task :default => 'specs' # now you can run the tests by just typing "rake" into your console
6
+
7
+ desc 'Run specs'
8
+ task :specs do
9
+ system('rspec')
10
+ end
@@ -0,0 +1,3 @@
1
+ module SafeCookies
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,122 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require "safe_cookies/version"
3
+ require "rack"
4
+
5
+ module SafeCookies
6
+ class Middleware
7
+
8
+
9
+ def initialize(app, options = {})
10
+ # Pass a hash for `cookies_to_update` with name as key and lifetime as value.
11
+ # Use this to update existing cookies that you expect to receive.
12
+ #
13
+ # Unfortunately, the client won't ever tell us if the cookie was originally
14
+ # sent with flags such as "secure" or which expiry date it currently has:
15
+ # http://tools.ietf.org/html/rfc6265#section-4.2.2
16
+ #
17
+ # The :non_secure option specifies cookies that will not be made secure. Use
18
+ # this for storing site usage settings like filters etc. that need to be available
19
+ # when not on HTTPS (this should rarely be the case).
20
+ #
21
+ # The :non_http_only option is analog, use it for storing data you want to access
22
+ # with javascript.
23
+
24
+ @app = app
25
+ @non_secure = (options.delete(:non_secure) || []).map(&:to_s)
26
+ @non_http_only = (options.delete(:non_http_only) || []).map(&:to_s)
27
+ @cookies_to_update = options
28
+ end
29
+
30
+ def call(env)
31
+ @env = env
32
+ status, headers, body = @app.call(@env)
33
+
34
+ secure_old_cookies!(headers) if @cookies_to_update.any?
35
+ secure_new_cookies!(headers)
36
+
37
+ [ status, headers, body ]
38
+ end
39
+
40
+ private
41
+
42
+ def secure(cookie)
43
+ # Regexp from https://github.com/tobmatth/rack-ssl-enforcer/
44
+ if secure?(cookie) and cookie !~ /(^|;\s)secure($|;)/
45
+ "#{cookie}; secure"
46
+ else
47
+ cookie
48
+ end
49
+ end
50
+
51
+ def http_only(cookie)
52
+ if http_only?(cookie) and cookie !~ /(^|;\s)HttpOnly($|;)/
53
+ "#{cookie}; HttpOnly"
54
+ else
55
+ cookie
56
+ end
57
+ end
58
+
59
+ def secure_old_cookies!(headers)
60
+ request = Rack::Request.new(@env)
61
+ return if request.cookies['secured_old_cookies']
62
+
63
+ @cookies_to_update.each do |key, expiry|
64
+ key = key.to_s
65
+ if request.cookies.has_key?(key)
66
+ value = request.cookies[key]
67
+ set_secure_cookie!(headers, key, value, expiry)
68
+ end
69
+ end
70
+ set_secure_cookie!(headers, 'secured_old_cookies', Rack::Utils.rfc2822(Time.now.gmtime))
71
+ end
72
+
73
+ def set_secure_cookie!(headers, key, value, expire_after = 365 * 24 * 60 * 60) # one year
74
+ options = {
75
+ :value => value,
76
+ :secure => secure?(key),
77
+ :httponly => http_only?(key),
78
+ :expires => Time.now + expire_after # This is what Rails does
79
+ }
80
+ Rack::Utils.set_cookie_header!(headers, key, options)
81
+ end
82
+
83
+ def secure_new_cookies!(headers)
84
+ cookies = headers['Set-Cookie']
85
+ if cookies
86
+ # Rails 2.3 / Rack 1.1 offers an array which is actually nice.
87
+ cookies = cookies.split("\n") unless cookies.is_a?(Array)
88
+
89
+ # On Rack 1.1, cookie values sometimes contain trailing newlines.
90
+ # Example => ["foo=1; path=/\n", "bar=2; path=/"]
91
+ # Note that they also mess up browsers, when this array is merged
92
+ # again and the "Set-Cookie" header then contains double newlines.
93
+ cookies = cookies.
94
+ map(&:strip).
95
+ select{ |c| c.length > 0}.
96
+ map(&method(:secure)).
97
+ map(&method(:http_only))
98
+
99
+ # Unfortunately there is no pretty way to touch a "Set-Cookie" header.
100
+ # It contains more information than the "HTTP_COOKIE" header from the
101
+ # browser's request contained, so a `Rack::Request` can't parse it for
102
+ # us. A `Rack::Response` doesn't offer a way either.
103
+ headers['Set-Cookie'] = cookies.join(",") # cookies are comma-separated
104
+ end
105
+ end
106
+
107
+ def secure?(cookie)
108
+ name = cookie.split('=').first.strip
109
+ ssl_request? and not @non_secure.include?(name)
110
+ end
111
+
112
+ def http_only?(cookie)
113
+ name = cookie.split('=').first.strip
114
+ not @non_http_only.include?(name)
115
+ end
116
+
117
+ def ssl_request?
118
+ @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https' # this is how Rails does it
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/safe_cookies/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Dominik Schöler"]
6
+ gem.email = ["dominik.schoeler@makandra.de"]
7
+ gem.description = %q{Make cookies as `secure` and `HttpOnly` as possible.}
8
+ gem.summary = %q{Make cookies as `secure` and `HttpOnly` as possible.}
9
+ gem.homepage = "http://www.makandra.de"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "safe_cookies"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = SafeCookies::VERSION
17
+
18
+ gem.add_dependency('rack')
19
+ gem.add_development_dependency('rspec')
20
+ gem.add_development_dependency('timecop')
21
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safe_cookies
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dominik Schöler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: timecop
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Make cookies as `secure` and `HttpOnly` as possible.
63
+ email:
64
+ - dominik.schoeler@makandra.de
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE
72
+ - README.md
73
+ - Rakefile
74
+ - lib/safe_cookies.rb
75
+ - lib/safe_cookies/version.rb
76
+ - safe_cookies.gemspec
77
+ homepage: http://www.makandra.de
78
+ licenses: []
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.24
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Make cookies as `secure` and `HttpOnly` as possible.
101
+ test_files: []