OWASP Web Top 10 — A02

Missing Encryption at Rest

When sensitive data is stored in plaintext on disk, in databases, or in backups, it becomes vulnerable to theft if the storage system is compromised. Encryption at rest protects data even when physical or logical access controls fail.

What Is Missing Encryption at Rest?

Missing Encryption at Rest refers to the security vulnerability where sensitive data is stored in an unencrypted, plaintext format on persistent storage media such as hard drives, databases, file systems, or backup archives. This means that if an attacker gains physical access to the storage medium, exploits a database vulnerability, or compromises backup systems, they can directly read sensitive information such as passwords, social security numbers, credit card details, medical records, or proprietary business data without needing to break any encryption.

Unlike encryption in transit (which protects data while it moves across networks using protocols like TLS/SSL), encryption at rest protects stored data while it is persisted on disk. The vulnerability arises from a false assumption that access controls, firewalls, and authentication mechanisms alone are sufficient to protect data. However, these controls can be bypassed through insider threats, stolen credentials, database exploits, or physical theft of storage devices. When data is stored unencrypted, a single point of compromise can expose vast amounts of sensitive information.

This vulnerability is ranked as A02 in the OWASP Top 10 2021 under Cryptographic Failures. Organizations face severe consequences from missing encryption at rest, including regulatory penalties under GDPR, HIPAA, PCI-DSS, and other data protection regulations that mandate encryption for sensitive data. Despite being a well-understood security requirement, many systems continue to store sensitive data in plaintext due to performance concerns, legacy system constraints, or developer oversight.

How It Works

1
Sensitive data is stored in plaintext

The application stores sensitive information such as user credentials, financial data, personal identifiable information (PII), or health records directly into a database column, file system, or backup archive without applying any encryption. For example, a database table might store credit card numbers or social security numbers as plain VARCHAR fields.

2
Data persists unencrypted on storage media

The unencrypted data is written to physical storage devices such as hard drives, SSDs, database files, log files, or backup tapes. While the data may be protected by access controls and authentication mechanisms at the application layer, it exists in readable plaintext format on the underlying storage medium.

3
Attacker gains unauthorized access to storage

An attacker compromises the storage system through various means: exploiting a SQL injection vulnerability to dump database contents, gaining access to backup files stored on cloud storage with weak credentials, stealing physical hard drives from a data center, or leveraging insider access to copy database files.

4
Sensitive data is directly accessible

Because the data is stored in plaintext, the attacker can immediately read and extract all sensitive information without needing to break encryption keys or perform cryptographic attacks. A simple database query, file viewer, or text editor reveals usernames, passwords, financial records, and other confidential data.

5
Data breach and exploitation

The attacker exfiltrates the stolen data for identity theft, financial fraud, or sale on dark web markets. The organization faces regulatory fines, legal liability, reputational damage, and loss of customer trust. The breach may remain undetected for months or years if proper monitoring and auditing mechanisms are not in place.

Vulnerable Code Example

Vulnerable — Java / Spring Boot
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    // VULNERABLE: Social Security Number stored in plaintext
    @Column(name = "ssn")
    private String socialSecurityNumber;

    // VULNERABLE: Credit card stored in plaintext
    @Column(name = "credit_card")
    private String creditCardNumber;

    // VULNERABLE: Sensitive medical data in plaintext
    @Column(name = "medical_record", columnDefinition = "TEXT")
    private String medicalRecord;

    // Getters and setters...
}

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void saveUser(User user) {
        // Data is saved directly to database without encryption
        // Database columns store sensitive data as plaintext VARCHAR/TEXT
        userRepository.save(user);
    }

    public void exportUserData(String filePath) throws IOException {
        List<User> users = userRepository.findAll();

        // VULNERABLE: Writing sensitive data to unencrypted file
        FileWriter writer = new FileWriter(filePath);
        for (User user : users) {
            writer.write(user.getSocialSecurityNumber() + ","
                + user.getCreditCardNumber() + "\n");
        }
        writer.close();
    }
}

// If an attacker gains database access or steals backup files:
// SELECT ssn, credit_card FROM users;
// Results: All sensitive data exposed in plaintext

Secure Code Example

Secure — Java / Spring Boot (Field-Level Encryption with AES-GCM)
@Component
public class EncryptionService {

    private final SecretKey secretKey;
    private static final String ALGORITHM = "AES/GCM/NoPadding";

    public EncryptionService() throws Exception {
        // Load encryption key from secure key management system (KMS)
        // In production: use AWS KMS, Azure Key Vault, HashiCorp Vault
        this.secretKey = loadKeyFromKMS();
    }

    public String encrypt(String plaintext) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        GCMParameterSpec parameterSpec = new GCMParameterSpec(128, generateIV());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);

        byte[] encryptedData = cipher.doFinal(plaintext.getBytes(UTF_8));
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    public String decrypt(String ciphertext) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        GCMParameterSpec parameterSpec = new GCMParameterSpec(128, extractIV());
        cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);

        byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
        return new String(decryptedData, UTF_8);
    }
}

@Converter
public class EncryptedStringConverter implements AttributeConverter<String, String> {

    @Autowired
    private EncryptionService encryptionService;

    @Override
    public String convertToDatabaseColumn(String attribute) {
        try {
            // SECURE: Encrypt before storing in database
            return encryptionService.encrypt(attribute);
        } catch (Exception e) {
            throw new RuntimeException("Encryption failed", e);
        }
    }

    @Override
    public String convertToEntityAttribute(String dbData) {
        try {
            // SECURE: Decrypt when reading from database
            return encryptionService.decrypt(dbData);
        } catch (Exception e) {
            throw new RuntimeException("Decryption failed", e);
        }
    }
}

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    // SECURE: SSN automatically encrypted/decrypted
    @Convert(converter = EncryptedStringConverter.class)
    @Column(name = "ssn")
    private String socialSecurityNumber;

    // SECURE: Credit card automatically encrypted/decrypted
    @Convert(converter = EncryptedStringConverter.class)
    @Column(name = "credit_card")
    private String creditCardNumber;

    // SECURE: Medical records encrypted at rest
    @Convert(converter = EncryptedStringConverter.class)
    @Column(name = "medical_record", columnDefinition = "TEXT")
    private String medicalRecord;

    // Getters and setters...
}

// Even if database is compromised, all sensitive fields
// are stored as encrypted ciphertext.
// Encryption keys are managed separately in secure KMS.

Types of Missing Encryption at Rest

Unencrypted Database Fields

The most common form of missing encryption at rest occurs when sensitive data like passwords, social security numbers, credit card details, API keys, or personal health information is stored in database columns as plaintext VARCHAR or TEXT fields. This includes both structured data in relational databases (MySQL, PostgreSQL) and unstructured data in NoSQL databases (MongoDB, Cassandra). Even when databases are behind firewalls, SQL injection vulnerabilities, stolen credentials, or insider threats can expose this data instantly.

Unencrypted File Storage

Applications often store sensitive documents, user uploads, exported reports, or configuration files on disk without encryption. This includes PDF documents containing financial records, CSV exports with customer data, uploaded medical images, or configuration files with API credentials. Cloud storage buckets (S3, Azure Blob) frequently contain unencrypted files that are misconfigured with public access. File system encryption (LUKS, BitLocker) provides protection against physical theft but does not protect against application-level compromises.

Unencrypted Backups & Logs

Database backups, application logs, and system snapshots often contain the same sensitive data as production systems but are stored in less secure locations without encryption. SQL dump files, automated database backups on cloud storage, application logs containing request/response data, or debugging logs with authentication tokens are prime targets. Backups may be retained for years, stored on removable media, or transferred to third-party backup services, creating multiple exposure points. Log aggregation systems (Splunk, ELK) may inadvertently store sensitive data extracted from application logs.

Impact

When sensitive data is stored without encryption, a single breach can have catastrophic consequences for both the organization and the individuals whose data is exposed. The severity depends on the type of data compromised and the scale of the breach.

Mass data exposure and identity theft

Unencrypted databases containing personal information enable large-scale identity theft. Attackers can harvest millions of records containing names, addresses, social security numbers, dates of birth, and other PII in a single breach. This data is sold on dark web markets and used for fraudulent credit applications, tax fraud, and account takeovers.

Financial fraud and payment card compromise

Plaintext storage of credit card numbers, bank account details, or payment tokens allows attackers to immediately conduct fraudulent transactions. Organizations violate PCI-DSS requirements by storing unencrypted cardholder data, leading to loss of payment processing privileges, mandatory forensic audits, and liability for fraudulent charges.

Regulatory penalties and legal liability

Failure to encrypt sensitive data violates GDPR, HIPAA, PCI-DSS, CCPA, and other data protection regulations. Organizations face massive fines (up to 4% of global revenue under GDPR), mandatory breach notifications, regulatory audits, class-action lawsuits, and potential criminal charges. Data subjects can claim damages for privacy violations.

Reputational damage and business disruption

Public disclosure of unencrypted data breaches destroys customer trust and brand reputation. Businesses experience customer churn, negative media coverage, loss of competitive advantage, difficulty acquiring new customers, and long-term revenue decline. Partner organizations may terminate contracts due to security concerns. Recovery can take years and cost hundreds of millions in remediation, legal fees, and lost business.

Prevention Checklist

Implement field-level encryption for sensitive data

Use application-level encryption (AES-256-GCM) to encrypt sensitive database columns before storage. Implement transparent encryption using JPA converters, Entity Framework value converters, or similar ORM mechanisms. This ensures data is encrypted at the application layer regardless of database-level protections. Use authenticated encryption modes (GCM, ChaCha20-Poly1305) to prevent tampering.

Enable database-level encryption (TDE)

Activate Transparent Data Encryption (TDE) on database servers to encrypt entire databases, data files, and log files at rest. TDE is supported by MySQL, PostgreSQL, SQL Server, Oracle, and most enterprise databases. While TDE does not protect against application-level attacks (SQL injection), it prevents data exposure from physical storage theft or unauthorized file access.

Use key management systems (KMS) for encryption keys

Never hardcode encryption keys in source code or configuration files. Use dedicated key management systems like AWS KMS, Azure Key Vault, Google Cloud KMS, or HashiCorp Vault to securely generate, store, rotate, and audit encryption keys. Implement key rotation policies and separate encryption keys from encrypted data storage. Use envelope encryption for large datasets.

Encrypt backups and archived data

Ensure all database backups, snapshots, exports, and archived data are encrypted before storage or transmission to backup locations. Use encrypted backup tools (pg_dump with encryption, mysqldump piped through GPG) or cloud-native encrypted backup services. Store backup encryption keys separately from backup data. Regularly test backup restoration procedures including decryption.

Sanitize logs and prevent sensitive data logging

Implement log filtering to prevent passwords, credit cards, API keys, tokens, and other secrets from appearing in application logs, debug output, or error messages. Use structured logging with automatic redaction of sensitive fields. Encrypt log files and rotate log encryption keys. Never log full request/response bodies containing user data. Use tools like git-secrets to prevent credentials in code repositories.

Conduct data discovery and classification audits

Regularly scan databases, file systems, and cloud storage to identify sensitive data that may be stored unencrypted. Use automated data discovery tools to locate PII, financial data, health records, and other regulated information. Classify data based on sensitivity and apply appropriate encryption controls. Implement Data Loss Prevention (DLP) systems to detect and prevent unencrypted sensitive data storage.

Real-World Examples

2021

T-Mobile Data Breach

T-Mobile suffered a data breach affecting over 54 million customers when attackers accessed servers containing unencrypted personal information including names, dates of birth, Social Security numbers, and driver's license information. The breach resulted from inadequate encryption of customer data at rest combined with compromised credentials. T-Mobile faced lawsuits and agreed to a $350 million settlement for security improvements.

2018

Marriott International

Marriott disclosed that attackers had unauthorized access to the Starwood guest reservation database for four years, compromising 500 million guest records. The exposed data included unencrypted passport numbers, payment card details, and personal information. The breach resulted in a £18.4 million GDPR fine from the UK ICO. Marriott admitted that encryption was not consistently applied to sensitive guest information.

2019

Capital One

A misconfigured AWS firewall allowed an attacker to access Capital One's cloud storage containing unencrypted personal information of over 100 million credit card customers and applicants, including Social Security numbers, bank account numbers, and credit scores. While some data was encrypted, significant portions were stored in plaintext. Capital One was fined $80 million by regulators and faces over $190 million in settlement costs.

2015

Anthem Health Insurance

Hackers breached Anthem's database and stole unencrypted personal information of 78.8 million customers, including names, birthdays, Social Security numbers, addresses, and employment information. Security experts criticized Anthem for storing sensitive health insurance data without encryption. The breach resulted in a $115 million settlement, one of the largest in history, and triggered multiple regulatory investigations into healthcare data security practices.

Ready to Test Your Knowledge?

Put what you have learned into practice. Try identifying and fixing missing encryption vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.