This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | ## |
||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||
2 | # Copyright (c) 2015-2016 WePay. |
||
3 | # |
||
4 | # Based on a stripped-down version of the AWS Signature v4 implementation. |
||
5 | # |
||
6 | # http://opensource.org/licenses/Apache2.0 |
||
7 | ## |
||
8 | |||
9 | 1 | require 'openssl' |
|
10 | |||
11 | ## |
||
12 | # The root WePay namespace. |
||
13 | ## |
||
14 | 1 | module WePay |
|
15 | |||
16 | ## |
||
17 | # The Signer class is designed for those who are signing data on behalf of a public-private keypair. |
||
18 | # |
||
19 | # In principle, the "client party" has public key (i.e., `client_id`) has a matching private key |
||
20 | # (i.e., `client_secret`) that can be verified by both the signer, as well as the client, but |
||
21 | # by nobody else as we don't want to make forgeries possible. |
||
22 | # |
||
23 | # The "signing party" has a simple an identifier which acts as an additional piece of entropy in the |
||
24 | # algorithm, and can help differentiate between multiple signing parties if the client party does |
||
25 | # something like try to use the same public-private keypair independently of a signing party |
||
26 | # (as is common with GPG signing). |
||
27 | # |
||
28 | # For example, in the original AWS implementation, the "self key" for AWS was "AWS4". |
||
29 | ## |
||
30 | 1 | class Signer |
|
31 | |||
32 | 1 | attr_reader :self_key |
|
33 | 1 | attr_reader :client_id |
|
34 | 1 | attr_reader :client_secret |
|
35 | 1 | attr_reader :hash_algo |
|
36 | |||
37 | ## |
||
38 | # Constructs a new instance of this class. |
||
39 | # |
||
40 | # @param client_id [String] A string which is the public portion of the keypair identifying the client party. The |
||
41 | # pairing of the public and private portions of the keypair should only be known to the client party and the |
||
42 | # signing party. |
||
43 | # @param client_secret [String] A string which is the private portion of the keypair identifying the client party. |
||
44 | # The pairing of the public and private portions of the keypair should only be known to the client party and |
||
45 | # the signing party. |
||
46 | # @option options [String] self_key (WePay) A string which identifies the signing party and adds additional entropy. |
||
47 | # @option options [String] hash_algo (sha512) The hash algorithm to use for signing. |
||
48 | ## |
||
49 | 1 | def initialize(client_id, client_secret, options = {}) |
|
50 | @client_id = client_id.to_s |
||
51 | @client_secret = client_secret.to_s |
||
52 | |||
53 | options = { |
||
54 | :self_key => 'WePay', |
||
55 | :hash_algo => 'sha512', |
||
56 | }.merge(options) |
||
57 | |||
58 | @self_key = options[:self_key].to_s |
||
59 | @hash_algo = options[:hash_algo].to_s |
||
60 | end |
||
61 | |||
62 | ## |
||
63 | # Sign the payload to produce a signature for its contents. |
||
64 | # |
||
65 | # @param payload [Hash] The data to generate a signature for. |
||
66 | # @option payload [required, String] token The one-time-use token. |
||
67 | # @option payload [required, String] page The WePay URL to access. |
||
68 | # @option payload [required, String] redirect_uri The partner URL to return to once the action is completed. |
||
69 | # @return [String] The signature for the payload contents. |
||
70 | ## |
||
71 | 1 | def sign(payload) |
|
72 | payload = payload.merge({ |
||
73 | :client_id => @client_id, |
||
74 | :client_secret => @client_secret, |
||
75 | }) |
||
76 | |||
77 | scope = create_scope |
||
78 | context = create_context(payload) |
||
79 | s2s = create_string_to_sign(scope, context) |
||
80 | signing_key = get_signing_salt |
||
81 | OpenSSL::HMAC.hexdigest(@hash_algo, signing_key, s2s) |
||
82 | end |
||
83 | |||
84 | ## |
||
85 | # Signs and generates the query string URL parameters to use when making a request. |
||
86 | # |
||
87 | # If the `client_secret` key is provided, then it will be automatically excluded from the result. |
||
88 | # |
||
89 | # @param payload [Hash] The data to generate a signature for. |
||
90 | # @option payload [required, String] token The one-time-use token. |
||
91 | # @option payload [required, String] page The WePay URL to access. |
||
92 | # @option payload [required, String] redirect_uri The partner URL to return to once the action is completed. |
||
93 | # @return [String] The query string parameters to append to the end of a URL. |
||
94 | ## |
||
95 | 1 | def generate_query_string_params(payload) |
|
96 | payload.delete(:client_secret) if payload.has_key? :client_secret |
||
97 | |||
98 | signed_token = sign(payload) |
||
99 | payload[:client_id] = @client_id |
||
100 | payload[:stoken] = signed_token |
||
101 | qsa = [] |
||
102 | |||
103 | payload.keys.sort.each do | key | |
||
104 | qsa.push sprintf("%s=%s", key, payload[key]) |
||
105 | end |
||
106 | |||
107 | qsa.join("&") |
||
108 | end |
||
109 | |||
110 | 1 | private |
|
111 | |||
112 | ## |
||
113 | # Creates the string-to-sign based on a variety of factors. |
||
114 | # |
||
115 | # @param scope [String] The results of a call to the `create_scope()` method. |
||
116 | # @param context [String] The results of a call to the `create_context()` method. |
||
117 | # @return [String] The final string to be signed. |
||
118 | ## |
||
119 | 1 | def create_string_to_sign(scope, context) |
|
120 | scope_hash = OpenSSL::Digest.new(@hash_algo, scope) |
||
121 | context_hash = OpenSSL::Digest.new(@hash_algo, context) |
||
122 | sprintf "SIGNER-HMAC-%s\n%s\n%s\n%s\n%s", @hash_algo.upcase, @self_key, @client_id, scope_hash, context_hash |
||
123 | end |
||
124 | |||
125 | ## |
||
126 | # An array of key-value pairs representing the data that you want to sign. |
||
127 | # All values must be `scalar`. |
||
128 | # |
||
129 | # @param payload [Hash] The data that you want to sign. |
||
130 | # @option payload [String] self_key (WePay) A string which identifies the signing party and adds additional entropy. |
||
131 | # @return [String] A canonical string representation of the data to sign. |
||
132 | ## |
||
133 | 1 | def create_context(payload) |
|
134 | canonical_payload = [] |
||
135 | |||
136 | payload.keys.each do | key | |
||
137 | val = payload[key].downcase |
||
138 | key = key.downcase |
||
139 | canonical_payload.push(sprintf "%s=%s\n", key, val) |
||
140 | end |
||
141 | |||
142 | canonical_payload.sort! |
||
143 | |||
144 | signed_headers_string = payload.keys.sort_by {|s| s.to_s }.join(";") |
||
145 | canonical_payload.join("") + "\n" + signed_headers_string |
||
146 | end |
||
147 | |||
148 | ## |
||
149 | # Gets the salt value that should be used for signing. |
||
150 | # |
||
151 | # @return [String] The signing salt. |
||
152 | ## |
||
153 | 1 | def get_signing_salt |
|
154 | self_key_sign = OpenSSL::HMAC.digest(@hash_algo, @client_secret, @self_key) |
||
155 | client_id_sign = OpenSSL::HMAC.digest(@hash_algo, self_key_sign, @client_id) |
||
156 | OpenSSL::HMAC.digest(@hash_algo, client_id_sign, 'signer') |
||
157 | end |
||
158 | |||
159 | ## |
||
160 | # Creates the "scope" in which the signature is valid. |
||
161 | # |
||
162 | # @return [String] The string which represents the scope in which the signature is valid. |
||
163 | ## |
||
164 | 1 | def create_scope |
|
165 | sprintf "%s/%s/signer", @self_key, @client_id |
||
166 | end |
||
167 | |||
168 | end |
||
169 | end |
||
170 |