This blog is the first installation in a series of security centred articles that is intended to help django developers secure their deployments. In this piece I wish talk about the security setup required for the secure use of JSON Web Token, an authorization mechanism used while transferring information in REST Frameworks such as Django REST-APIs
With business needs demanding more from web-applications, product teams have moved towards light-weight application development for scalability and efficiency. This usually includes building applications that use RESTful web-services, which use Application Programming Interface (API) to interact with other applications and web-services. One such popular web-framework that supports such an architecture is Django Web-Framework.
Django is a stateless application. Its completely decoupled; its front-end and back-end are completely different. Hence, it enables developers to build lightweight applications. Its popularity can also be attributed to its security features by design. It protects(see “Security in Django”) against multiple vulnerabilities such as SQL injection, CSRF, CSS, Clickjacking, and Session Hijacking. However, this doesn’t mean it’s completely secure. In fact certain vulnerabilities like JSON Web Token (JWT) attacks, XML External Entity or Insecure Direct Object Reference (IDOR) can only be protected against by proactive security measures that the developer needs to take when building an application.
What is JWT and Why is JWT necessary?
In a REST-Framework, you need some sort of token-based authorization mechanism that is compact, easier to scale, and reusable. Thats where JSON Web Token (JWT) comes in; it meets all the requirements. It is compact, supports a wide range of algorithms, and its parsing is common in most programming languages.
How does it work?
- The application requests authorization to the authorization server. This is performed through one of the different authorization flows.
- When the authorization is granted, the authorization server returns a JWT token to the application.
- The application uses the access token to access a protected resource (like an API).
- Three sections separated with dots
- All are base 64 encoded
- Header - usually contains 2 parts, in a JSON format
- Alg: hashing algorithm (HS256, RS512, ES384ect)
- Type: should be JWT
- Payload - contains
- The information which we need to transmit
- The information related to token itself
- Information is JSON representation of claims (key:value)
- A hash of header and payload using a secret
Okay, now that you know what JWTs are and their role in Django REST-framework, let's look at its security aspect.
JWT Attack Surface
JSON Web Tokens are not insecure by design. Any attacks you might come across involving JWT is largely down to poor implementation. A good example of such a vulnerability is the one Auth0(Auth0 is one of the biggest identity-as-a-service platform) faced which enabled any malicious user to bypass login through cross-site request forgery (CSRF). All that any malicious user would need is the victim’s user ID.
- Sensitive data in JWT
JWTs are used for authorization, as mentioned earlier, and hence has sensitive data embedded in it. JWTs have two JSON objects that store information; The header and the Payload. The header consists of information about what algorithm was used to sign or encrypt the payload in that token. Header by itself is not encrypted. The token is base64 encoded, which can be easily decoded. And, with the available algo info, you can decrypt the payload. Hence, it is very crucial to use strong secrets to encrypt the payload before using in a JWT.
2. Change the algorithm to “none”
All libraries allow the “none” option for a signature algorithm. This means, when a user submits the token for authorization, there is no signature verification done in the backend.
In the image above, on the right, the base64 encoded data before the first dot includes the algorithm info. Shown on the right side of the image is the decoded data, and as we can see that the “alg” field is using HS256. This can be replaced with “none”, and some implementations will accept it as a legit token,
So, a malicious user can change the algorithm to “none”, remove the signature, and use the modified token to access a server.
3. Exploiting HS256
The algorithm HS256 uses a secret key to sign and verify each message. And, the algorithm RS256 uses a private key to sign messages, and a public key to verify them. If we change the algorithm from RS256 to HS256, the signature is now verified using the HS256 algorithm using the public key as secret key. Since the public key is not secret at all, we can correctly sign such messages. Here is how one can exploit them:
- Get a token signed with RSA (you only have access to the public key)
- Decode the header and change the algorithm from RSA “RS256” to HMAC “HS256”
- Tamper with the payload
- Sign the token with the public RSA key
Securing JSON Web Tokens
Now that you know what the attack surface for Django web-services look like, the following approach to securing them will become obvious to you.
- Use strong keys and secrets to encrypt
- Review the libraries you pick
- Make sure you check the signature
- Make sure your tokens expire
- Enforce the algorithm
Django is a very easy to use framework that already comes with robust security measures. However, developers should proactively take measures to protect their application from attacks such as JWT manipulation. To further your understanding of how to secure your Django Framework, I recommend watching Tilak’s talk( see “Unique ways to Hack into a Python Web Service” at DjangoCon US 2018.