Verify the Signed Request

When the controller is configured to include signatures, it is easy for the ECP to ignore them. The ECP simply extracts the information it is interested in from the provided attributes and ignores the rest.

However, it is highly likely that an administrator that enables response signing wants to use the signatures to authenticate the redirected requests it receives. This section covers how to do that. The whole process is shown in Verifying a Signed Request Basic Validation Checks.

The algorithm used to sign the redirection response (and therefore the redirected request to the ECP) is based on Amazon Web Services API Signature Version 4. AWS documentation refers to this approach as “Pre-signed URLs”.

Basic Steps

The following steps indicate the basic procedure for verifying the signature. Refer to Basic Flow for Verifying a Signature for the graphical flow.

  1. Perform basic validation on the request message (are all required fields present, is the date current?). If these validations fail, there is no point in computing the signature.
  2. Extract the signature from the received request.
  3. From the received request, construct the string over which the signature will be computed. All but one component of this string come from the query parameters.
  4. Generate the signing key. The shared secret is used to generate a signing key and is not itself the signing key.
  5. Generate the signature using the signing key and the constructed string.
  6. Compare the extracted signature (X-Amz-Signature) to the signature just computed. If they do not match, the request is invalid and should be discarded.
Click to expand in new window
Basic Flow for Verifying a Signature

Verifying a Signed Request Basic Validation Checks

The following items can be considered when validating the redirect prior to computing the signature:

  1. Does the request contain a token parameter, a WLAN parameter, and a destination URL? If not, the request either did not come from the controller or was tampered with en route.
  2. If the request contains a timestamp, does the timestamp meet the following requirement:
timestamp <= now <= timestamp + x_amz_expires

Or if an allowance for clocks being out of sync is made,

timestamp - fuzz <= now <= timestamp + x_amz_expires

If not, the request is invalid, possibly the result of a user bookmarking the ECP landing page on a previous visit. The request should be rejected or discarded.

  1. Are all parameters formatted in accordance with the descriptions?
  2. Are all parameters required for the signature present in the request?

The first 1/3 of “verifyAwsUrlSignature” and the private method “validateQueryParms” in section crypt_aws_s4.php provide examples of performing these types of checks in PHP.

Extracting the Signature from the Request

The signature is in the “X-Amz-Signature” query string parameter. Obviously the signature itself can‘t be included in the computation of the signature so it must be removed from the request and set aside for later comparison. How the signature is removed from the request will depend on the program language and framework used to implement the external captive portal. The method “simpleaws::verifyAwsUrlSignature” in crypt_aws_s4.php illustrates one way to remove the signature when the query parameters are in a PHP array.

Building the String to Sign

Steps in Building the String to Sign shows the main actions required to build the string that will be signed out of the request:

  1. Build the scope string.
  2. Build a “canonicalized” version of the request.
  3. Assemble the scope string, the canonicalized string, and some additional inputs to create the string to sign.

The scope string is easy to build out of a valid request. It is made from parts of the string in the “X-Amz-Credentials” parameter. If the credentials are valid then the scope string can be created by un-escaping the forward slashes it contains (i.e. replace ‘%2f‘ with ‘/‘), and then taking all the characters to the right of the first forward slash. The scope ends up being the fully qualified credential, less the identity string.

Click to expand in new window
Steps in Building the String to Sign
Note

Note

Parts of the Scope

The fully qualified Amazon credential consists of:

  • An identity string (the one configured in the controller GUI).
  • The date portion of the X-Amz-Date.
  • A region string. For a real Amazon application this is one of the geographic service regions defined by Amazon. The service region is not critical for the FF-ECP implementation so it is always set to ‘world‘.
  • A service identifier. The service is always set to ‘ecp‘.
  • The identifier ‘aws4_request‘, which identifies the signature version.

The canonicalized request string has the format:

"GET\n"
.<URL-Path-Component>."\n"
.<URL-Query-Parameters>."\n"
.'host:'.<URL-Host>
."\n\nhost\nUNSIGNED-PAYLOAD";

Where:

Finally the string that will actually be signed is composed as:

"AWS4-HMAC-SHA256\n"
.<Date>."\n"
.<scope>."\n"
.sha256(<canonicalized-request-string>)

where

Creating the Signing Key

The process for generating signatures uses symmetric key encryption. The controller and the ECP use a shared key (the one configured on the controller‘s WLAN Service‘s captive portal configuration dialog) and the same encryption algorithm to generate and validate the signature.

The shared key is not used directly. Instead it is used to generate a secure hash (“HMAC”) that is then used as the key to sign the request. The process for creating the key is shown below in Creating the Signing Key.

Click to expand in new window
Creating the Signing Key

In the above figure:

  1. “Date without Time” is the first 8 characters in the “X-Amz-Date” attribute, which corresponds to the date only in “YYYYMMDD” format.
  2. “Shared Key” is the shared key configured on the controller. It is the shared key that is paired with the identity used to create the “X-Amz-Credential” attribute in the redirected request.
  3. “Region String” is the region component of the Scope string.
  4. “Service String” is the service component of the Scope string.
  5. “Constant-String-To-Sign” is the string “aws4_request”.

And each of the “Create…” actions consists of generating a secure HMAC using SHA256 from the inputs. The output secure hash is in binary format (not encoded as a hex character string). The output of each step acts as the signing key for the subsequent step. The signing key for the first step is the shared secret, pre-pended with the literal ‘AWS4‘.

Note that for any given identity the correct signing key only needs to be computed once per day. If the calculations are cached the cache should include an entry for the previous day to cope with the request being sent just before midnight UTC. The previous day‘s key only needs to be kept for a small overlapping period (perhaps 10 minutes at the most).

Creating the Signature and Verifying the Request

At this point the signature for the request is computed as a secure HMAC using SHA256. The signing key is created as described in Creating the Signing Key and the string to sign is created as described in Building the String to Sign.

Verifying the signature in the request consists of standard string comparison between the transmitted and computed keys. If they aren‘t identical the request is invalid. The client can be sent a web page containing a generic reject message or the request can be discarded silently.