Contact Us

If you still have questions or prefer to get help directly from an agent, please submit a request.
We’ll get back to you as soon as possible.

Please fill out the contact form below and we will reply as soon as possible.

  • Go to Haptik Website
  • Contact Us
  • Home

TRACT Security

Written by Medha Anand

Updated on December 17th, 2021

Contact Us

If you still have questions or prefer to get help directly from an agent, please submit a request.
We’ll get back to you as soon as possible.

Please fill out the contact form below and we will reply as soon as possible.

  • Getting Started
    Build Deploy Analyse Manage Account Bot Deactivation
  • Bot Building
    Essentials Smart Skills Steps User Messages Bot Responses Entities Connections Customisations User feedback collection Testing Whatsapp Bots NLU Bot Maintenance
  • Smart Agent Chat
    Set up Admin Settings MyChats Section (Agent Inbox) Live Traffic Section Teams Section Archives Section Analytics Plans on Smart Agent Chat
  • Conversation Design
    Design Basics Design Guides Designing for Platforms Designing WhatsApp Bots
  • Developer Guides
    Code Step Integration Static Step Integration Shopify Integration SETU Integration Exotel Integration CIBIL integration Freshdesk KMS Integration PayU Integration Zendesk Guide Integration Twilio Integration Razorpay Integration LeadSquared Integration USU(Unymira) Integration Helo(VivaConnect) Integration Salesforce KMS Integration Stripe Integration PayPal Integration CleverTap Integration Fynd Integration HubSpot Integration Magento Integration WooCommerce Integration Microsoft Dynamics 365 Integration
  • Deployment
    Web SDK WhatsApp Facebook Instagram Sunshine Conversation LINE Google Business Messages Telegram MS Teams Bot as an API iOS SDK Android SDK
  • External Agent Tool Setup
    Zendesk Chat Salesforce Service Cloud Freshchat Zoho NICE CXOne Gorgias
  • Analytics & Reporting
    Intelligent Analytics
  • Notifications
    SMS Notifications Success Measurement
  • Commerce Plus
    Catalog Integration Bot Building Guide Channel Deployments Unified ML Pipeline Documentation
  • Troubleshooting Guides
    Error Messages FAQs
  • Release Notes
+ More

Table of Contents

Signing management requestsVerifying that the request was sent by ECTFetching the certificateCertificate verificationSignature verificationTimestamp verificationSample python snippet for signed requestsVerifying hook signaturesVerifying that the request was sent by TRACTFetching the certificateCertificate verificationSignature verificationTimestamp verificationSample python (django) snippet for verifying hook signatures  

Signing management requests

Few management APIs require requests signed by the ECT for authentication and authorization.

Note: This is required only for issuing JWTs and listing ECT clients. All other APIs do not require this.

These APIs include:

  • JWT issuing APIs
  • Client enumeration APIs

Verifying that the request was sent by ECT

To verify that a request came from the ECT service, we:

  • Check the request signature to verify the authenticity of the request.
    You must sign all management requests.
  • Check the request timestamp to make sure that the request is not an old one being sent as part of a replay attack

Note: When one or both of these checks fail, our web service sends a response with an HTTP status code of 400 Bad Request. Requests that ECTs send to our web service must include two HTTP headers that we use to check the request signature:

Management request headers

Parameter
Description
SignatureCertChainUrl
The URL to fetch the certificate chain from. (In case of a CA signed certificate.)
SignatureCertUUID
The certificate ID returned by us when you provide us your certificate. (In case of a self signed certificate.)
Signature
Signature generated from the body

Fetching the certificate

Certificate URL validation

Delete

Applicable for ECTs using CA-signed certificates.

To check and validate the request signature, we verify the URL in the SignatureCertChainUrl header to make sure that it matches the format that ECT uses.

This can help to protect against requests that attempt to make our web service download malicious files.

Make sure that the URL meets all of the following criteria:

  • The protocol is https (not case sensitive).
  • The hostname is the FQDN of the ECT. (not case sensitive).
  • The path begins with /ect.api/ (case sensitive).
  • If a port is specified in the URL, the port is 443.

The following are examples of correctly formatted URLs:

  • https://subdomain.ect.com/ect.api/ect-api-cert.pem
  • https://subdomain.ect.com:443/ect.api/ect-api-cert.pem
  • https://subdomain.ect.com/ect.api/../ect.api/ect-api-cert.pem

The following are examples of invalid URLs:

  • http://subdomain.ect.com/ect.api/ect-api-cert.pem (invalid protocol)
  • https://notect.com/ect.api/ect-api-cert.pem (invalid hostname)
  • https://subdomain.ect.com/EcT.aPi/ect-api-cert.pem (invalid path)
  • https://subdomain.ect.com/invalid.path/ect-api-cert.pem (invalid path)
  • https://subdomain.ect.com:563/ect.api/ect-api-cert.pem (invalid port)

If the URL does not meet all of these criteria, we do not proceed to the next stage.

We download the PEM-encoded X.509 certificate chain using the URL specified in the SignatureCertChainUrl header in the request.

This chain can be provided at runtime so that it can be updated periodically.

This certificate chain we downloaded in the preceding stage is composed of the following, in order:

  • The ECT signing certificate.
  • One or more additional certificates that create a chain of trust to a root certificate of a certificate authority (CA).

Certificate ID validation

Delete

Applicable for ECTs using self-signed certificates.

If you choose to utilize a self-signed certificate, we will generate and return a certificate UUID for the same.

Requests signed with the said certificate need to provide the SignatureCertUUID parameter.

Upon the receipt of a management request, we validate the UUID provided and fetch the corresponding certificate from our data store.

Certificate verification

CA signed certificate

We confirm the validity of the signing certificate by completing the following stages:

  • Check the Not Before and Not After dates of the signing certificate to make sure that it is not expired.
  • Make sure the ECT FQDN is present in the Subject Alternative Names (SANs) section of the signing certificate.
  • Validate that all certificates in the chain combine to create a chain of trust to a trusted root CA certificate.

Self-signed certificate

We confirm the validity of the signing certificate by completing the following stages:

  • Validate that a certificate with the said ID exists with us.
  • Check the Not Before and Not After dates of the signing certificate to make sure that it is not expired.
  • Make sure the domain name ECT FQDN is present in the Subject Alternative Names (SANs) section of the signing certificate.

If certificate verification fails, we discard the request.

Signature verification

  • After confirming the validity of the signing certificate, we extract the public key from it.
  • We base64-decode the value of the Signature header in the request to obtain the encrypted signature.
  • Use the public key extracted from the signing certificate to decrypt the encrypted signature to produce the asserted hash value.
  • We generate an SHA-1 hash value from the full HTTP request body to produce the derived hash value.
  • Compare the asserted hash value and derived hash values to ensure that they match.
    If they do not match, we discard the request.

Note: make sure the signature is generated as follows:

  • Convert the prepared request body to bytes with UTF-8 encoding.
  • Generate an SHA-1 hash of the encoded body.
  • ASN.1 encode the hash and convert it from hex to binary.
  • Encrypt the binary blob with your private key.

These stages are provided only for reference. You should not need to do the above operations by hand. Your language WILL provide a one-shot signing system. It is HIGHLY RECOMMENDED that you use the high-level functionality and avoid using low-level primitives.

Timestamp verification

  • Every request that your web service sends must include a timestamp in the request body.

  • The timestamp is part of the signed portion of the request, so it cannot be modified without also invalidating the request signature.

  • We use this timestamp to verify the freshness of the request before sending a response.

  • This can help protect our web service from attempted replay attacks in which an attacker acquires a properly signed request and then repeatedly resends it to disrupt the our service.

We allow a tolerance of no more than 150 seconds (two and a half minutes). This means that our service should only process requests in which the timestamp is within 150 seconds of the current time.

If the timestamp differs from the current time by more than 150 seconds, we discard the request.

The timestamp is part of the request object in the JSON body of the request. For example:

POST /jwt/issue
{
 "fqdn": "awesome.ect.com",
 "client_id": "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
 "timestamp": "2019-05-13T12:34:56Z",
}

Please ensure that the timestamp value is an ISO 8601 formatted string, for example 2019-05-13T12:34:56Z. The time is expected to be in the UTC timezone. (As indicated by the Z in the timestamp string.)

We return HTTP error code 400 Bad Request, to reject requests in which the timestamp falls outside the tolerance.

Sample python snippet for signed requests

#!/usr/bin/env python3
import datetime
import requests
import json
import pem
import base64
from OpenSSL import crypto
keyfile_path = 'host.key' # PEM encoded RSA/ECDSA private key
keyfile = pem.parse_file(keyfile_path)[0]
private_key = crypto.load_privatekey(crypto.FILETYPE_PEM, keyfile.as_bytes())
url = 'https://<base_url>/tract/management/token/issue/' # base URL is pre-shared with you
payload = {
 'client_id': '', # client ID for which you are generating the token
 # ID can be fetched from the client enumeration API
 'fqdn': '', # your FQDN pre-shared with us
 'timestamp': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
}
# optional minification stage
# doesn't really make a difference as long as the body sent and the body hashed+signed are the same
minified_json = json.dumps(payload, separators=(',', ':'))
headers = {
 'Content-Type': 'application/json',
 'Signature': base64.b64encode(crypto.sign(private_key, bytes(minified_json, 'utf-8'), 'sha1')),
 'SignatureCertUUID': '' # This is shared with you when you pre-share a cert with us.
 # WARNING: Make sure the UUID is the correct UUID for the certificate used for this request.
 # Mismatching UUID will cause the request to fail.
}
response = requests.request('POST', url, headers=headers, data=minified_json)
# verify data sent and received
print(response.request.headers, end='\n\n')
print(response.request.body, end='\n\n')
print(response.text.encode('utf8'))

Verifying hook signatures

All outgoing hook requests from TRACT to ECTs are signed to authenticate that the request actually originated from TRACT.

Verifying that the request was sent by TRACT

To verify that a request came from TRACT, do:

  • Check the request signature to verify the authenticity of the request.

We sign all outgoing hook requests.

  • Check the request timestamp to make sure that the request is not an old one being sent as part of a replay attack.

Error

Note: When one or both of these checks fail, your web service should send a response with an HTTP status code of 400 Bad Request

Requests that TRACT sends to your web service includes two HTTP headers that should be used to check the request signature

Management request headers

Parameter

Description
signature-certificate-url
The URL to fetch the certificate chain from
signature
Signature generated from the body

Fetching the certificate

Certificate URL validation

To check and validate the request signature, verify the URL in the signature-certificate-url header to make sure that it matches the format that TRACT uses.

This can help to protect against requests that attempt to make your web service download malicious files.

Make sure that the URL meets all of the following criteria:
  • The protocol is HTTPS (not case sensitive).
  • The hostname is the FQDN of the TRACT server you are communicating with. It is of the format *.haptikapi.com for production, and *.hellohaptik.com for staging. (not case sensitive).
  • The path is /tract/hooks/certificate/.
  • If a port is specified in the URL, the port is 443.
The following are examples of correctly formatted URLs:
  • https://subdomain.haptikapi.com/tract/hooks/certificate/
  • https://subdomain.haptikapi.com:443/tract/hooks/certificate/
  • https://subdomain.haptikapi.com:443//tract/hooks/certificate/
The following are examples of invalid URLs:
  • http://subdomain.haptikapi.com/tract/hooks/certificate/ (invalid protocol)
  • https://nothaptikapi.com/tract/hooks/certificate/ (invalid hostname)
  • https://subdomain.haptikapi.com/tract.api/hooks/certificate/ (invalid path)
  • https://subdomain.haptikapi.com/invalid.path/tract-api-cert.pem (invalid path)
  • https://subdomain.haptikapi.com:563/ect.api/ect-api-cert.pem (invalid port)

If the URL does not meet all of these criteria, do not proceed to the next stage

Download the PEM-encoded X.509 certificate using the URL specified in the signature-certificate-url header in the request.

This is provided at runtime so that it can be updated periodically.

This certificate chain we downloaded in the preceding stage is composed of the following, in order:

  • The ECT signing certificate.
  • Zero or more additional certificates create a chain of trust to a root certificate of a certificate authority (CA).

Certificate verification

To confirm the validity of the signing certificate, complete the following stages:

  • Check the Not Before and Not After dates of the signing certificate to make sure that it is not expired.
  • Make sure the domain name of the TRACT server is present in the Subject Alternative Names (SANs) section of the signing certificate.

If certificate verification fails, discard the request.

Signature verification

  • After confirming the validity of the signing certificate, extract the public key from it.
  • base64-decode the value of the signature header in the request to obtain the encrypted signature.
  • Use the public key extracted from the signing certificate to decrypt the encrypted signature to produce the asserted hash value.
  • Generate an SHA-256 hash value from the full HTTP request body to produce the derived hash value.
  • Compare the asserted hash value and derived hash values to ensure that they match.

If they do not match, discard the request. 

Note: Make sure you perform this before decoding the JSON body. You should always perform all cryptographic operations on raw bytes.

Note: the signature is generated as follows:

  1. We convert the prepared request body to bytes with UTF-8 encoding.
  2. Generate a SHA-256 hash of encoded body.
  3. ASN.1 encode the hash and convert it from hex to binary.
  4. Encrypt the binary blob with our private key.

These stages are provided only for reference. You should not need to do the above operations by hand. Your implementation language or framework WILL provide a one-shot signature verification system. It is HIGHLY RECOMMENDED that you use the high level functionality and avoid using low level primitives.

Timestamp verification

  • Every request that our web service sends includes a signature_timestamp in the request body.

  • The timestamp is part of the signed portion of the request, so it cannot be modified without also invalidating the request signature.

  • We use this timestamp to verify the freshness of the request.

  • This can help protect your web service from attempted replay attacks in which an attacker acquires a properly signed request and then repeatedly resends it to disrupt the web service.

We allow a tolerance of no more than 120 seconds (two minutes). This means that your service should only process requests in which the timestamp is within 120 seconds of the current time.

If the timestamp differs from the current time by more than 120 seconds, discard the request.

The timestamp is part of the request object in the JSON body of the request. For example:

{
    "user_id": "qUgNn5epx_QBQYL_.UrSrmMXE-_QmBZ-8pwglUWAJ8OcqhPK0yKjb",
    "conversation_number": 10,
    "message_body": "{\\"text\\": \\"7\\", \\"type\\": \\"TEXT\\", \\"data\\": {\\"quick_replies\\": []}}",
    "message_id": 83607,
    "sender": "ceu",
    "timestamp": "2021-08-05T10:59:14Z",
    "metadata": {},
    "signature_timestamp": "2021-08-06T08:42:39Z"
}

The timestamp value is an ISO 8601 formatted string, for example 2019-05-13T12:34:56Z. The time is in the UTC timezone. (As indicated by the Z in the timestamp string.)

You should HTTP error code 400 Bad Request to reject requests in which the timestamp falls outside the tolerance.

Sample python (django) snippet for verifying hook signatures  

import base64
import json
import pem
import requests
import time
from OpenSSL import crypto
from dateutil import parser
from django.core.handlers.wsgi import WSGIRequest
from url_normalize import url_normalize
from urllib.parse import urlparse

def request_handler(request: WSGIRequest):
    headers = request.META
    try:
        signature = headers['HTTP_' + 'signature'.upper()]
        certificate_url = headers['HTTP_' + 'signature-certificate-url'.upper()]
        body = request.body
        return VerifyIncomingHookSignature(certificate_url, signature, body).verify()
    except Exception as err:
        print(err)
        return False

class VerifyIncomingHookSignature(object):
    """docstring for VerifySelfSignedCert


    Attributes:
        arg (TYPE): Description
    """


    TOLERANCE = 60 * 2  # 2 minutes


    def __init__(self, signature_certificate_url: str, signature: bytes, body: bytes):
        super(VerifyIncomingHookSignature, self).__init__()


        print()
        print('signature_certificate_url: %s | %s' % (signature_certificate_url, type(signature_certificate_url)))
        self.signature_certificate_url = signature_certificate_url
        print('signature: %s | %s' % (signature, type(signature)))
        self.signature = signature
        print('body: %s | %s' % (body, type(body)))
        self.body = body


        self.certificate = None
        self.normalized_url = None


    def fetch_certificate(self):
        print(f'[INFO] begin fetch_certificate')
        try:
            self.certificate = json.loads(requests.get(self.signature_certificate_url).content)['certificate']
        except Exception as err:
            print(f'[FAIL] fetch_certificate: {err}')


    def normalize_url(self):
        print(f'[INFO] begin normalize_url')
        try:
            self.normalized_url = url_normalize(self.signature_certificate_url)
        except Exception as err:
            print(f'[FAIL] normalize_url: {err}')


    def validate_signature_cert_url(self):
        print(f'[INFO] begin validate_signature_cert_url')
        self.normalize_url()
        try:
            o = urlparse(self.normalized_url)
            assert o.scheme == 'https'
            assert o.hostname == 'devqa.hellohaptik.com'
            assert o.path == '/tract/hooks/certificate/'
            if o.port:
                assert o.port == 443
            self.fetch_certificate()
            return True
        except AssertionError:
            print('[FAIL] validate_signature_cert_url')
            return False
        except Exception as err:
            print(f'[FAIL] validate_signature_cert_url {err}')
            return False


    def load_certificate(self):
        print(f'[INFO] begin load_certificate')
        try:
            self.certificate = crypto.load_certificate(
                crypto.FILETYPE_PEM,
                pem.parse(bytes(self.certificate, 'utf-8'))[0].as_bytes()
            )
        except Exception as err:
            print(f'[FAIL] load_certificate: {err}')
        return True


    def verify_hash(self):
        print(f'[INFO] begin verify_hash')
        try:
            crypto.verify(self.certificate,
                          base64.b64decode(self.signature),
                          self.body,
                          'sha256')
            return True
        except crypto.Error as err:
            print(f'[FAIL] verify_hash: {err}')
            return False


    def validate_timestamp(self):
        print(f'[INFO] begin validate_timestamp')
        try:
            timestamp = parser.parse(json.loads(self.body)['signature_timestamp']).timestamp()
            now = time.time()
            if not now - self.TOLERANCE < timestamp < now + self.TOLERANCE:
                print(f'[FAIL] validate_timestamp')
                return False
        except Exception as err:
            print(f'[FAIL] validate_timestamp: {err}')
            return False
        return True


    def verify(self):
        result = (
                self.signature_certificate_url
                and self.signature
                and isinstance(self.signature, bytes)
                and self.body
                and isinstance(self.body, bytes)
                and self.validate_signature_cert_url()
                and self.load_certificate()
                and self.verify_hash()
                and self.validate_timestamp()
        )
        if result:
            print('ALL OK')
        return result
Delete

TRACT supports only one business per account. So if you are trying to implement agent escalations other than Smart Agent Chat, there should be one bot in one account. If you have more than one bot, then you need to contact Haptik support.

Was this article helpful?

Yes
No
Give feedback about this article

Related Articles

  • Introduction to ECT
  • Management APIs
  • Thread control (TC)
  • Appendix

Platform

  • Conversation Studio
  • Smart Skills
  • Advanced NLU
  • Intelligent Analytics
  • Omnichannel
  • Smart Agent Chat
  • Enterprise Security
  • Integrations

Solutions

  • Conversational Commerce
  • Lead Generation
  • Customer Care
  • WhatsApp
  • Conversational IVR
  • Google Business Messages

Industries

  • Retail/ E-Commerce
  • Financial Services
  • Travel & Hospitality
  • Telecom

Knowledge

  • ROI Calculator
  • Reports & Research
  • Case Studies
  • Webinars
  • ISAT
  • Tech Blog
  • Business Blog
  • Resources
  • Haptik v/s Yellow
  • Haptik v/s Liveperson
  • Haptik v/s IBM Watson
  • Haptik v/s Verloop
  • Conversations on AI

Company

  • Why Haptik
  • About Us
  • Careers
  • News & Media
  • Awards & Recognition
  • Contact Us
  • Partnerships
  • Investor Relations

Subscribe

Sign up to recieve the latest updates

Find us on

  • Twitter-footer
  • Linkedin-footer
  • YT-footer
  • Insta-footer
  • G2-footer
  • Facebook-footer

Knowledge Base Software powered by Helpjuice

Copyright © jio Haptik Technology Limited 2021 | Data Security & Privacy Policy | GDPR

North America | Asia Pacific | Africa | enterprise@haptik.ai

Definition by Author

0
0