For security reasons, you must sign all API requests. Alibaba Cloud uses request signatures to verify the identities of API callers. Each API request must contain a signature, regardless of whether the API request is sent by using HTTP or HTTPS.

Signature algorithm

This topic describes the signature algorithm that is used in Function Compute. This signature algorithm is implemented in SDKs to sign API requests. This way, you do not need to manually calculate signatures. For more information, see Supported SDKs.

Function Compute checks whether a request is valid based on the Authorization header field. If a function is configured with an HTTP trigger and allows anonymous access, Function Compute does not check the validity of the request. A request can pass the verification only when the client that sends the request uses the same signature algorithm as Function Compute. If a request does not contain the Authorization header field or the Authorization header field contains an invalid signature, Function Compute returns the HTTP 403 error.

Note You are not charged when Function Compute returns the HTTP 403 error. For more information, see Billing.
signature = base64(hmac-sha256(HTTP_METHOD + "\n" 
                + CONTENT-MD5 + "\n"     
                + CONTENT-TYPE + "\n" 
                + DATE + "\n" 
                + CanonicalizedFCHeaders
                + CanonicalizedResource))

// The Authorization header field.                
Authorization = "FC" + accessKeyID + ":" + signature            

The following section describes the parameters:

  • HTTP_METHOD: the HTTP method that is used to send a request. Specify the value in uppercase, for example, PUT, GET, POST, or DELETE.
  • CONTENT-MD5: the MD5 hash of the request content. If the request does not contain the Content-MD5 header field, leave this parameter empty.
  • CONTENT-TYPE: the type of the request content. Requests bound for Function Compute must be of the application/json type.
  • DATE: required. The time when the signature is generated. The time must be in the GMT format.
    Notice The difference between the time when the signature is generated on the client and the time when Function Compute receives the request cannot exceed 15 minutes. Otherwise, Function Compute rejects the request.
  • CanonicalizedFCHeaders: the string that consists of all HTTP header fields whose names start with x-fc- in the request. For more information about how to generate the string, see CanonicalizedFCHeaders.
  • CanonicalizedResource: the path in the request URL. In most cases, Function Compute decodes the path in a request URL and then removes the parameters in the path.
    • The path in a request URL is in the $api-version/api-path format, where:
      • api-version: the API version. The current API version is 2016-08-15.
      • api-path: the path that is used to call each API operation. For example, the path that is used to call the CreateService operation is /services. For more information, see List of operations by function.
    • Function Compute uses different methods to process CanonicalizedResource in common requests and CanonicalizedResource in the requests for functions that are configured with HTTP triggers and require authorization.
      • Request for a function that is configured with an HTTP trigger and requires authorization: If parameters are specified in CanonicalizedResource in the request, Function Compute separates the parameters with \n. The key-value pairs of the parameters are sorted in alphabetical order. If parameters are not specified in CanonicalizedResource in the request, Function Compute appends \n to the end of the value of CanonicalizedResource. Example:
        // The path in the URL of a request for a function that is configured with an HTTP trigger and requires authorization.
        /2016-08-15/proxy/service-name/func-name/path-with-%20-space/action? x=1&a=2&x=3&with%20space=foo%20bar
        
        // The URL-decoded path.
        /2016-08-15/proxy/service-name/func-name/path-with- -space/action? x=1&a=2&x=3&with space=foo bar
        
        // CanonicalizedResource in a request for a function that is configured with an HTTP trigger and requires authorization.
        /2016-08-15/proxy/service-name/func-name/path-with- -space/action\na=2\nwith space=foo bar\nx=1\nx=3
        
        // The path in the URL of a common request.
        /2016-08-15/service-name/func-name/path-with-%20-space/action? x=1&a=2&x=3&with%20space=foo%20bar
        
        // The URL-decoded path.
        /2016-08-15/service-name/func-name/path-with- -space/action? x=1&a=2&x=3&with space=foo bar
        
        // CanonicalizedResource in a common request.
        /2016-08-15/service-name/func-name/path-with- -space/action                    
        Note If a key in CanonicalizedResource maps to multiple values, Function Compute considers the key and values as a whole when it sorts the key-value pairs.
      • Common request: Function Compute performs URL decoding on the value of CanonicalizedResource in the request and obtains the string to the left of the question mark (?). The parameters to the right of the question mark (?) are discarded.
        Note Common requests include all requests except the requests for functions that are configured with HTTP triggers and require authorization.
  • hmac-sha 256: the key that is used to generate the signature. Your AccessKey secret must be used as the key.

    You can use the following pseudocode to verify the signature scheme:

    // Create a string.
    function composeStringToSign(method, path, headers, queries) {
      var contentMD5 = headers['content-md5'] || '';
      var contentType = headers['content-type'] || '';
      var date = headers['date'];
      var signHeaders = buildCanonicalHeaders(headers, 'x-fc-');
    
      var u = url.parse(path);
      var pathUnescaped = decodeURIComponent(u.pathname);
      var str = `${method}\n${contentMD5}\n${contentType}\n${date}\n${signHeaders}${pathUnescaped}`;
    
      if (queries) {
        var params = [];
        Object.keys(queries).forEach(function (key) {
          var values = queries[key];
          var type = typeof values;
          if (type === 'string') {
            params.push(`${key}=${values}`);
            return;
          }
          if (type === 'object' && values instanceof Array) {
            queries[key].forEach(function (value) {
              params.push(`${key}=${value}`);
            });
          }
        });
        params.sort();
        str += '\n' + params.join('\n');
      }
      return str;
    }
    
    // Use HMAC-SHA256 and Base64 to calculate the signature. The value of the source parameter is the string that is created by using the preceding code.
    function signString(source, secret) {
      const buff = crypto.createHmac('sha256', secret)
        .update(source, 'utf8')
        .digest();
      return new Buffer(buff, 'binary').toString('base64');
    }                   

CanonicalizedFCHeaders

To generate a value for CanonicalizedFCHeaders, perform the following steps:

  1. Convert the name of each HTTP header field that starts with x-fc- to lowercase letters.
  2. Generate a substring for each HTTP header field. The substring is in the ${key}:${value}\n format. For example, the substring x-fc-invocation-type:Sync\n is generated for X-Fc-Invocation-Type:Sync. The generated substrings are sorted in alphabetical order based on the names of the header fields.
    • ${key}: the name of the HTTP header field.
    • ${value}: the value of the HTTP header field.
  3. Concatenate the sorted substrings to form the required string.
    You can use the following pseudocode to verify the signature scheme:
    // javascript
    // prefix = 'x-fc-'
    function buildCanonicalHeaders(headers, prefix) {
        var list = [];
        var keys = Object.keys(headers);
    
        var fcHeaders = {};
        for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
    
            var lowerKey = key.toLowerCase().trim();
            if (lowerKey.startsWith(prefix)) {
                list.push(lowerKey);
                fcHeaders[lowerKey] = headers[key];
            }
        }
        list.sort();
    
        var canonical = '';
        for (var _i = 0; _i < list.length; _i++) {
            var _key = list[_i];
            canonical += `${_key}:${fcHeaders[_key]}\n`;
        }
    
        return canonical;
    }            
    After you obtain the signature by using the preceding methods, you can use the following method to generate a value for the Authorization header field:
    Authorization = "FC" + accessKeyID + ":" + signature