Approov is an API security solution used to verify that requests received by your backend services originate from trusted versions of your mobile apps, and here you can find a full working example for the Tyk API Gateway that is the base for the Approov quickstart.
For more information about how Approov works and why you should use it you can read the Approov Overview at the root of this repo.
The docker stack provided via the docker-compose.yml
file in this repo is used for development proposes and if you are familiar with docker then feel free to use it to follow the quickstart before you try it for real in your project.
For easier use of the docker stack we will use the ./tyk
bash script to issue the necessary docker-compose
commands.
Do not forget to properly setup the .env
file at the root of this repo before you start the docker stack.
cp .env.example .env
Edit rhe .env
file and add a dummy secret:
APPROOV_BASE64_SECRET=h+CX0tOzdAAR9l15bWAqvq7w9olk66daIH+Xk+IAHhVVHszjDzeGobzNnqyRze3lw/WVyWrc2gZfh3XXfBOmww==
This dummy secret was used to sign the valid Approov tokens in the tests that you can run with ./tyk test approov-token
.
The docker stack is composed by the Tyk API Gateway version 4.1
and Redis version 5.0
.
./tyk build
We will create a proxy for httpbin.org where all the API endpoints require a valid and not expired Approov token.
We will use a bash script helper to simplify the usage, while at same time we provide the cURL command being executed by it. You can always see the exact cURL command being executed via the bash script by invoking it in debug mode bash -x ./tyk command
.
The Python plugin will run as a Tyk Middleware that needs to be provided has a bundle that is loaded when the Tyk API Gateway it's started, therefore we need to build it before everything else.
./tyk setup python-build-bundle
Redis takes sometime to be ready to be used by the Tyk API Gateway, therefore the script will pause for 15 seconds before it returns to the terminal.
./tyk stack up gateway
You can omit the tyk-gateway
service to follow the logs of all services declared in the docker-compose.yml
file.
./tyk logs --follow tyk-gateway
./tyk setup python-plugin-security-policy
or
curl http://localhost:8002/tyk/policies -i -s -H 'x-tyk-authorization: ___YOUR_SUPER_SECRET_HERE___' -H 'Content-Type: application/json' -X POST -d '
{
"id": "approov.python",
"access_rights": {
"httpbin.org.python": {
"allowed_urls": [],
"api_id": "httpbin.org.python",
"api_name": "HttpBin Python Plugin",
"versions": [
"Default"
]
}
},
"org_id": "1",
"active": true,
"name": "Approov Python Plugin",
"rate": 0,
"per": 1,
"quota_max": -1,
"state": "active",
"tags": ["Approov"]
}'
The output:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 08 Sep 2022 18:26:25 GMT
Content-Length: 56
{"key":"approov.python","status":"ok","action":"added"}
The Tyk security policy for Approov doesn't require rate limiting or quota usage, because the Approov token check guarantees with a very high degree of confidence that incoming requests are from what the Tyk API Gateway expects, a genuine and unmodified instance of your mobile app, not one that is under attack or that has been tampered with. Bots will not succeed on accessing the API because they are not able to provide an Approov token, and fake tokens will fail the signature check.
./tyk setup python-plugin-api
or
ccurl http://localhost:8002/tyk/apis -i -s -H 'x-tyk-authorization: ___YOUR_SUPER_SECRET_HERE___' -H 'Content-Type: application/json' -X POST -d '{
"api_id": "httpbin.org.python",
"slug": "httpbin",
"name": "HttpBin Python Plugin",
"org_id": "1",
"auth": {
"auth_header_name": "Api-Key"
},
"definition": {
"location": "header",
"key": "x-api-version"
},
"version_data": {
"not_versioned": true,
"versions": {
"Default": {
"name": "Default",
"use_extended_paths": true
}
}
},
"proxy": {
"listen_path": "/",
"target_url": "https://httpbin.org",
"strip_listen_path": true
},
"active": true,
"enable_jwt": false,
"use_keyless": false,
"custom_middleware_bundle": "bundle.zip"
}'
The output:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Wed, 31 Aug 2022 17:52:02 GMT
Content-Length: 53
{"key":"httpbin.org","status":"ok","action":"added"}
The keys being modified in the API definition are the keys jwt_*
and the key auth_header_name
.
./tyk setup python-plugin-api-key
or
curl http://localhost:8002/tyk/keys/create -i -s -H 'x-tyk-authorization: ___YOUR_SUPER_SECRET_HERE___' -H 'Content-Type: application/json' -X POST -d '{
"allowance": 1000,
"rate": 1000,
"per": 1,
"expires": -1,
"quota_max": -1,
"org_id": "1",
"quota_renews": 1449051461,
"quota_remaining": -1,
"quota_renewal_rate": 60,
"access_rights": {
"httpbin.org.python": {
"api_id": "httpbin.org.python",
"api_name": "HttpBin Python Plugin",
"versions": ["Default"]
}
},
"meta_data": {},
"apply_policy_id": "approov.python"
}'
The output:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 08 Sep 2022 18:21:22 GMT
Content-Length: 97
{"key":"1759a7bdfbeec418ca500eb776cb126da","status":"ok","action":"added","key_hash":"03de5816"}
The API key (in this case the string 1759a7bdfbeec418ca500eb776cb126da
) is automatically added to the .env
file in order to later be used when running the tests with ./tyk test approov-token
.
For the changes to take effect you need to reload or restart your Tyk API Gateway:
./tyk stack reload
or
curl -i -H 'x-tyk-authorization: ___YOUR_TYK_SUPER_SECRET_HERE___' -s http://localhost:8002/tyk/reload/group
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 08 Sep 2022 18:37:00 GMT
Content-Length: 29
{"status":"ok","message":""}
Now that we have an API protected with Approov it's time to test it, which we will do in the next section.
Any incoming API request requires a correctly signed and not expired Approov token, thus we will test several scenarios to ensure that the Approov protection works as expected.
The Tyk API Gateway will only forward the API request to the API endpoint https://httpbin.org/uuid
when the Approov token passes the validation, otherwise the API request will be denied.
The Approov token is correctly signed and it's not expired.
./tyk test approov-token valid
or
curl http://localhost:8002/uuid \
-i \
-H 'Content-Type: application/json' \
-H 'Api-Key: 18e64cd239ba847a0a6de4925b6389cfa' \
-H 'Approov-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IlJFUExBQ0VfV0lUSF9ZT1VSX09SR0FOSVpBVElPTl9JRDg2OTZkYmQ4MDYxZjRiNTM5MDExYWU2OGI0ZmZjNzllIn0.eyJpc3MiOiJhcHByb292LmlvIiwicG9sIjoiaHR0cGJpbi5vcmciLCJleHAiOjQ3MDg2ODMyMDUuODkxOTEyfQ.u-rlLdZgaYUjUpU_wWi7nzeMgae_IfcT7asu22ptXn0'
The output:
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Length: 53
Content-Type: application/json
Date: Wed, 31 Aug 2022 18:34:09 GMT
Server: gunicorn/19.9.0
X-Ratelimit-Limit: -1
X-Ratelimit-Remaining: 0
X-Ratelimit-Reset: 0
{
"uuid": "afaaeb50-8f15-44cd-b1e2-1a1326bbfede"
}
The Approov token provided in the header of the request was signed with a secret not known by the Tyk API Gateway to signal that it cannot trust in the incoming API request.
./tyk test approov-token invalid-signature
or
curl http://localhost:8002/uuid \
-i \
-H 'Content-Type: application/json' \
-H 'Approov-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IlJFUExBQ0VfV0lUSF9ZT1VSX09SR0FOSVpBVElPTl9JRDg2OTZkYmQ4MDYxZjRiNTM5MDExYWU2OGI0ZmZjNzllIn0.eyJpc3MiOiJhcHByb292LmlvIiwicG9sIjoiaHR0cGJpbi5vcmciLCJleHAiOjQ3MDg2ODMyMDUuODkxOTEyfQ.u-rlLdZgaYUjUpU_wWi7nasMgae_IfcT7asu22ptXn0'
The output:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
X-Generator: tyk.io
Date: Thu, 08 Sep 2022 19:19:50 GMT
Content-Length: 31
{
"error": "Unauthorized"
}
The Approov token provided in the header of the request was correctly signed with the same secret known by the Tyk API Gateway, but the exp
claim is in the past, therefore the token it's expired and the incoming API request cannot be served.
./tyk test approov-token expired
or
curl http://localhost:8002/uuid \
-i \
-H 'Content-Type: application/json' \
-H 'Approov-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IlJFUExBQ0VfV0lUSF9ZT1VSX09SR0FOSVpBVElPTl9JRDg2OTZkYmQ4MDYxZjRiNTM5MDExYWU2OGI0ZmZjNzllIn0.eyJpc3MiOiJhcHByb292LmlvIiwicG9sIjoiaHR0cGJpbi5vcmciLCJleHAiOjE2NjE1MzAxMjB9.bdl_U893ahMEV5bEp7mPAIkRr53qVA0iuZs0LqvSIho'
The output:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
X-Generator: tyk.io
Date: Thu, 08 Sep 2022 19:21:42 GMT
Content-Length: 31
{
"error": "Unauthorized"
}
The Approov token isn't present in the headers of the request therefore the incoming API request cannot be served.
./tyk test approov-token missing
or
curl http://localhost:8002/uuid \
-i \
-H 'Content-Type: application/json'
The output:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
X-Generator: tyk.io
Date: Thu, 08 Sep 2022 19:23:57 GMT
Content-Length: 31
{
"error": "Unauthorized"
}
The Approov token in the headers of the request is empty therefore the incoming API request cannot be served.
./tyk test approov-token empty
or
curl http://localhost:8002/uuid \
-i \
-H 'Content-Type: application/json' \
-H 'Approov-Token: '
The output:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
X-Generator: tyk.io
Date: Thu, 08 Sep 2022 19:25:05 GMT
Content-Length: 31
{
"error": "Unauthorized"
}
If you find any issue while following our instructions then just report it here, with the steps to reproduce it, and we will sort it out and/or guide you to the correct path.
If you wish to explore the Approov solution in more depth, then why not try one of the following links as a jumping off point: