# Sensitive data is encrypted at rest using platform-secure storage

- **Pattern:** `ab-000481` (`app-store-privacy-data.data-handling.sensitive-data-encryption`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000481
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000481)

## Why it matters

AsyncStorage in React Native is unencrypted plaintext on every platform — its contents are readable from a jailbroken iOS device or from any app on Android versions below 4.4. Storing auth tokens, JWTs, or user PII in AsyncStorage means a single compromised device exposes session credentials that can be replayed to impersonate the user with full account access. OWASP 2021 A02 (Cryptographic Failures), CWE-312 (Cleartext Storage of Sensitive Information), HIPAA §164.312(a)(2)(iv), and NIST SP 800-218 PW.4 all require sensitive data at rest to be encrypted using platform-provided mechanisms (Keychain on iOS, Keystore on Android).

## Severity rationale

High because auth tokens in AsyncStorage are accessible to any process on a compromised device, enabling session hijacking and full account takeover without a network attack.

## Remediation

Replace `AsyncStorage` for sensitive data with `expo-secure-store`, which uses Keychain on iOS and EncryptedSharedPreferences on Android:

```ts
import * as SecureStore from 'expo-secure-store';

// Store
await SecureStore.setItemAsync('authToken', token);

// Retrieve
const token = await SecureStore.getItemAsync('authToken');
```

For non-Expo React Native, use `react-native-keychain`:

```ts
import * as Keychain from 'react-native-keychain';
await Keychain.setGenericPassword('token', jwtValue);
```

Search for every `AsyncStorage.setItem` call in `src/` and audit what is being stored. Move tokens and PII to secure storage; leave only non-sensitive preferences (theme, language) in AsyncStorage.

## Detection

- **ID:** `sensitive-data-encryption`
- **Severity:** `high`
- **What to look for:** Count all relevant instances and enumerate each. Look for sensitive data stored in unencrypted storage. The key risk: `AsyncStorage` (React Native) is plaintext and unencrypted — any data stored there is readable from a jailbroken/rooted device and accessible to other apps on older Android versions. Search for `AsyncStorage.setItem` calls that store: auth tokens, session tokens, refresh tokens, JWT, API keys, user PII (name, email, phone, address), health data, financial data, or any object stringified with `JSON.stringify()` containing these fields. Compare against encrypted alternatives: `expo-secure-store` (Keychain on iOS, EncryptedSharedPreferences on Android), `react-native-keychain` (Keychain on iOS, Keystore on Android), `@react-native-async-storage/async-storage` combined with `rn-encrypted-storage`. For Flutter, look for `shared_preferences` storing sensitive data vs. `flutter_secure_storage`. For native iOS, look for `UserDefaults` storing tokens vs. `Keychain` (`SecItemAdd`). Flag: auth tokens in `AsyncStorage`; raw JWTs stored in `localStorage`-equivalent; any string containing "token", "secret", "key", "password", "ssn", "creditCard" stored in plaintext storage.
- **Pass criteria:** Auth tokens and sensitive PII are stored in platform-secure encrypted storage (Keychain on iOS, Keystore on Android, or equivalent). At least 1 implementation must be verified. No tokens or credentials found in `AsyncStorage` or `SharedPreferences`.
- **Fail criteria:** Auth tokens, session keys, or sensitive PII stored in `AsyncStorage`, `SharedPreferences`, or equivalent unencrypted storage.
- **Skip (N/A) when:** App is stateless (no local data persistence) or has no sensitive data storage (no auth, no PII collection).
- **Detail on fail:** `"JWT token stored via AsyncStorage.setItem('authToken', token) in src/services/auth.ts — AsyncStorage is unencrypted plaintext on all platforms"` or `"User profile including email and phone stored in AsyncStorage via JSON.stringify — sensitive PII in plaintext local storage"`
- **Remediation:** Unencrypted token storage is exploitable on jailbroken/rooted devices and creates privacy liability.
  1. Replace `AsyncStorage` for sensitive data with `expo-secure-store`:
     ```ts
     import * as SecureStore from 'expo-secure-store';

     // Store
     await SecureStore.setItemAsync('authToken', token);

     // Retrieve
     const token = await SecureStore.getItemAsync('authToken');
     ```
  2. For non-Expo React Native, use `react-native-keychain`:
     ```ts
     import * as Keychain from 'react-native-keychain';
     await Keychain.setGenericPassword('token', jwtValue);
     ```
  3. Audit every `AsyncStorage.setItem` call — move tokens and PII to secure storage, leave only non-sensitive preferences (theme, language) in AsyncStorage

## External references

- cwe CWE-312
- owasp A02
- nist SC-28
- hipaa §164.312(a)(2)(iv)
- ssdf PW.4

Taxons: cryptography-and-secrets

HTML version: https://auditbuffet.com/patterns/ab-000481
