security: harden WebDAV server against Slowloris/connection starvation and fix LZ4 bomb protection
This commit is contained in:
+44
-1
@@ -436,7 +436,19 @@ pub fn decrypt_chunk(
|
||||
match buffer[0] {
|
||||
COMPRESSION_NONE => Ok(buffer[1..].to_vec()),
|
||||
COMPRESSION_LZ4 => {
|
||||
let decompressed = lz4_flex::decompress_size_prepended(&buffer[1..])
|
||||
let payload = &buffer[1..];
|
||||
if payload.len() < 4 {
|
||||
bail!("LZ4-Chunk beschädigt: Payload zu kurz für Längen-Präfix");
|
||||
}
|
||||
let uncompressed_size = u32::from_le_bytes(payload[0..4].try_into().unwrap()) as usize;
|
||||
if uncompressed_size > CHUNK_SIZE {
|
||||
bail!(
|
||||
"LZ4-Dekomprimierungsfehler: Decompression-Bomb Schutz ausgelöst (angeforderte Größe {} Bytes > Limit {} Bytes)",
|
||||
uncompressed_size,
|
||||
CHUNK_SIZE
|
||||
);
|
||||
}
|
||||
let decompressed = lz4_flex::decompress_size_prepended(payload)
|
||||
.map_err(|e| anyhow::anyhow!("LZ4-Dekomprimierungsfehler im Chunk: {e}"))?;
|
||||
Ok(decompressed)
|
||||
}
|
||||
@@ -704,6 +716,37 @@ mod tests {
|
||||
let recovered_cid_1 = i64::from_le_bytes(unwrapped_1[64..72].try_into().unwrap());
|
||||
assert_eq!(recovered_cid_1, carrier_node_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lz4_decompression_bomb_protection() {
|
||||
use aes_gcm::KeyInit;
|
||||
let dek = generate_dek();
|
||||
let cipher = Aes256Gcm::new_from_slice(&*dek).unwrap();
|
||||
let node_id = 999;
|
||||
let chunk_index = 0;
|
||||
let aad = build_chunk_aad(node_id, chunk_index);
|
||||
|
||||
// Erstelle präparierte LZ4-Payload mit deklarierter Größe von 5 MB (> 1 MB CHUNK_SIZE)
|
||||
let mut malicious_plaintext = Vec::new();
|
||||
malicious_plaintext.push(COMPRESSION_LZ4);
|
||||
let fake_uncompressed_size: u32 = 5 * 1024 * 1024; // 5 MB
|
||||
malicious_plaintext.extend_from_slice(&fake_uncompressed_size.to_le_bytes());
|
||||
malicious_plaintext.extend_from_slice(&[0u8; 32]); // Dummy-LZ4-Payload
|
||||
|
||||
let mut nonce_bytes = [0u8; 12];
|
||||
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut nonce_bytes);
|
||||
let nonce = Nonce::from_slice(&nonce_bytes);
|
||||
|
||||
let mut ct = malicious_plaintext.clone();
|
||||
let tag = cipher.encrypt_in_place_detached(nonce, &aad, &mut ct).unwrap();
|
||||
let tag_bytes: [u8; 16] = tag.as_slice().try_into().unwrap();
|
||||
|
||||
// Entschlüsselung muss fehlschlagen, da Dekomprimierungs-Bomb-Schutz greift
|
||||
let res = decrypt_chunk(&dek, node_id, chunk_index, &ct, &nonce_bytes, &tag_bytes, FORMAT_VERSION_V2);
|
||||
assert!(res.is_err(), "Dekomprimierungs-Bomb über 1 MB muss abgewiesen werden!");
|
||||
let err_msg = res.err().unwrap().to_string();
|
||||
assert!(err_msg.contains("Decompression-Bomb Schutz ausgelöst"), "Fehlermeldung erwartet: {}", err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user