logo

Database

Excessive privileges - Access Mode

Need

Creation of sensitive files with owner-only permissions

Context

• Usage of Rust 1.75+ on Unix-like systems for building application services

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

• Usage of std::os::unix::fs for file permissions

Description

1. Non compliant code

use actix_web::{put, web, HttpResponse};
use std::fs::OpenOptions;
use std::io::Write;
use std::os::unix::fs::OpenOptionsExt;

#[put("/settings/webhook-secret")]
async fn save_webhook_secret(body: web::Bytes) -> HttpResponse {
    // Readable and writable by every user on the host...

The `save_webhook_secret` handler below writes a webhook signing secret to `webhook.key`, creating the file with `OpenOptions::mode(0o666)`. Mode `0o666` gives read and write access to every user on the host (the process umask usually removes some of those bits, but the code cannot rely on it). Any other local account, a compromised process running as another user or a container that shares the volume can read the secret and forge webhooks, or overwrite it to lock the application out. Modes such as `0o777`, `0o644` for secrets, or `Permissions::from_mode(0o707)` with `fs::set_permissions` have the same effect.

2. Steps

• Create files that hold secrets or personal data with `OpenOptions::mode(0o600)`, and directories with `0o700`.

• Never use world-readable or world-writable modes such as `0o666`, `0o777` or `0o707` with `mode`, `set_mode` or `Permissions::from_mode`.

• Call `fs::set_permissions` when the file may already exist, because `mode` only applies at creation.

• Grant access to other services through a dedicated group with a mode such as `0o640` instead of world permissions.

• Run the service with a restrictive umask, such as `0o077`, as a second layer of defense.

3. Secure code example

use actix_web::{put, web, HttpResponse};
use std::fs::{self, OpenOptions, Permissions};
use std::io::Write;
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};

const SECRET_PATH: &str = "/var/lib/app/webhook.key";

fn write_secret(contents: &[u8]) -> std::io::Result<()> {...

The corrected handler creates the file with mode `0o600`, so only the account that runs the service can read or write it. `OpenOptions::mode` only applies when the file is created. If `webhook.key` already existed with broader permissions, for example from an earlier version of the code, it would keep them. The handler therefore also calls `fs::set_permissions` with `0o600` before writing, which fixes existing files. Secrets should live in a directory that is itself restricted to the service account, with mode `0o700`, and access for other services should be granted through groups with the narrowest mode that works, such as `0o640`, never through world permissions.