ex-aws / ex_aws

A flexible, easy to use set of clients AWS APIs for Elixir

Home Page:https://hex.pm/packages/ex_aws

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"Signature Does Not Match" Error when uploading file with space or special characters in key via S3.upload.

aram0112 opened this issue · comments

Environment

Elixir 1.12.1 (compiled with Erlang/OTP 24)
ex_aws: 2.2.9
hackney: 1.18.0

Current behavior

When uploading a file via

"File Path"
|> S3.Upload.stream_file()
|> S3.upload("bucket", "key with space or special character")
|> ExAws.request(config)

A 403 http_error is received with the error message "The request signature we calculated does not match the signature you provided. Check your key and signing method."

Expected behavior

A 200 http response and a successful file upload.

Potential Fix

I pinpointed the issue to the ExAws.Auth module and was able to successfully upload a file after making the following changes.

def headers(http_method, url, service, config, headers, body) do
    with {:ok, config} <- validate_config(config) do
      datetime = :calendar.universal_time()

      # Added this line to the headers method to ensure the properly encoded url is used when generating the headers.
      url = ExAws.Request.Url.sanitize(url, service)

      headers =
        [
          {"host", URI.parse(url).authority},
          {"x-amz-date", amz_date(datetime)}
          | headers
        ]
        |> handle_temp_credentials(config)

      auth_header =
        auth_header(
          http_method,
          url,
          headers,
          body,
          service |> service_override(config) |> service_name,
          datetime,
          config
        )

      {:ok, [{"Authorization", auth_header} | headers]}
    end
  end
defp signature(http_method, url, query, headers, body, service, datetime, config) do
    # Removed the last function call to encode the path as I am guessing the path here needs to match the ultimate S3 key 
    # with spaces and special characters 
    # previously was path = url |> Url.get_path(service) |> Url.uri_encode()
    path = url |> Url.get_path(service)
    request = build_canonical_request(http_method, path, query, headers, body)
    string_to_sign = string_to_sign(request, service, datetime, config)
    Signatures.generate_signature_v4(service, config, datetime, string_to_sign)
  end

I am hoping for any guidance from someone with more experience in the Auth Module on whether this is an actual issue and fix or whether I am approaching the issue incorrectly. Thank you.