The Toloka API sends event notifications that may contain sensitive data. It's important to prevent data leaks and ensure reliable data protection. To do this, make sure that requests are being sent through the Toloka API.
An additional header — Toloka-Signature — is used to confirm that the Toloka API is the source of notifications. The header is generated from request parameters. The HMAC Sha256 hashing algorithm is used for creating the signature.
When creating a subscription, add the secret_key parameter to the request body. It checks whether incoming requests were sent through the Toloka API. If the parameter was added, the additional Toloka-Signature header appears in the event notification.
The Toloka-Signature header consists of three fields:
To verify that requests were sent via the Toloka API:
Make sure that all the necessary fields are specified in the header. Example:
Toloka-Signature : {v=1, ts=946728000000, sign=d3...ab}
Concatenate the values of the ts and v fields with the request payload string. Use a dot as the separator.
946728000000.1.{"events":...}
With the HMAC Sha256 algorithm, generate a signature from the concatenated string and your secret key.
hmac_sha256(secret_key, '946728000000.1.{"events":...}')
Compare the generated signature and the signature from the Toloka-Signature header. If they match, the requests were sent using the Toloka API.
Let's take a look at a sample event notification sent by the Toloka API:
POST /webhook_endpoint HTTP/1.1Host: client_apiToloka-Signature: {v=1, ts=946728000000, sign=609af3eefd4c12b6afad30ab456efcd21fe82f4247d3340151a3ca0c97a6cbcb}Content-Type: application/jsonContent-Length: 355{ "events": [ { "event_time": "2000-01-01T12:00:00", "project_id": "project-1", "pool_id": "pool-1", "uuid": "00000000-0000-0000-0000-000000000000", "task_suite_id": "task-suite-1", "assignment_id": "assignment-1", "webhook_subscription_id": "subscription-1", "type": "ASSIGNMENT_APPROVED" } ]}
This incoming request is validated as follows:
import hmacimport hashlibdef is_valid_signature(secret_key, request_payload, toloka_header_ts, toloka_header_v, toloka_header_sign): data = str(toloka_header_ts) + '.' + str(toloka_header_v) + '.' + request_payload signature = hmac.new(bytearray(secret_key.encode()), bytearray(data.encode()), hashlib.sha256) return signature.hexdigest() == toloka_header_sign# the secret key used when creating a subscriptionsecret_key = '12345'# the body of the request received from the Toloka APIrequest_payload = "{\"events\":[{\"event_time\": \"2000-01-01T12:00:00\", \"project_id\":\"project-1\", \"pool_id\":\"pool-1\", \"uuid\":\"00000000-0000-0000-0000-000000000000\", \"task_suite_id\":\"task-suite-1\", \"assignment_id\":\"assignment-1\", \"webhook_subscription_id\":\"subscription-1\", \"type\":\"ASSIGNMENT_APPROVED\"}]}"# the "ts" field value from the incoming Toloka-Signature headertoloka_header_ts = 946728000000# the "v" field value from the incoming Toloka-Signature headertoloka_header_v = 1# the "sign" field value from the incoming Toloka-Signature headertoloka_header_sign = '609af3eefd4c12b6afad30ab456efcd21fe82f4247d3340151a3ca0c97a6cbcb'if (is_valid_signature(secret_key, request_payload, toloka_header_ts, toloka_header_v, toloka_header_sign)): print('Valid signature')else: print('Invalid signature')