You must sign all API requests to ensure security. Alibaba Cloud uses the request signature to verify the identity of the API caller. Each API request must contain a signature, regardless of whether the request is sent over HTTP or HTTPS.

Signature algorithm

This topic describes the signature algorithm that is used in API requests of Function Compute. This signature algorithm is implemented in SDKs to sign API requests so that 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 passes the verification only if 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 status code 403.

Note You are not charged when Function Compute returns the HTTP status code 403. 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 parameters are contained in the preceding sample code:

  • HTTP_METHOD: the HTTP method that is used to send a request. Specify the value in uppercase letters, such as PUT, GET, POST, or DELETE.
  • CONTENT-MD5: the MD5 hash value of the request body. If the request does not contain the Content-MD5 header field, leave this parameter empty.
  • CONTENT-TYPE: the type of the request body. Requests for Function Compute must be of the application/json type.
  • DATE: the time when you want to perform the operation. This parameter cannot be left empty. The value must be of the RFC1123 type in GMT. Example: Mon, 02 Jan 2006 15:04:05 GMT.
    Notice The interval between the time when the signature is generated on the client and the time when Function Compute receives the request cannot exceed 15 minutes. If the interval exceeds 15 minutes, Function Compute rejects the request.
  • CanonicalizedFCHeaders: the string that consists of all HTTP header fields whose names are prefixed with x-fc- in the request. For more information about how to generate a 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 params content from the path.
    • The path in a request URL is in the $api-version/api-path format, in which:
      • api-version: the API version. The API version that you use 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 configured with HTTP triggers that require authentication.
      • Requests for a function that is configured with an HTTP trigger that requires authentication: If the params content is specified, parameters are separated with \n. The key-value pairs of the parameters in params are sorted in alphabetical order. If the params content is not specified, Function Compute appends \n to the end of the value of CanonicalizedResource. Examples:
        // The path in the URL of a request for a function that is configured with an HTTP trigger that requires authentication.
        /2016-08-15/proxy/service-name/func-name/path-with-%20-space/action?x=1&a=2&x=3&with%20space=foo%20bar
        
        // The path that is obtained after the URL is decoded.
        /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 that requires authentication.
        /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 path that is obtained after the URL is decoded.
        /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 the key in params 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 content before the question mark (?). The params content is discarded.
        Note Common requests include all requests except the requests for functions that are configured with HTTP triggers that require authentication.
  • 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