Insecure temporary files
Need
Protection of sensitive data that must be staged in temporary files
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 the tempfile crate for temporary files
Description
1. Non compliant code
use actix_web::{post, web, HttpResponse};
use serde::Deserialize;
use std::{env, fs};
#[derive(Deserialize)]
struct ImportKey {
api_key: String,
}...The `import_key` handler below receives an API key and writes it to `backup.key` inside `env::temp_dir()`, the shared system temporary directory, so a background job can pick it up. On most systems that directory is `/tmp`, which every local user and process can list. The file is created with the default permissions, usually readable by everyone, under a fixed and predictable name. Any other process on the host, including a compromised dependency or another tenant of the same machine, can read the key. An attacker can also create `backup.key` in advance, or a symbolic link with that name, so that the service writes the secret into a file the attacker controls. The file is never deleted, so the key also survives on disk and in backups long after it was needed. Paths such as `/var/tmp` and `C:\Windows\Temp` have the same problems.
2. Steps
• Avoid writing passwords, tokens and keys to disk; pass them in memory or store them in a secrets manager.
• Never write sensitive data to `env::temp_dir()`, `/tmp`, `/var/tmp` or `C:\Windows\Temp` with a fixed file name.
• When a temporary file is required, create it with `tempfile::Builder::tempfile_in` inside a service-owned directory with mode `0700`.
• Keep the `NamedTempFile` handle for as long as the file is needed so it is deleted automatically, and delete it explicitly on error paths.
• Encrypt sensitive content before it reaches the filesystem, and exclude staging directories from backups.
3. Secure code example
use actix_web::{post, web, HttpResponse};
use serde::Deserialize;
use std::fs::DirBuilder;
use std::io::Write;
use std::os::unix::fs::DirBuilderExt;
use tempfile::NamedTempFile;
const PRIVATE_DIR: &str = "/var/lib/app/private";...The corrected handler stages the key with `stage_secret`, which avoids each of those problems. The file is created inside a directory that belongs to the service and is created with mode `0700`, so no other user can even list it. `tempfile::Builder::tempfile_in` gives the file a random name and creates it atomically with `O_EXCL` and mode `0600`, which prevents pre-created files and symbolic link attacks. The returned `NamedTempFile` deletes the file when it is dropped, so the secret does not outlive its use. The best option is still not to write the secret to disk at all: pass it to the background job in memory, or store it in a secrets manager and pass only a reference. When a file is unavoidable, the content should also be encrypted, and the directory should not be included in backups.
References
• 028. Insecure temporary files