# PYTHON-CRYPTO-SEC-031: Unauthenticated Cipher Mode Audit (cryptography lib)

> **Severity:** MEDIUM | **CWE:** CWE-327 | **OWASP:** A02:2021

- **Language:** Python
- **Category:** Cryptography
- **URL:** https://codepathfinder.dev/registry/python/cryptography/PYTHON-CRYPTO-SEC-031
- **Detection:** `pathfinder scan --ruleset python/PYTHON-CRYPTO-SEC-031 --project .`

## Description

Audit rule that flags usage of CBC, CTR, CFB, and OFB cipher modes from the `cryptography` library via `modes.CBC()`, `modes.CTR()`, `modes.CFB()`, and `modes.OFB()`. These modes provide confidentiality but do NOT provide ciphertext authentication or integrity protection. Without a separate message authentication code (MAC), an attacker who can observe or manipulate ciphertext can mount padding oracle attacks (CBC), bit-flipping attacks (CTR/CFB/OFB), or replay attacks — all without knowing the encryption key.
This is an audit rule, not a hard vulnerability rule. CBC with a correct Encrypt-then-MAC construction using HMAC-SHA256 is cryptographically sound, but the analysis engine cannot currently detect whether an HMAC is applied to the ciphertext after encryption. Therefore all four modes are flagged for manual review. False positives are expected for code that correctly combines these modes with HMAC — the recommended action is to verify the HMAC is present and suppress the finding if the implementation is correct.
For new code, the simplest correct choice is AES-GCM via `AESGCM` from `cryptography.hazmat.primitives.ciphers.aead` — it provides both confidentiality and authentication in a single primitive with no risk of HMAC omission.


## Vulnerable Code

```python
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from Crypto.Cipher import AES

# SEC-031: CBC mode (unauthenticated - audit)
cbc_cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())

aes_cbc = AES.new(key, AES.MODE_CBC, iv)
```

## Secure Code

```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

# PREFERRED: AES-GCM provides confidentiality + authentication, no HMAC needed
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
ct = aesgcm.encrypt(nonce, b"sensitive data", associated_data=b"header")

# ACCEPTABLE: CBC + HMAC (Encrypt-then-MAC) — verify MAC BEFORE decrypting
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import hmac, hashes, padding
import os

key_enc = os.urandom(32)
key_mac = os.urandom(32)
iv = os.urandom(16)
padder = padding.PKCS7(128).padder()
padded = padder.update(b"sensitive data") + padder.finalize()
cipher = Cipher(algorithms.AES(key_enc), modes.CBC(iv))
enc = cipher.encryptor()
ct = enc.update(padded) + enc.finalize()
h = hmac.HMAC(key_mac, hashes.SHA256())
h.update(iv + ct)
tag = h.finalize()
# Store (iv, ct, tag) and verify tag before decrypting

# SECURE: ChaCha20Poly1305 — modern AEAD alternative to AES-GCM
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
key = ChaCha20Poly1305.generate_key()
nonce = os.urandom(12)
chacha = ChaCha20Poly1305(key)
ct = chacha.encrypt(nonce, b"sensitive data", associated_data=None)

```

## Detection Rule (Python SDK)

```python
from rules.python_decorators import python_rule
from codepathfinder import calls, flows, QueryType
from codepathfinder.presets import PropagationPresets

class CryptoModes(QueryType):
    fqns = ["cryptography.hazmat.primitives.ciphers.modes"]


@python_rule(
    id="PYTHON-CRYPTO-SEC-031",
    name="Unauthenticated Cipher Mode (cryptography)",
    severity="MEDIUM",
    category="cryptography",
    cwe="CWE-327",
    tags="python,cryptography,cipher-mode,unauthenticated,CWE-327",
    message="CBC/CTR/CFB/OFB without authentication (HMAC). Use GCM or add HMAC.",
    owasp="A02:2021",
)
def detect_unauthenticated_mode_crypto():
    """Audit: detects CBC/CTR/CFB/OFB mode usage that may lack authentication."""
    return CryptoModes.method("CBC", "CTR", "CFB", "OFB")
```

## How to Fix

- Migrate to `AESGCM` from `cryptography.hazmat.primitives.ciphers.aead` for new code — it provides authenticated encryption with no risk of forgetting the MAC.
- If CBC is required (e.g., for protocol compatibility), use Encrypt-then-MAC with HMAC-SHA256. Always verify the MAC before decrypting — never decrypt-then-verify.
- For CTR/CFB/OFB modes, add an HMAC over (nonce || ciphertext) before transmitting. Verify the HMAC on receipt before decryption.
- Consider ChaCha20Poly1305 as an alternative AEAD if AES hardware acceleration is unavailable.
- Do not use CBC for TLS-like record protocols — use TLS 1.3 which mandates AEAD-only cipher suites.

## FAQ

**Q: Is CBC safe if I use it with HMAC?**

Yes — CBC used with Encrypt-then-MAC (ETM) where HMAC-SHA256 covers the IV and ciphertext is cryptographically sound. The critical requirements are: (1) use Encrypt-THEN-MAC, not MAC-then-Encrypt; (2) verify the MAC before performing any decryption; (3) use separate keys for encryption and MAC. The analysis engine flags CBC because it cannot detect whether HMAC is correctly applied, not because CBC+HMAC is inherently broken.


**Q: What is a padding oracle attack and why does it affect CBC?**

In CBC mode, decryption of each block depends on the previous ciphertext block via XOR. If a decryption endpoint reveals whether padding is valid (through different error messages, timing differences, or exceptions), an attacker can perform a byte-by-byte oracle attack to decrypt any ciphertext block without the key. Famous examples include POODLE (CVE-2014-3566) and Lucky13. Using Encrypt-then-MAC and rejecting invalid MACs before any decryption eliminates this attack surface.


**Q: What is a bit-flipping attack and why does it affect CTR/CFB/OFB?**

CTR, CFB, and OFB modes produce keystream that is XORed with plaintext. An attacker who can modify a specific bit in the ciphertext will flip the corresponding bit in the decrypted plaintext in a predictable way — without knowing the key. Without a MAC, an attacker can modify ciphertext to produce controlled plaintext changes at known positions (e.g., flipping an "admin=false" flag to "admin=true" in an encrypted cookie).


**Q: Why does this rule flag CTR mode if CTR is commonly recommended?**

CTR mode itself provides good confidentiality. The flag is not about CTR being broken but about CTR lacking authentication. If an application uses CTR mode without HMAC, it is vulnerable to bit-flipping attacks on decrypted data. The rule flags CTR as an audit checkpoint to confirm HMAC is present. CTR+HMAC is a valid construction.


**Q: Why does this rule flag both CBC and CTR together?**

All four modes (CBC, CTR, CFB, OFB) share the property of providing confidentiality without authentication. While CBC is also vulnerable to padding oracle attacks (an additional risk CTR avoids), both families require separate MAC protection. A single audit rule covering all four avoids missing any unauthenticated mode usage.


**Q: Should I use AES-GCM or AES-CBC+HMAC for new applications?**

For new applications use AES-GCM via AESGCM — it is simpler (no separate MAC key, no risk of incorrect ETM ordering), hardware-accelerated on modern CPUs (AES-NI + PCLMULQDQ), and the standard choice in TLS 1.3, modern SSH, and cloud cryptography APIs. CBC+HMAC is acceptable when protocol compatibility requires it but introduces implementation complexity that GCM avoids.


**Q: What is the difference between MAC-then-Encrypt and Encrypt-then-MAC?**

Encrypt-then-MAC (ETM) applies HMAC to the ciphertext after encryption — the MAC covers the ciphertext and IV. Verification rejects tampered ciphertext before any decryption occurs, eliminating padding oracle attack surface. MAC-then-Encrypt applies HMAC to plaintext before encryption — the MAC is then encrypted. Verification requires decryption first, which re-exposes padding oracle vulnerabilities. Always use Encrypt-then-MAC.


## References

- [CWE-327: Use of a Broken or Risky Cryptographic Algorithm](https://cwe.mitre.org/data/definitions/327.html)
- [NIST SP 800-38A: Recommendation for Block Cipher Modes of Operation](https://csrc.nist.gov/publications/detail/sp/800-38a/final)
- [NIST SP 800-38D: Recommendation for GCM Mode](https://csrc.nist.gov/publications/detail/sp/800-38d/final)
- [Padding Oracle Attacks: Vaudenay (2002), POODLE (CVE-2014-3566), Lucky13](https://www.openssl.org/~bodo/tls-cbc.txt)
- [Authenticated Encryption: Relations among notions and analysis (Bellare, Namprempre)](https://csrc.nist.gov/publications)
- [OWASP Cryptographic Failures (A02:2021)](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/)

---

Source: https://codepathfinder.dev/registry/python/cryptography/PYTHON-CRYPTO-SEC-031
Code Pathfinder — Open source, type-aware SAST with cross-file dataflow analysis
