Home > Alexa > Alexa Skills Kit

Hosting a Custom Skill as a Web Service

Introduction

Introduction

You can add a new custom skill to Alexa by implementing a web service that accepts requests from and sends responses to the Alexa service in the cloud. You can write your web service using any programming language, as long as the service meets the requirements described below.

Note that this document only applies when developing and hosting a web service. If you implement the skill as an AWS Lambda function in AWS Lambda (a service offering by Amazon Web Services), see Creating an AWS Lambda Function for a Custom Skill instead.

Web services can only be used for custom skills. If you are using the Smart Home Skill API, you must host your skill adapter as a Lambda function.

Requirements for Your Web Service

To handle requests sent by Alexa, your web service must meet the following requirements:

  1. The service must be Internet-accessible.
  2. The service must adhere to the Alexa Skills Kit interface.
  3. The service must support HTTP over SSL/TLS, leveraging an Amazon-trusted certificate.
  4. The service must accept requests on port 443.
  5. The service must present a certificate with a subject alternate name that matches the domain name of the endpoint.
  6. The service must validate that incoming requests are coming from Alexa.

Note: if you are using Apache HTTP Server to host your web service, use version 2.4.10 or later. Earlier versions of Apache HTTP Server send an “unrecognized name” warning if the server is not configured with a ServerName or ServerAlias in the configuration files. This prevents the Alexa service from sending the customer’s request to your server. To address this, either upgrade to 2.4.10 or later, or add ServerName / ServerAlias to your server’s configuration file.

Verifying that the Request was Sent by Alexa

Requests sent to your web service are transmitted over the Internet. To protect your endpoint from potential attackers, your web service should verify that incoming requests were sent by Alexa. Any requests coming from other sources should be rejected.

There are two parts to validating incoming requests:

  • Check the request signature to verify the authenticity of the request. Alexa signs all HTTPS requests.
    • This is required for certifying your Alexa skill and making it available to Amazon users. Web services that accept unsigned requests or fail to verify the request signature are rejected.
    • The Java library does this verification in the SpeechletServlet class. If you do not use the Java library, you must do this verification yourself.
    • If you use the Java library without using the SpeechletServlet class, you can use the SpeechletRequestSignatureVerifier class to do this.
  • Check the request timestamp to ensure that the request is not an old request being sent as part of a “replay” attack.
    • This is required for certifying your Alexa skill and making it available to Amazon users.
    • The Java library does this verification in the SpeechletServlet. You need to provide the tolerance to allow in seconds in the system property com.amazon.speech.speechlet.servlet.timestampTolerance.
    • If you use the Java library without using the SpeechletServlet class, you can use the TimeStampSpeechletRequestVerifier class to do this.

See the following sections for details.

Checking the Signature of the Request

Requests sent by Alexa provide the information you need to verify the signature in the HTTP headers:

  • SignatureCertChainUrl
  • Signature

To validate the signature:

  1. Verify the URL specified by the SignatureCertChainUrl header value on the request to ensure that it matches the format used by Amazon. See Verifying the Signature Certificate URL.
  2. Download the PEM-encoded X.509 certificate chain that Alexa used to sign the message as specified by the SignatureCertChainUrl header value on the request.

    This chain is provided at runtime so that the certificate may be updated periodically, so your web service should be resilient to different URLs with different content.

  3. This certificate chain is composed of, in order, (1) the Amazon signing certificate and (2) one or more additional certificates that create a chain of trust to a root certificate authority (CA) certificate. To confirm the validity of the signing certificate, perform the following checks:
    • The signing certificate has not expired (examine both the Not Before and Not After dates)
    • The domain echo-api.amazon.com is present in the Subject Alternative Names (SANs) section of the signing certificate
    • All certificates in the chain combine to create a chain of trust to a trusted root CA certificate
  4. Once you have determined that the signing certificate is valid, extract the public key from it.
  5. Base64-decode the Signature header value on the request to obtain the encrypted signature.
  6. Use the public key extracted from the signing certificate to decrypt the encrypted signature to produce the asserted hash value.
  7. Generate a SHA-1 hash value from the full HTTPS request body to produce the derived hash value
  8. Compare the asserted hash value and derived hash values to ensure that they match.

Verifying the Signature Certificate URL

Before downloading the certificate from the URL specified in the SignatureCertChainUrl header, you should ensure that the URL represents a URL Amazon would use for the certificate. This protects against requests that attempt to make your web service download malicious files and similar attacks.

First, normalize the URL so that you can validate against a correctly formatted URL. For example, normalize

https://s3.amazonaws.com/echo.api/../echo.api/echo-api-cert.pem

to:

https://s3.amazonaws.com/echo.api/echo-api-cert.pem

Next, determine whether the URL meets each of the following criteria:

  1. The protocol is equal to https (case insensitive).
  2. The hostname is equal to s3.amazonaws.com (case insensitive).
  3. The path starts with /echo.api/ (case sensitive).
  4. If a port is defined in the URL, the port is equal to 443.

Examples of correctly formatted URLs:

  • https://s3.amazonaws.com/echo.api/echo-api-cert.pem
  • https://s3.amazonaws.com:443/echo.api/echo-api-cert.pem
  • https://s3.amazonaws.com/echo.api/../echo.api/echo-api-cert.pem

Examples of invalid URLs:

  • http://s3.amazonaws.com/echo.api/echo-api-cert.pem (invalid protocol)
  • https://notamazon.com/echo.api/echo-api-cert.pem (invalid hostname)
  • https://s3.amazonaws.com/EcHo.aPi/echo-api-cert.pem (invalid path)
  • https://s3.amazonaws.com/invalid.path/echo-api-cert.pem (invalid path)
  • https://s3.amazonaws.com:563/echo.api/echo-api-cert.pem (invalid port)

If the URL does not pass these tests, reject the request and do not proceed with verifying the signature.

Checking the Timestamp of the Request

Every request sent to your web service by Alexa includes a timestamp. This information is part of the signed portion of the request, so it cannot be changed without also invalidating the request signature. Using this timestamp to verify the freshness of the request before responding protects your service from attackers attempting a “replay” attack in which they acquire a properly signed request and then repeatedly resend it to disrupt your service.

Your service should allow a tolerance of no more than 150 seconds (two and a half minutes). This means that your service should only accept requests in which the request timestamp is within 150 seconds of the current time. Web services that allow a longer tolerance cannot be published to Amazon customers.

If you are using the Java library, the SpeechletServlet class handles this verification. You need to specify the tolerance to allow in seconds in the system property:

com.amazon.speech.speechlet.servlet.timestampTolerance

If the timestamp provided with the request does not fall within the specified tolerance, the SpeechletServlet does not call any of your web service’s methods, but instead returns an HTTP error code (400 Bad Request). Leaving this property blank turns off timestamp verification. This is acceptable for development and testing, but verification should be enabled before publishing your Alexa skill to users.

You can set this system property by passing an argument to the JVM when your web service starts up:

  • If your web service is running within Eclipse using a Launcher class (such as the Launcher.java class provided in the samples), you can add a VM argument to the run configuration:

    -Dcom.amazon.speech.speechlet.servlet.timestampTolerance=tolerance in seconds

  • If you are starting your web service using an Ant script, add the following property within the <java> tag of your script:

     <java classname="Launcher" classpathref="java.sdk.classpath" fork="true">
         <sysproperty key="com.amazon.speech.speechlet.servlet.timestampTolerance"
                      value="tolerance in seconds" />
     </java>
    
  • If your web service is running within Elastic Beanstalk, you can configure properties in the Elastic Beanstalk console. See the “Setting Java System Properties for Elastic Beanstalk” section in Deploying a Web Service for a Custom Skill to AWS Elastic Beanstalk.

If you are not using the Java library, you need to do this verification yourself. The timestamp is provided as part of the request object in the JSON body of the request:

{
  "version": "1.0",
  "session": {
    "new": boolean,
    ...(additional session properties not shown)
  },
  "request": {
    "type": "LaunchRequest",
    "timestamp": "string",
    "requestId": "string"
  }
}

The timestamp is provided as an ISO 8601 formatted string (for example, 2015-05-13T12:34:56Z). Your code needs to parse the string into a date object, then verify that it is within the tolerance your web service allows (no more than 150 seconds). Reject requests in which the timestamp falls outside the tolerance with an error code (such as 400 Bad Request).

Next Steps