Securing an API - From basics to beyond
This repository is used to accompany and illustrate some security vulnerabilities highlighted during the Securing an API - From basics to beyond talk.
Table of Contents
- Important settings
- Install & Run
- Usefull links
- Play with API & login page
- Play with Oauth2
- Play with API Management
- Talk sessions
GIT - Seems obvious right... - 2.20.1+
Docker - Docker Engine release - 17.12.0+
Docker-compose - compose - 1.23+
Maven - Dependency Management / Back - 3.3.9+
Java - Compilation & runtime / Back - Java 8+
Npm - Dependency Management / Front - 6.8.0+
We have four stacks for this demo :
- A web application - Angular
- An API - Spring boot
- An Authorization Server - Gravitee Acces Management
- An API Management - Gravitee API Management
If you run the full stack (gravitee api management & access management) in addition to the initial spring boot api & angular website, then please make sure to follow these two below recommendations :
#1: Declare two domains into your /etc/hosts by running
sudo vi /etc/hosts and add following value:
127.0.0.1 apim.gravitee.io am.gravitee.io
#2: Upgrade default memory allocation to Docker For Mac (default 2, use at least 4 as described below)
Install & Run
For the first time run cmd
make init else run cmd
make start or have a look to command lines inside the Makefile.
- Front - Single Page App Demo : http://localhost:8888 - lusoalex/lusoalex
- API - Swagger specifications : http://localhost:8880/swagger-ui.html
- API - Database access : http://localhost:8880/console - jdbc:h2:mem:testdb
- Gravitee Access Management : https://am.gravitee.io/ui - admi/adminadmin
- Gravitee API Management : https://apim.gravitee.io/portal/ - admin/admin
- Traefik Dashboard : http://localhost:8080/dashboard/
Play with API & login page
Target here is only to show an example of injection which is currently the number 1 in the OWASP top 10. (latest owasp report was 2017)
We will inject some sql in the form field (password field). A common injection is to input
something' or 1=1 --, in our case, for the demo effect, we will use instead
something' or username='admin' --
JWT Brute force
By exploring the browser console, we can see that a JWT token is saved on the local storage.
By decoding (BASE64) the token or do it thanks to https://www.jwt.io, we see that the token use HS256 encryption.
HS256 is symetric encryption, i will then use a tool to try to get the shared secret. I use here brendan rius jwt-cracker, this tool can find secret when they are too small, which is our case.
You can find others tool with different way of finding the shared secret, like trying all password included in a file instead of doing brut force
Once we have the secret we can generate by ourself a token, use it to replace the current local storage token.
Just need to refresh and see what happens...
Play with Oauth2
Current webapp is using implicit or code flow oauth2 flow.
There's plenty of oauth2 hacks that happens in past years (facebook, google, github...), i will focus on redirect_uri.
My expectations here is only to show that we must always do exact matching on redirect, avoiding regex, wildcard & so on.
The client settings has a https://localhost:8888/login* redirect_uri, you can then change the redirect_uri with http://localhost:8888/login/../hack
Play with API Management
By restoring current mongo settings, the above API will be exposed through the API Management gateway on port 8082 and path /confoo
Call API through Gateway :
curl -X GET http://localhost:8082/confoo/users -H 'X-Gravitee-Api-Key: 4d58b5f6-88dc-4253-8f80-a70aca9d2989'
Call API through Gateway :
curl -X GET http://localhost:8082/confoo/users/f90df750-3069-48e6-8df7-50306988e691 -H 'X-Gravitee-Api-Key: 4d58b5f6-88dc-4253-8f80-a70aca9d2989'
I used a white list on the API endpoint to allow only /users and /users/ endpoint access and GET http verb only and forbid all others paths.
If you try to access to confoo/authenticate/basic, you'll get a 403 with api key, and 401 without api key.
Path not white listed API endpoint :
curl -X POST http://localhost:8082/confoo/authenticate/basic -H 'X-Gravitee-Api-Key: 4d58b5f6-88dc-4253-8f80-a70aca9d2989' -H 'Authorization: Basic bHVzb2FsZXg6bHVzb2FsZXg='
Path ok but not the http verb :
curl -X DELETE http://localhost:8082/confoo/users/f90df750-3069-48e6-8df7-50306988e691 -H 'X-Gravitee-Api-Key: 4d58b5f6-88dc-4253-8f80-a70aca9d2989'
Rate limiting & quota
Target here is to see what happens if we exceed the number of allowed api calls.
We'll run a wrk command to trigger many request in a short time and see what happens.
this is not a benchmark...
Run wrk command
Generate many requests for 10 seconds, using 12 threads, and keeping 400 HTTP connections open.
Most of the request will fall in 429 due to Rate limit exceeded ! You reach the limit of 10 requests per 1 seconds",
we have 6791 requests for 6692 failures, mean 100 request succeed.
I select an asynchronous rate limit to reduce the latency overhead, but we can enable it in synchronous...
We will stop the API container to see what happens when we try to access to the api through the api management gateway.
docker stop TALK_SECURITY_BACK_DEMO
As a consequence, the health check of the API will show an unavailability.
By doing the curl, you'll get a 503 (Service Unavailable) response. The gateway will not send the request to the API until the healh check is healthy.
That means it let the API try to reboot and reply to the request immediately (better than a timeout).
Any contributions are more than welcome. But before please pay attention to :