1
0
Fork 0
mirror of https://github.com/dani-garcia/vaultwarden.git synced 2025-09-12 04:32:42 +00:00

Security fixes for admin and sendmail

Because the Vaultwarden Admin Backend endpoints did not validated the Content-Type during a request, it was possible to update settings via CSRF. But, this was only possible if there was no `ADMIN_TOKEN` set at all. To make sure these environments are also safe I added the needed content-type checks at the functions.
This could cause some users who have scripts which uses cURL for example to adjust there commands to provide the correct headers.

By using a crafted favicon and having access to the Admin Backend an attacker could run custom commands on the host/container where Vaultwarden is running on. The main issue here is that we allowed the sendmail binary name/path to be changed. To mitigate this we removed this configuration item and only then `sendmail` binary as a name can be used.
This could cause some issues where the `sendmail` binary is not in the `$PATH` and thus not able to be started. In these cases the admins should make sure `$PATH` is set correctly or create a custom shell script or symlink at a location which is in the `$PATH`.

Added an extra security header and adjusted the CSP to be more strict by setting `default-src` to `none` and added the needed missing specific policies.

Also created a general email validation function which does some more checking to catch invalid email address not found by the email_address crate.

Signed-off-by: BlackDex <black.dex@gmail.com>
This commit is contained in:
BlackDex 2025-01-24 10:35:47 +01:00
commit 08fb368d5f
No known key found for this signature in database
GPG key ID: 58C80A2AA6C765E1
8 changed files with 69 additions and 55 deletions

View file

@ -1,8 +1,10 @@
use std::env::consts::EXE_SUFFIX;
use std::process::exit;
use std::sync::{
atomic::{AtomicBool, Ordering},
RwLock,
use std::{
env::consts::EXE_SUFFIX,
process::exit,
sync::{
atomic::{AtomicBool, Ordering},
RwLock,
},
};
use job_scheduler_ng::Schedule;
@ -12,7 +14,7 @@ use reqwest::Url;
use crate::{
db::DbConnType,
error::Error,
util::{get_env, get_env_bool, get_web_vault_version, parse_experimental_client_feature_flags},
util::{get_env, get_env_bool, get_web_vault_version, is_valid_email, parse_experimental_client_feature_flags},
};
static CONFIG_FILE: Lazy<String> = Lazy::new(|| {
@ -676,8 +678,6 @@ make_config! {
_enable_smtp: bool, true, def, true;
/// Use Sendmail |> Whether to send mail via the `sendmail` command
use_sendmail: bool, true, def, false;
/// Sendmail Command |> Which sendmail command to use. The one found in the $PATH is used if not specified.
sendmail_command: String, true, option;
/// Host
smtp_host: String, true, option;
/// DEPRECATED smtp_ssl |> DEPRECATED - Please use SMTP_SECURITY
@ -890,16 +890,12 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
}
if cfg.use_sendmail {
let command = cfg.sendmail_command.clone().unwrap_or_else(|| format!("sendmail{EXE_SUFFIX}"));
let command = format!("sendmail{EXE_SUFFIX}");
let mut path = std::path::PathBuf::from(&command);
if !path.is_absolute() {
match which::which(&command) {
Ok(result) => path = result,
Err(_) => err!(format!("sendmail command {command:?} not found in $PATH")),
}
}
// Check if we can find the sendmail command to execute
let Ok(path) = which::which(&command) else {
err!(format!("sendmail command {command} not found in $PATH"))
};
match path.metadata() {
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
@ -932,8 +928,8 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
}
}
if (cfg.smtp_host.is_some() || cfg.use_sendmail) && !cfg.smtp_from.contains('@') {
err!("SMTP_FROM does not contain a mandatory @ sign")
if !is_valid_email(&cfg.smtp_from) {
err!(format!("SMTP_FROM '{}' is not a valid email address", cfg.smtp_from))
}
if cfg._enable_email_2fa && cfg.email_token_size < 6 {