logo

Database

Lack of data validation - Trust boundary violation

Need

Storage in the session of values that the server has verified, never of raw client input

Context

• Usage of Rust 1.75+ for building application services

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

• Usage of the actix-session crate for server-side session state

• Usage of the argon2 crate for password verification

Description

1. Non compliant code

use actix_session::Session;
use actix_web::{post, web, HttpResponse};
use serde::Deserialize;

#[derive(Deserialize)]
struct LoginForm {
    username: String,
    password: String,...

The `login` handler below copies the `username` and the `role` fields of the login form into the session before it checks anything. The form includes `role` as a hidden field, and the rest of the application reads `role` from the session to decide what the user may do. The session is on the trusted side of the application: code that reads it assumes its values were set by the server. Here, the values come straight from the client, and they are written even when the password is wrong. An attacker can submit `role=admin` with any user name, get an authenticated administrator session without valid credentials, and use it on every endpoint that trusts the session. Mixing untrusted input into trusted state this way is a trust boundary violation. It also appears when a handler stores a user identifier, a price or a permission flag from the request so that a later step can use it without checking it again.

2. Steps

• Write to `actix_session::Session` only values that the server has produced or verified, such as identifiers and roles loaded from the database after authentication.

• Never accept roles, permissions, prices or user identifiers from the request and store them in the session for later steps.

• Call `session.renew()` after a successful login or privilege change to prevent session fixation.

• Re-validate the data read from the session when it drives authorization, and load permissions from the database for sensitive operations.

• Prefer a server-side session store such as Redis, so session contents are never held by the client, even when signed or encrypted.

3. Secure code example

use actix_session::Session;
use actix_web::{post, web, HttpResponse};
use argon2::{Argon2, PasswordHash, PasswordVerifier};
use serde::Deserialize;
use std::collections::HashMap;

struct User {
    id: u64,...

The corrected handler only writes to the session after the credentials have been verified, and it writes values that come from the server, not from the form. The form no longer has a `role` field. `authenticate` looks up the user in the server-side store and verifies the password against its Argon2 hash; only when it succeeds does the handler take the user identifier and the role from that record. Any value the client tries to send for them is ignored. Before inserting, the handler calls `session.renew()`, which issues a new session identifier. This prevents session fixation, where an attacker makes the victim log in with a session identifier the attacker already knows.