uri_signature 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8a1482fdd0e2a6698ec0b611e9a54a898674e065
4
+ data.tar.gz: 7477e503fe4028a3055fc524c407c6f226ba434b
5
+ SHA512:
6
+ metadata.gz: 99b51e00cae68744b8204a2b530cdd7f7725e7c9aca3193a660151d53b24afb2e420a60a7bd8da261f93b99f327758d869c29feb13dafc4f8ccc4f621018aa05
7
+ data.tar.gz: 33f270cbc220e79521eb8ee37a6d2c4086a55d65c43bca560f54fa424552f15949ea6dd7cfba37004b9ef7ba2f14a8bbe9346f353fe1cfd9d5f5d34613686901
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in uri_signature.gemspec
4
+ gemspec
@@ -0,0 +1,41 @@
1
+ # UriSignature
2
+
3
+ A general purpose way of signing a URL when you need the signature to be added
4
+ to the URL. This is useful when you need to redirect a user from one service to
5
+ another and communicate information without any risk of tampering. It's based
6
+ on a shared secret and HMAC being used to sign the URL.
7
+
8
+ This could be used in techniques described in http://broadcast.oreilly.com/2009/12/principles-for-standardized-rest-authentication.html
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'uri_signature'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install uri_signature
25
+
26
+ ## Usage
27
+
28
+ ```ruby
29
+ pry(main)> require 'uri_signature'
30
+
31
+ pry(main)> signed_uri = URISignature.sign("http://foobar.com?my_trusted_information=helloworld", expiry: 300, key: "my_shared_secret_key").to_s
32
+ => "http://foobar.com?my_trusted_information=helloworld&signature=b99f3d00610361be49327938b82802c933aa4fac&signature_expires=1444691758"
33
+
34
+ pry(main)> URISignature.valid?(signed_uri, key: "my_shared_secret_key")
35
+ => true
36
+ ```
37
+
38
+ ## Contributing
39
+
40
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dgvz/uri_signature.
41
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,101 @@
1
+ require 'uri_signature/version'
2
+ require 'addressable/uri'
3
+
4
+ class URISignature
5
+ class SignatureError < StandardError; end
6
+ class InvalidSignatureError < SignatureError; end
7
+ class ExpiredSignatureError < SignatureError; end
8
+
9
+ MINUTE = 60
10
+
11
+ # Sign the given URI and return a signed URI.
12
+ #
13
+ # @param uri [String] a valid URI
14
+ # @param expiry [Integer] How many seconds before the signature expires.
15
+ # @param key [String] The key used to sign the uri
16
+ # @return [String] The signed URI. The signature is added to the query
17
+ # params.
18
+ def self.sign(uri, expiry: 5 * MINUTE, key: ENV['HMAC_SIGNATURE_KEY'])
19
+ uri = Addressable::URI.parse(uri)
20
+
21
+ query_values = (uri.query_values || {}).to_a
22
+
23
+ query_values << ["signature_expires", (Time.now + expiry).tv_sec]
24
+
25
+ # Sort by the key to guarantee URI is always constructed in the same way
26
+ query_values = query_values.sort_by { |k, v| k }
27
+
28
+ uri.query_values = query_values
29
+
30
+ add_signature(uri, key).to_s
31
+ uri.to_s
32
+ end
33
+
34
+ # Is the given uri correctly signed.
35
+ #
36
+ # @param uri [String]
37
+ # @param raise_error [Boolean] Defaults to to true. Set this to false if
38
+ # you don't this to raise but rather return false if it's invalid. We
39
+ # default raise_error to true to be extra cautious as wanting to handle
40
+ # incorrectly signed callbacks is probably an edge case.
41
+ # @return [TrueClass, FalseClass] Will return true if correctly signed.
42
+ # Otherwise it will raise. If you really don't want it to raise you can
43
+ # pass `raise_error: false`.
44
+ # @raise [URISignature::SignatureError] if for any reason the
45
+ # signature is invalid. This can be disabled by setting
46
+ # `:raise_error => false`.
47
+ def self.valid?(uri, key: ENV['HMAC_SIGNATURE_KEY'], raise_error: true)
48
+
49
+ # First remove the signature
50
+ comparison_uri = Addressable::URI.parse(uri).clone.tap do |u|
51
+ query_values = u.query_values.clone
52
+ query_values.delete("signature")
53
+ u.query_values = query_values
54
+ end
55
+
56
+ # Then recalculate and add back in the signature
57
+ add_signature(comparison_uri, key)
58
+
59
+ # Compare the uri to the original
60
+ if secure_compare(uri, comparison_uri.to_s)
61
+ expires = Time.at(comparison_uri.query_values["signature_expires"].to_i)
62
+ if expires < Time.now
63
+ raise URISignature::ExpiredSignatureError, "The signature for #{uri} has expired."
64
+ end
65
+ true
66
+ else
67
+ raise URISignature::InvalidSignatureError, "Invalid signature provided in #{uri}."
68
+ end
69
+ end
70
+
71
+ # @!private
72
+ # This method is meant to only be used in this class. Do not use it externally.
73
+ # @param uri [String]
74
+ # @param key [String]
75
+ def self.add_signature(uri, key)
76
+ unsigned_uri = uri.to_s
77
+
78
+ digest = OpenSSL::Digest.new('sha1')
79
+ hmac = OpenSSL::HMAC.hexdigest(digest, key, unsigned_uri)
80
+
81
+ query_values = uri.query_values.to_a
82
+ query_values << ["signature", hmac]
83
+
84
+ # Sort again to ensure consistency
85
+ uri.query_values = query_values.sort_by { |k, v| k }
86
+ end
87
+
88
+ # Use a secure string comparison method to avoid timing attacks
89
+ # (https://en.wikipedia.org/wiki/Timing_attack)
90
+ def self.secure_compare(a, b)
91
+ return false if a.empty? || b.empty? || a.bytesize != b.bytesize
92
+ l = a.unpack "C#{a.bytesize}"
93
+
94
+ res = 0
95
+ b.each_byte { |byte| res |= byte ^ l.shift }
96
+ res == 0
97
+ end
98
+
99
+ private_class_method :add_signature
100
+ private_class_method :secure_compare
101
+ end
@@ -0,0 +1,3 @@
1
+ module UriSignature
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'uri_signature/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "uri_signature"
8
+ spec.version = UriSignature::VERSION
9
+ spec.authors = ["Dylan Griffith"]
10
+ spec.email = ["dyl.griffith@gmail.com"]
11
+
12
+ spec.summary = %q{A general purpose way of signing a URL when you need the signature to be added to the URL.}
13
+ spec.description = %q{A general purpose way of signing a URL when you need the signature to be added to the URL.}
14
+ spec.homepage = "https://github.com/dgvz/uri_signature"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "addressable"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.10"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec"
26
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uri_signature
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dylan Griffith
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-10-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A general purpose way of signing a URL when you need the signature to
70
+ be added to the URL.
71
+ email:
72
+ - dyl.griffith@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - README.md
82
+ - Rakefile
83
+ - lib/uri_signature.rb
84
+ - lib/uri_signature/version.rb
85
+ - uri_signature.gemspec
86
+ homepage: https://github.com/dgvz/uri_signature
87
+ licenses: []
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.2.2
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: A general purpose way of signing a URL when you need the signature to be
109
+ added to the URL.
110
+ test_files: []