logo

Database

Lack of data validation - Token

Need

Verification of the signature and claims of every JSON Web Token before trusting it

Context

• Usage of Rust 1.75+ for building application services

• Usage of the actix-web crate for handling HTTP requests

• Usage of the jsonwebtoken crate for validating JSON Web Tokens

Description

1. Non compliant code

use actix_web::http::header::AUTHORIZATION;
use actix_web::{get, HttpRequest, HttpResponse};
use jsonwebtoken::dangerous::insecure_decode;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Claims {
    sub: String,...

The `profile` handler below reads the bearer token from the `Authorization` header and decodes it with `jsonwebtoken::dangerous::insecure_decode`, which parses the claims without checking the signature or any other claim. Without signature verification, the token is just JSON that the client wrote. An attacker can take any token, change `sub` to another user or add an administrator role, re-encode it and send it: the handler accepts the claims as authentic, and even expired tokens pass. Calling `insecure_disable_signature_validation` on a `Validation` before `decode`, as older code does, has the same effect. This turns every endpoint that relies on the token into an authentication bypass.

2. Steps

• Remove every call to `jsonwebtoken::dangerous::insecure_decode` and to `Validation::insecure_disable_signature_validation` from production code.

• Decode tokens only with `jsonwebtoken::decode` and a `Validation` restricted to the algorithm the issuer uses.

• Configure the expected audience and issuer with `set_audience` and `set_issuer`, and require `exp` with `set_required_spec_claims`.

• Load the verification key from configuration at startup, and use the issuer's public key when tokens are signed with an asymmetric algorithm.

• Reject the request with `401 Unauthorized` whenever validation fails, and never fall back to reading unverified claims.

3. Secure code example

use actix_web::http::header::AUTHORIZATION;
use actix_web::{get, web, HttpRequest, HttpResponse};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Claims {
    sub: String,...

The corrected handler keeps signature verification enabled, which is the default of `Validation`, and makes the rest of the checks explicit in the `validation` function. `Validation::new(Algorithm::HS256)` accepts only that algorithm, so a token that declares `none` or a different algorithm in its header is rejected. `set_audience` and `set_issuer` reject tokens issued by another system or for another service, and `set_required_spec_claims` makes `exp`, `aud`, `iss` and `sub` mandatory, so a token without expiration is refused instead of being accepted forever. The `DecodingKey` is created once from the secret loaded at startup and shared through `web::Data`. Any validation failure returns `401 Unauthorized` without revealing which check failed.