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 161 162 163 164 165 166 167 |
# 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 if @signing_algorithm == 'sigv4-s3express'.to_sym && Signer.use_crt? && Aws::Crt::GEM_VERSION <= '0.1.9' raise ArgumentError, 'This version of aws-crt does not support S3 Express. Please update this gem to at least version 0.2.0.' 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.
192 193 194 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 192 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
184 185 186 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 184 def credentials_provider @credentials_provider end |
#region ⇒ String (readonly)
173 174 175 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 173 def region @region end |
#service ⇒ String (readonly)
170 171 172 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 170 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.
188 189 190 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 188 def unsigned_headers @unsigned_headers end |
Class Method Details
.use_crt? ⇒ Boolean
881 882 883 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 881 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'
}
)
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 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 416 def presign_url() return crt_presign_url() if Signer.use_crt? creds, expiration = 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'] = presigned_url_expiration(, expiration, Time.strptime(datetime, "%Y%m%dT%H%M%S%Z")).to_s if creds.session_token if @signing_algorithm == 'sigv4-s3express'.to_sym params['X-Amz-S3session-Token'] = creds.session_token else params['X-Amz-Security-Token'] = creds.session_token end end 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
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 329 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 #=> "..."
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 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'gems/aws-sigv4/lib/aws-sigv4/signer.rb', line 241 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 if creds.session_token if @signing_algorithm == 'sigv4-s3express'.to_sym sigv4_headers['x-amz-s3session-token'] = creds.session_token else sigv4_headers['x-amz-security-token'] = creds.session_token end end 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 |