Class: Aws::Sigv4::Signer
- Inherits:
-
Object
- Object
- Aws::Sigv4::Signer
- Defined in:
- gems/aws-sigv4/lib/aws-sigv4/signer.rb
Overview
Utility class for creating AWS signature version 4 signature. This class provides two methods for generating signatures:
#sign_request - Computes a signature of the given request, returning the hash of headers that should be applied to the request.
#presign_url - Computes a presigned request with an expiration. By default, the body of this request is not signed and the request expires in 15 minutes.
Configuration
To use the signer, you need to specify the service, region, and credentials. The service name is normally the endpoint prefix to an AWS service. For example:
ec2.us-west-1.amazonaws.com => ec2
The region is normally the second portion of the endpoint, following the service name.
ec2.us-west-1.amazonaws.com => us-west-1
It is important to have the correct service and region name, or the signature will be invalid.
Credentials
The signer requires credentials. You can configure the signer with static credentials:
signer = Aws::Sigv4::Signer.new(
service: 's3',
region: 'us-east-1',
# static credentials
access_key_id: 'akid',
secret_access_key: 'secret'
)
You can also provide refreshing credentials via the :credentials_provider
.
If you are using the AWS SDK for Ruby, you can use any of the credential
classes:
signer = Aws::Sigv4::Signer.new(
service: 's3',
region: 'us-east-1',
credentials_provider: Aws::InstanceProfileCredentials.new
)
Other AWS SDK for Ruby classes that can be provided via :credentials_provider
:
Aws::Credentials
Aws::SharedCredentials
Aws::InstanceProfileCredentials
Aws::AssumeRoleCredentials
Aws::ECSCredentials
A credential provider is any object that responds to #credentials
returning another object that responds to #access_key_id
, #secret_access_key
,
and #session_token
.
Constant Summary collapse
- @@use_crt =
begin require 'aws-crt' true rescue LoadError false end
Instance Attribute Summary collapse
-
#apply_checksum_header ⇒ Boolean
readonly
When
true
thex-amz-content-sha256
header will be signed and returned in the signature headers. -
#credentials_provider ⇒ #credentials
readonly
Returns an object that responds to
#credentials
, returning an object that responds to the following methods:. -
#region ⇒ String
readonly
-
#service ⇒ String
readonly
-
#unsigned_headers ⇒ Set<String>
readonly
Returns a set of header names that should not be signed.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(options = {}) ⇒ Signer
constructor
A new instance of Signer.
-
#presign_url(options) ⇒ HTTPS::URI, HTTP::URI
Signs a URL with query authentication.
-
#sign_event(prior_signature, payload, encoder) ⇒ Object
Signs a event and returns signature headers and prior signature used for next event signing.
-
#sign_request(request) ⇒ Signature
Computes a version 4 signature signature.
Constructor Details
#initialize(service: , region: , access_key_id: , secret_access_key: , session_token: nil, **options) ⇒ Signer #initialize(service: , region: , credentials: , **options) ⇒ Signer #initialize(service: , region: , credentials_provider: , **options) ⇒ Signer
Returns a new instance of Signer.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 141 def initialize( = {}) @service = extract_service() @region = extract_region() @credentials_provider = extract_credentials_provider() @unsigned_headers = Set.new((.fetch(:unsigned_headers, [])).map(&:downcase)) @unsigned_headers << 'authorization' @unsigned_headers << 'x-amzn-trace-id' @unsigned_headers << 'expect' @uri_escape_path = .fetch(:uri_escape_path, true) @apply_checksum_header = .fetch(:apply_checksum_header, true) @signing_algorithm = .fetch(:signing_algorithm, :sigv4) @normalize_path = .fetch(:normalize_path, true) @omit_session_token = .fetch(:omit_session_token, false) if @signing_algorithm == :sigv4a && !Signer.use_crt? raise ArgumentError, 'You are attempting to sign a' \ ' request with sigv4a which requires the `aws-crt` gem.'\ ' Please install the gem or add it to your gemfile.' end end |
Instance Attribute Details
#apply_checksum_header ⇒ Boolean (readonly)
When true
the x-amz-content-sha256
header will be signed and
returned in the signature headers.
185 186 187 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 185 def apply_checksum_header @apply_checksum_header end |
#credentials_provider ⇒ #credentials (readonly)
Returns an object that responds to
#credentials
, returning an object that responds to the following
methods:
#access_key_id
=> String#secret_access_key
=> String#session_token
=> String, nil#set?
=> Boolean
177 178 179 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 177 def credentials_provider @credentials_provider end |
#region ⇒ String (readonly)
166 167 168 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 166 def region @region end |
#service ⇒ String (readonly)
163 164 165 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 163 def service @service end |
#unsigned_headers ⇒ Set<String> (readonly)
Returns a set of header names that should not be signed. All header names have been downcased.
181 182 183 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 181 def unsigned_headers @unsigned_headers end |
Class Method Details
.use_crt? ⇒ Boolean
843 844 845 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 843 def use_crt? @@use_crt end |
Instance Method Details
#presign_url(options) ⇒ HTTPS::URI, HTTP::URI
Signs a URL with query authentication. Using query parameters to authenticate requests is useful when you want to express a request entirely in a URL. This method is also referred as presigning a URL.
See Authenticating Requests: Using Query Parameters (AWS Signature Version 4) for more information.
To generate a presigned URL, you must provide a HTTP URI and the http method.
url = signer.presign_url(
http_method: 'GET',
url: 'https://my-bucket.s3-us-east-1.amazonaws.com/key',
expires_in: 60
)
By default, signatures are valid for 15 minutes. You can specify the number of seconds for the URL to expire in.
url = signer.presign_url(
http_method: 'GET',
url: 'https://my-bucket.s3-us-east-1.amazonaws.com/key',
expires_in: 3600 # one hour
)
You can provide a hash of headers that you plan to send with the request. Every 'X-Amz-*' header you plan to send with the request must be provided, or the signature is invalid. Other headers are optional, but should be provided for security reasons.
url = signer.presign_url(
http_method: 'PUT',
url: 'https://my-bucket.s3-us-east-1.amazonaws.com/key',
headers: {
'X-Amz-Meta-Custom' => 'metadata'
}
)
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 402 def presign_url() return crt_presign_url() if Signer.use_crt? creds = fetch_credentials http_method = extract_http_method() url = extract_url() headers = downcase_headers([:headers]) headers['host'] ||= host(url) datetime = headers['x-amz-date'] datetime ||= ([:time] || Time.now).utc.strftime("%Y%m%dT%H%M%SZ") date = datetime[0,8] content_sha256 = headers['x-amz-content-sha256'] content_sha256 ||= [:body_digest] content_sha256 ||= sha256_hexdigest([:body] || '') params = {} params['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256' params['X-Amz-Credential'] = credential(creds, date) params['X-Amz-Date'] = datetime params['X-Amz-Expires'] = extract_expires_in() params['X-Amz-Security-Token'] = creds.session_token if creds.session_token params['X-Amz-SignedHeaders'] = signed_headers(headers) params = params.map do |key, value| "#{uri_escape(key)}=#{uri_escape(value)}" end.join('&') if url.query url.query += '&' + params else url.query = params end creq = canonical_request(http_method, url, headers, content_sha256) sts = string_to_sign(datetime, creq) url.query += '&X-Amz-Signature=' + signature(creds.secret_access_key, date, sts) url end |
#sign_event(prior_signature, payload, encoder) ⇒ Object
Signs a event and returns signature headers and prior signature used for next event signing.
Headers of a sigv4 signed event message only contains 2 headers * ':chunk-signature' * computed signature of the event, binary string, 'bytes' type * ':date' * millisecond since epoch, 'timestamp' type
Payload of the sigv4 signed event message contains eventstream encoded message which is serialized based on input and protocol
To sign events
headers_0, signature_0 = signer.sign_event(
prior_signature, # hex-encoded string
payload_0, # binary string (eventstream encoded event 0)
encoder, # Aws::EventStreamEncoder
)
headers_1, signature_1 = signer.sign_event(
signature_0,
payload_1, # binary string (eventstream encoded event 1)
encoder
)
The initial prior_signature should be using the signature computed at initial request
Note:
Since ':chunk-signature' header value has bytes type, the signature value provided needs to be a binary string instead of a hex-encoded string (like original signature V4 algorithm). Thus, when returning signature value used for next event siging, the signature value (a binary string) used at ':chunk-signature' needs to converted to hex-encoded string using #unpack
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 315 def sign_event(prior_signature, payload, encoder) # Note: CRT does not currently provide event stream signing, so we always use the ruby implementation. creds = fetch_credentials time = Time.now headers = {} datetime = time.utc.strftime("%Y%m%dT%H%M%SZ") date = datetime[0,8] headers[':date'] = Aws::EventStream::HeaderValue.new(value: time.to_i * 1000, type: 'timestamp') sts = event_string_to_sign(datetime, headers, payload, prior_signature, encoder) sig = event_signature(creds.secret_access_key, date, sts) headers[':chunk-signature'] = Aws::EventStream::HeaderValue.new(value: sig, type: 'bytes') # Returning signed headers and signature value in hex-encoded string [headers, sig.unpack('H*').first] end |
#sign_request(request) ⇒ Signature
Computes a version 4 signature signature. Returns the resultant signature as a hash of headers to apply to your HTTP request. The given request is not modified.
signature = signer.sign_request(
http_method: 'PUT',
url: 'https://domain.com',
headers: {
'Abc' => 'xyz',
},
body: 'body' # String or IO object
)
# Apply the following hash of headers to your HTTP request
signature.headers['host']
signature.headers['x-amz-date']
signature.headers['x-amz-security-token']
signature.headers['x-amz-content-sha256']
signature.headers['authorization']
In addition to computing the signature headers, the canonicalized request, string to sign and content sha256 checksum are also available. These values are useful for debugging signature errors returned by AWS.
signature.canonical_request #=> "..."
signature.string_to_sign #=> "..."
signature.content_sha256 #=> "..."
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 234 def sign_request(request) return crt_sign_request(request) if Signer.use_crt? creds = fetch_credentials http_method = extract_http_method(request) url = extract_url(request) headers = downcase_headers(request[:headers]) datetime = headers['x-amz-date'] datetime ||= Time.now.utc.strftime("%Y%m%dT%H%M%SZ") date = datetime[0,8] content_sha256 = headers['x-amz-content-sha256'] content_sha256 ||= sha256_hexdigest(request[:body] || '') sigv4_headers = {} sigv4_headers['host'] = headers['host'] || host(url) sigv4_headers['x-amz-date'] = datetime sigv4_headers['x-amz-security-token'] = creds.session_token if creds.session_token sigv4_headers['x-amz-content-sha256'] ||= content_sha256 if @apply_checksum_header headers = headers.merge(sigv4_headers) # merge so we do not modify given headers hash # compute signature parts creq = canonical_request(http_method, url, headers, content_sha256) sts = string_to_sign(datetime, creq) sig = signature(creds.secret_access_key, date, sts) # apply signature sigv4_headers['authorization'] = [ "AWS4-HMAC-SHA256 Credential=#{credential(creds, date)}", "SignedHeaders=#{signed_headers(headers)}", "Signature=#{sig}", ].join(', ') # Returning the signature components. Signature.new( headers: sigv4_headers, string_to_sign: sts, canonical_request: creq, content_sha256: content_sha256 ) end |