Security

    iOS Keychain vs UserDefaults: where to store tokens

    A 2026 view contrasting an auth token stored encrypted in the iOS Keychain with a token mistakenly written in plain text to UserDefaults in the app container

    Storing an auth token in UserDefaults is one of the most common iOS security mistakes, and it is easy to make because UserDefaults is so convenient. The problem is that UserDefaults is not secure storage: it writes to a plain property list in your app's container, readable on a compromised or backed-up device. The Keychain exists for exactly this, encrypted, system-managed storage for small secrets. Here is why a token belongs in the Keychain, never in UserDefaults, and how to use each correctly.

    Short answer

    On iOS, store tokens, passwords, and other secrets in the Keychain, not in UserDefaults. Per Apple's Keychain documentation, the Keychain is encrypted, system-managed secure storage designed for small secrets, with access controls and hardware-backed key protection. UserDefaults, by contrast, stores values in a plain, unencrypted property list inside your app container, so anything there can be read from a backup or a compromised device. So UserDefaults is for non-sensitive preferences like settings and flags, while the Keychain is for anything that must stay confidential. Putting a token in UserDefaults is a storage vulnerability; moving it to the Keychain is the fix.

    What you should know

    • UserDefaults is not secure: it stores values in plain, unencrypted form.
    • The Keychain is encrypted: system-managed storage for small secrets.
    • Tokens belong in the Keychain: along with passwords and keys.
    • UserDefaults is for preferences: non-sensitive settings and flags only.
    • Keychain has access controls: choose an appropriate accessibility level.

    Why is UserDefaults wrong for tokens?

    Because it was never meant to be secure, and it is not. UserDefaults persists its data to a property list file in your app's container, stored in plain text, so any value you put there, including an auth token or an API key, is readable by anyone who can access the app's files. That includes someone inspecting a device backup, or an attacker on a jailbroken device browsing the container. There is no encryption and no access control on UserDefaults; it is a convenience store for small, non-sensitive values like a theme preference or an onboarding flag. So a token in UserDefaults is effectively stored in the clear, which is exactly the kind of insecure storage that turns a lost or compromised device into an account compromise.

    Why is the Keychain the right place?

    Because it is purpose-built secure storage for secrets. The Keychain encrypts the items you store, manages the keys for you with hardware backing on supported devices, and gates access with accessibility attributes that control when an item can be read, for example only while the device is in use and not locked. It is designed for small, sensitive values, exactly the size of a token, password, or key, and it persists across app launches. You also control whether items are restricted to the current device and excluded from backups. So the Keychain gives a token the encryption, access control, and key management that UserDefaults entirely lacks, which is why it is the standard place to keep anything confidential on iOS.

    Keychain versus UserDefaults

    The choice comes down to whether the value is sensitive. The table contrasts them.

    AspectKeychainUserDefaults
    EncryptionYes, encrypted and key-managedNo, plain property list
    Intended dataSmall secrets: tokens, passwords, keysNon-sensitive preferences and settings
    Access controlAccessibility attributes per itemNone
    Backup and device scopeConfigurable, can be device-onlyStored and backed up in the clear
    Right for an auth tokenYesNo

    The takeaway is direct: a token, credential, or key goes in the Keychain, and UserDefaults is reserved for data you would not mind anyone reading. Choosing UserDefaults for a secret is the mistake; the Keychain is the correct default for confidential values.

    What to watch out for

    The first trap is reaching for UserDefaults because it is one line of code, when the value is a secret that belongs in the Keychain. The second is using an overly permissive Keychain accessibility level, so prefer one that requires the device to be in use rather than locked, and use a device-only option for items that should not move to a new device via backup. The third is storing a large or non-secret blob in the Keychain, which is the wrong tool for that. A pre-submission scan such as PTKD.com (https://ptkd.com) reads the compiled IPA against OWASP MASVS and flags sensitive data stored insecurely, such as a token in UserDefaults or a plist, so you can confirm secrets actually go to the Keychain before you ship. Moving the value to the Keychain is the fix it points you to.

    What to take away

    • Store tokens, passwords, and keys in the Keychain, which is encrypted, access-controlled, system-managed secure storage.
    • Never store secrets in UserDefaults, which writes plain, unencrypted values readable from a backup or a compromised device.
    • Use UserDefaults only for non-sensitive preferences, and choose an appropriate Keychain accessibility level, device-only where suitable.
    • Use a pre-submission scan such as PTKD.com to catch any secret left in UserDefaults or a plist, then move it to the Keychain.
    • #ios
    • #keychain
    • #userdefaults
    • #secure-storage
    • #tokens
    • #owasp-masvs
    • #app-security

    Frequently asked questions

    Can I store an auth token in UserDefaults?
    No. UserDefaults persists data to a plain, unencrypted property list in your app's container, so a token there can be read from a device backup or by an attacker on a compromised device. It has no encryption and no access control. Tokens, passwords, and keys belong in the Keychain, which is encrypted and access-controlled. Use UserDefaults only for non-sensitive preferences like a theme setting or an onboarding flag.
    Why is the Keychain better than UserDefaults for secrets?
    Because it is purpose-built secure storage. The Keychain encrypts the items you store, manages keys with hardware backing on supported devices, and gates access with accessibility attributes that control when an item can be read, such as only while the device is in use and not locked. It is sized for small secrets like tokens and keys, persists across launches, and can be restricted to the device and excluded from backups. UserDefaults provides none of that.
    Is UserDefaults ever okay to use?
    Yes, for non-sensitive data. UserDefaults is a fine, convenient store for preferences and settings you would not mind anyone reading, like display options, feature flags, or an onboarding-completed marker. The rule is simply that it must never hold secrets, since it stores values in the clear. So use UserDefaults for ordinary preferences and the Keychain for anything confidential, and the convenience of UserDefaults stops being a liability.
    Which Keychain accessibility level should I use?
    Prefer one that requires the device to be in use rather than locked, rather than an always-accessible level, so a secret is not readable while the device is locked. For items that should not move to a new device through a backup or transfer, use a device-only accessibility option. Avoid the most permissive settings, which weaken the protection. The accessibility attribute is part of what makes the Keychain secure, so choose it deliberately for each item.
    How do I find secrets stored insecurely in my app?
    Scan the build. A pre-submission scan such as PTKD.com reads the compiled IPA against OWASP MASVS and flags sensitive data stored insecurely, such as a token in UserDefaults or a property list, so you can confirm secrets actually go to the Keychain before you ship. If it finds a token in plain storage, the fix is to move it to the Keychain with an appropriate accessibility level, closing the storage vulnerability.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free