signed_form 0.0.1.pre1

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: a3cc9c5608222124cd6ed26784e343bec0c7d8c0
4
+ data.tar.gz: bed58f01bae751fefee75375fc9584a0fec4ea44
5
+ SHA512:
6
+ metadata.gz: c2a685ab4786e2b6615f32d8431108fd1d4601cb180caeddb09c05de8b774e8f4f6a5066017fb59406b6687ac95baa5837350efa1560936ecb0986c512fa6eb3
7
+ data.tar.gz: fa2731445d7c927966bfb1177a11a120282a907e77c9e493527ec9ce75a9405183d0f6aa6084602ab27265c79d961dd89562a09006f2e85b8d68964f4e59a348
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/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ before_install: gem install bundler
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in signed_form.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Erich Menge
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,98 @@
1
+ # SignedForm
2
+
3
+ SignedForm brings new convenience and security to your Rails 4 or Rails 3 application.
4
+
5
+ ## How It Works
6
+
7
+ Traditionally, when you create a form with Rails you enter your fields using something like `f.input :name` and etc.
8
+ Once you're done making your form you need to make sure that you've either set those parameters as accessible in the
9
+ model (Rails 3) or use `permit` (Rails 4). This is redundant. Why would you make a form for a user to fill out and
10
+ then not accept their input? Thus you need to always maintain this synchronization.
11
+
12
+ SignedForm generates a list of attributes that you have in your form and attaches them to be submitted with the form
13
+ along with a HMAC-SHA1 signature of those attributes to protect them from tampering. That means no more `permit` and
14
+ no more `attr_accessible`. It just works.
15
+
16
+ What this looks like:
17
+
18
+ ``` erb
19
+ <%= signed_form_for(@user) do |f| %>
20
+ <%= f.text_field :name %>
21
+ <%= f.text_field :address %>
22
+ <%= f.submit %>
23
+ <% end %>
24
+ ```
25
+
26
+ ``` ruby
27
+ UserController < ApplicationController
28
+ def create
29
+ @user = User.find params[:id]
30
+ @user.update_attributes params[:user]
31
+ end
32
+ end
33
+ ```
34
+
35
+ That's it. You're done. Need to add a field? Pop it in the form. You don't need to then update a list of attributes.
36
+ `signed_form_for` works just like the standard `form_for`.
37
+
38
+ Of course, you're free to continue using the standard `form_for`. `SignedForm` is strictly opt-in. It won't change the
39
+ way you use standard forms.
40
+
41
+ ## Alpha Quality Software
42
+
43
+ Because of the security sensitive nature of this gem I'm releasing this as 0.0.1.pre1 until I can get some more eyeballs
44
+ on the code. This software should not be considered production ready. At this time it is only suitable for
45
+ experimentation.
46
+
47
+ Now that I've made that disclaimer, you should know that SignedForm is functional.
48
+
49
+ ## Requirements
50
+
51
+ SignedForm requires:
52
+
53
+ * Ruby 1.9 or later
54
+ * Ruby on Rails 4 or 3 ([strong_parameters](https://github.com/rails/strong_parameters) gem required for Rails 3)
55
+
56
+ ## Installation
57
+
58
+ Add this line to your application's Gemfile:
59
+
60
+ gem 'signed_form', '0.0.1.pre1'
61
+
62
+ And then execute:
63
+
64
+ $ bundle
65
+
66
+ If you're using Rails 3, you'll also need to install the [strong_parameters](https://github.com/rails/strong_parameters)
67
+ gem. Please set it up as instructed on the linked GitHub repo.
68
+
69
+ If you're using Rails 4, it works out of the box.
70
+
71
+ You'll also need to create an initializer:
72
+
73
+ $ echo 'SignedForm::HMAC.secret_key = SecureRandom.hex(64)' > config/initializers/signed_form.rb
74
+
75
+ **IMPORTANT** Please read below for information regarding this secret key.
76
+
77
+ ## Special Considerations
78
+
79
+ If you're running only a single application server the above initializer should work great for you, with a couple of
80
+ caveats. If a user is in process of filling out a form and you restart your server, their form will be invalidated.
81
+ You could pick a secret key using `rake secret` and put that in the initializer instead, but then in the event you
82
+ remove a field someone could still access it using the old signature if some malicious person were to keep it around.
83
+
84
+ If you're running multiple application servers, the above initializer will not work. You'll need to keep the key in sync
85
+ between all the servers. The security caveat with that is that if you ever remove a field from a form without updating
86
+ that secret key, a malicious user could still access the field with the old signature. So you'll probably want to
87
+ choose a new secret in the event you remove access to an attribute in a form.
88
+
89
+ My above initializer example errs on the side of caution, generating a new secret key every time the app starts up. Only
90
+ you can decide what is right for you with respect to the secret key.
91
+
92
+ ## Contributing
93
+
94
+ 1. Fork it
95
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
96
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
97
+ 4. Push to the branch (`git push origin my-new-feature`)
98
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ module SignedForm
2
+ module PermitSignedParams
3
+ def permit_signed_form_data
4
+ return if request.method == 'GET' || params['form_signature'].blank?
5
+
6
+ data, signature = params['form_signature'].split('--', 2)
7
+
8
+ data ||= ''
9
+ signature ||= ''
10
+
11
+ raise Errors::InvalidSignature, "Form signature is not valid" unless SignedForm::HMAC.verify_hmac signature, data
12
+ allowed_attributes = Marshal.load Base64.strict_decode64(data)
13
+
14
+ allowed_attributes.each do |k, v|
15
+ params[k] = params.require(k).permit(*v)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ ActionController::Base.send :include, SignedForm::PermitSignedParams
22
+ ActionController::Base.prepend_before_filter :permit_signed_form_data
23
+
@@ -0,0 +1,17 @@
1
+ module SignedForm
2
+ module ActionView
3
+ module FormHelper
4
+ def signed_form_for(record, options = {}, &block)
5
+ options[:builder] = SignedForm::FormBuilder
6
+
7
+ form_for(record, options) do |f|
8
+ output = capture(f, &block)
9
+ f.form_signature_tag + output
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ ActionView::Base.send :include, SignedForm::ActionView::FormHelper
17
+
@@ -0,0 +1,6 @@
1
+ module SignedForm
2
+ module Errors
3
+ class NoSecretKey < StandardError; end
4
+ class InvalidSignature < StandardError; end
5
+ end
6
+ end
@@ -0,0 +1,43 @@
1
+ module SignedForm
2
+ class FormBuilder < ::ActionView::Helpers::FormBuilder
3
+ attr_accessor :signed_attributes, :signed_attributes_object
4
+
5
+ # Rails 3 uses strings, Rails 4 uses symbols
6
+ (field_helpers.map(&:to_s) - %w(label fields_for)).each do |h|
7
+ define_method(h) do |field, *args|
8
+ signed_attributes_object << field
9
+ super(field, *args)
10
+ end
11
+ end
12
+
13
+ def initialize(*)
14
+ super
15
+ if options[:signed_attributes_object]
16
+ self.signed_attributes_object = options[:signed_attributes_object]
17
+ else
18
+ self.signed_attributes = HashWithIndifferentAccess.new(object_name => [])
19
+ self.signed_attributes_object = signed_attributes[object_name]
20
+ end
21
+ end
22
+
23
+ def form_signature_tag
24
+ encoded_data = Base64.strict_encode64 Marshal.dump(signed_attributes)
25
+ signature = SignedForm::HMAC::create_hmac(encoded_data)
26
+ token = "#{encoded_data}--#{signature}"
27
+ %(<input type="hidden" name="form_signature" value="#{token}" />\n).html_safe
28
+ end
29
+
30
+ def fields_for(record_name, record_object = nil, fields_options = {}, &block)
31
+ hash = HashWithIndifferentAccess.new
32
+ if nested_attributes_association?(record_name)
33
+ hash["#{record_name}_attributes"] = fields_options[:signed_attributes_object] = []
34
+ else
35
+ hash[record_name] = fields_options[:signed_attributes_object] = []
36
+ end
37
+
38
+ signed_attributes_object << hash
39
+ super
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,36 @@
1
+ require 'openssl'
2
+
3
+ module SignedForm
4
+ module HMAC
5
+ class << self
6
+ attr_accessor :secret_key
7
+
8
+ def create_hmac(data)
9
+ if secret_key.nil? || secret_key.empty?
10
+ raise Errors::NoSecretKey, "Please consult the README for instructions on creating a secret key"
11
+ end
12
+
13
+ OpenSSL::HMAC.hexdigest OpenSSL::Digest::SHA1.new, secret_key, data
14
+ end
15
+
16
+ def verify_hmac(signature, data)
17
+ if secret_key.nil? || secret_key.empty?
18
+ raise Errors::NoSecretKey, "Please consult the README for instructions on creating a secret key"
19
+ end
20
+
21
+ secure_compare OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret_key, data), signature
22
+ end
23
+
24
+ # After the Rack implementation
25
+ def secure_compare(a, b)
26
+ return false unless a.bytesize == b.bytesize
27
+
28
+ l = a.unpack("C*")
29
+
30
+ r, i = 0, -1
31
+ b.each_byte { |v| r |= v ^ l[i+=1] }
32
+ r == 0
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ module SignedForm
2
+ MAJOR = 0
3
+ MINOR = 0
4
+ PATCH = 1
5
+ PRE = 'pre1'
6
+
7
+ VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join '.'
8
+ end
9
+
@@ -0,0 +1,7 @@
1
+ require "signed_form/version"
2
+ require "signed_form/errors"
3
+ require "signed_form/form_builder"
4
+ require "signed_form/hmac"
5
+ require "signed_form/action_view/form_helper"
6
+ require "signed_form/action_controller/permit_signed_params"
7
+
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'signed_form/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "signed_form"
8
+ spec.version = SignedForm::VERSION
9
+ spec.authors = ["Erich Menge"]
10
+ spec.email = ["erichmenge@gmail.com"]
11
+ spec.description = %q{Rails signed form security}
12
+ spec.summary = %q{Rails signed form security}
13
+ spec.homepage = "https://github.com/erichmenge/signed_form"
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
+ spec.add_development_dependency "rspec", "~> 2.13"
24
+
25
+ spec.add_dependency "actionpack", ">= 3.0"
26
+
27
+ spec.required_ruby_version = '>= 1.9'
28
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe SignedForm::HMAC do
4
+ describe 'create_hmac' do
5
+ it 'should raise if no key is given' do
6
+ expect { SignedForm::HMAC.create_hmac "foo" }.to raise_error(SignedForm::Errors::NoSecretKey)
7
+ end
8
+
9
+ context 'when a key is present' do
10
+ before { SignedForm::HMAC.secret_key = "superdupersecret" }
11
+ after { SignedForm::HMAC.secret_key = nil }
12
+
13
+ it 'should create a hex signature' do
14
+ SignedForm::HMAC.create_hmac("my signed message").length.should == 40
15
+ end
16
+ end
17
+ end
18
+
19
+ describe 'verify_hmac' do
20
+ it 'should raise if no key is given' do
21
+ expect { SignedForm::HMAC.verify_hmac 'foo', 'bar' }.to raise_error(SignedForm::Errors::NoSecretKey)
22
+ end
23
+
24
+ context 'when a key is present' do
25
+ before { SignedForm::HMAC.secret_key = "superdupersecret" }
26
+ after { SignedForm::HMAC.secret_key = nil }
27
+
28
+ let(:signature) { SignedForm::HMAC.create_hmac "My super secret" }
29
+
30
+ specify { SignedForm::HMAC.verify_hmac(signature, "My super secret").should be_true }
31
+ specify { SignedForm::HMAC.verify_hmac(signature, "My bad secret").should_not be_true }
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,11 @@
1
+ require 'action_view'
2
+ require 'action_controller'
3
+ require 'signed_form'
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+ config.filter_run :focus
9
+
10
+ config.order = 'random'
11
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: signed_form
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre1
5
+ platform: ruby
6
+ authors:
7
+ - Erich Menge
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-03-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
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: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: actionpack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Rails signed form security
70
+ email:
71
+ - erichmenge@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - .travis.yml
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/signed_form.rb
84
+ - lib/signed_form/action_controller/permit_signed_params.rb
85
+ - lib/signed_form/action_view/form_helper.rb
86
+ - lib/signed_form/errors.rb
87
+ - lib/signed_form/form_builder.rb
88
+ - lib/signed_form/hmac.rb
89
+ - lib/signed_form/version.rb
90
+ - signed_form.gemspec
91
+ - spec/signed_form/hmac_spec.rb
92
+ - spec/spec_helper.rb
93
+ homepage: https://github.com/erichmenge/signed_form
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '1.9'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>'
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.1
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.0.0
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Rails signed form security
117
+ test_files:
118
+ - spec/signed_form/hmac_spec.rb
119
+ - spec/spec_helper.rb