uri_signature 0.1.0

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.
@@ -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: []