Skip to main content

Overview

This vulnerability occurs when an application receives or retrieves data (such as cookies, session data, user-provided parameters, or files) but fails to verify that the data has not been tampered with. Even if data is encrypted, encryption alone (especially in modes like CBC without a MAC) does not prevent an attacker from modifying or corrupting the ciphertext, which may lead to predictable changes in the decrypted plaintext (e.g., a “bit-flipping” attack). This flaw also applies to plaintext data where integrity is crucial, like signed JWTs (where the signature must be checked) or cookies where a MAC (Message Authentication Code) should be used to prevent tampering.

Business Impact

Failure to check data integrity allows attackers to tamper with data undetected, leading to:
  • Privilege Escalation: Modifying a cookie or token (e.g., user_role=user to user_role=admin).
  • Authentication Bypass: Crafting or modifying session data to impersonate another user.
  • Data Corruption: Altering sensitive data in transit or at rest (e.g., changing transaction amounts).
  • Code Execution: Bit-flipping attacks against encrypted, serialized objects might lead to Insecure Deserialization (CWE-502).

Reference Details

CWE ID: CWE-353 Related CWEs: CWE-345 (Data Authenticity), CWE-565 / CWE-784 (Cookie Integrity) OWASP Top 10 (2021): A08:2021 - Software and Data Integrity Failures Severity: High

Framework-Specific Analysis and Remediation

This is a design and implementation flaw. Modern frameworks often provide integrity checks by default on sensitive data like cookies and tokens, but custom implementations can easily miss this. Key Remediation Principles:
  1. Use Authenticated Encryption (AEAD): Prefer modern encryption ciphers like AES-GCM or ChaCha20-Poly1305 that bundle encryption (confidentiality) and a MAC (integrity/authenticity) together.
  2. Encrypt-then-MAC: If using older ciphers (like AES-CBC), you must apply a strong MAC (like HMAC-SHA256) to the ciphertext and verify it before decrypting.
  3. Verify Signatures: For signed data (like JWTs), always verify the signature using a secure key before trusting the payload (see CWE-347).
  4. Use Framework Defaults: Rely on built-in framework mechanisms for session and cookie management (e.g., Django/Laravel encrypted cookies, ASP.NET Core Data Protection) as they typically include integrity checks.

  • Python
  • Java
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Using cryptography in AES-CBC mode without an HMAC, or rolling custom cookie mechanisms. Django’s default session/cookie backends are secure.

Vulnerable Scenario 1: AES-CBC without MAC

Data is encrypted but can be tampered with via a “bit-flipping attack.”
# utils/encryption_cbc.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
import base64

# (Assume KEY is loaded securely)

def encrypt_cbc_no_mac(data):
    iv = get_random_bytes(AES.block_size)
    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    padded_data = pad(data.encode(), AES.block_size)
    ciphertext = cipher.encrypt(padded_data)
    # DANGEROUS: Returning IV + Ciphertext without a MAC.
    # An attacker can flip bits in the ciphertext, which will
    # predictably garble the decrypted plaintext block.
    return base64.b64encode(iv + ciphertext)

def decrypt_cbc_no_mac(encoded_data):
    iv_and_ciphertext = base64.b64decode(encoded_data)
    iv = iv_and_ciphertext[:AES.block_size]
    ciphertext = iv_and_ciphertext[AES.block_size:]
    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    # DANGEROUS: Decrypting without first verifying integrity.
    # Attacker's bit-flipping will result in garbled data here.
    padded_plaintext = cipher.decrypt(ciphertext)
    plaintext = unpad(padded_plaintext, AES.block_size)
    return plaintext.decode() # Might return corrupted data
# views.py (Django)
def set_prefs_unsafe(request):
    # DANGEROUS: Storing user ID in a cookie without signing.
    # Attacker can change 'user_id=123' to 'user_id=1' (admin).
    response.set_cookie('user_prefs', 'user_id=123')
    return response

Mitigation and Best Practices

  • Encryption: Use an AEAD mode like AES-GCM.
  • Cookies: Use Django’s signed cookies (request.get_signed_cookie(), response.set_signed_cookie()) or encrypted cookies (default session backend) which provide integrity.

Secure Code Example

# utils/encryption_gcm.py (Secure - AES-GCM)
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64

# (Assume KEY is loaded securely)

def encrypt_gcm(data):
    # SECURE: AES-GCM provides authenticated encryption (AEAD).
    nonce = get_random_bytes(12) # GCM recommended nonce size
    cipher = AES.new(KEY, AES.MODE_GCM, nonce=nonce)
    ciphertext, tag = cipher.encrypt_and_digest(data.encode())
    # SECURE: Store nonce + tag + ciphertext.
    return base64.b64encode(nonce + tag + ciphertext)

def decrypt_gcm(encoded_data):
    data = base64.b64decode(encoded_data)
    nonce = data[:12]
    tag = data[12:28] # 16-byte tag
    ciphertext = data[28:]
    cipher = AES.new(KEY, AES.MODE_GCM, nonce=nonce)
    try:
        # SECURE: decrypt_and_verify() checks the tag (integrity).
        # Throws ValueError if tag/data is tampered with.
        plaintext = cipher.decrypt_and_verify(ciphertext, tag)
        return plaintext.decode()
    except (ValueError, KeyError) as e:
        # Integrity check failed!
        print(f"Decryption/Verification Failed: {e}")
        return None
# views.py (Django Secure Cookie)
def set_prefs_secure(request):
    response = HttpResponse("Prefs set")
    # SECURE: Uses Django's signing mechanism (based on SECRET_KEY)
    response.set_signed_cookie('user_id', 123, salt='my-salt-value')
    return response

def get_prefs_secure(request):
    try:
        # SECURE: Fails with BadSignature if tampered.
        user_id = request.get_signed_cookie('user_id', salt='my-salt-value')
        return HttpResponse(f"User ID: {user_id}")
    except signing.BadSignature:
        return HttpResponse("Invalid cookie signature", status=400)

Testing Strategy

Identify data passed between client/server or stored/retrieved that requires integrity (encrypted data, session cookies, tokens).
  • Encrypted Data: If using CBC, attempt to flip bits in the ciphertext and observe the decrypted result. If using AEAD (GCM), modify any part of the nonce, tag, or ciphertext; verify decryption fails.
  • Cookies: Modify the value of a signed cookie (like Django’s sessionid or a manually signed cookie). Verify the application rejects it (BadSignature).