Skip to content

clerk_preferences.xml stores DEVICE_TOKEN and DEVICE_ID in plaintext — not removed by PR #571 #581

@mkandalaf-amipass

Description

@mkandalaf-amipass

When integrating the Clerk Android SDK, the file clerk_preferences.xml is created in the app's internal storage with sensitive data stored in cleartext, including a DEVICE_TOKEN (containing a client_id and rotating_token) and a DEVICE_ID.

This was identified during a third-party mobile security assessment (PCI DSS penetration test) and classified as CWE-312: Cleartext Storage of Sensitive Information.

Example content found on device:

  <!-- /data/data/<package>/shared_prefs/clerk_preferences.xml -->
  <string name="DEVICE_TOKEN">eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...</string>
  <string name="DEVICE_ID">69bb8dd4-9ad4-41da-98a3-7f24bbd16cc3</string>

Expected behavior

Sensitive data managed by the SDK — including any device identifiers and tokens — should be encrypted at rest using Android's EncryptedSharedPreferences (backed by Android Keystore, AES-256-GCM), consistent with Android security best practices and PCI DSS requirements.


Actual behavior

clerk_preferences.xml stores DEVICE_TOKEN and DEVICE_ID in plaintext XML. Any actor with access to the device filesystem (physical access, rooted device, or elevated ADB privileges) can read and potentially reuse these values.

Note on PR #571
We noticed that PR #571 (refactor(api): remove unused Android device attestation startup flow and middleware) was merged in v1.0.11 and removed DeviceAssertionMiddleware.kt and FraudSettings.kt. We verified the compiled output of both v1.0.10 and v1.0.11 and confirmed the following classes and string constants are still present in v1.0.11:

  • com/clerk/api/attestation/DeviceAttestationHelper
  • com/clerk/api/network/api/DeviceAttestationApi
  • CLERK_PREFERENCES_FILE_NAME
  • DEVICE_TOKEN_UPDATE
  • clerk_preferences

The middleware that consumed the token was removed, but DeviceAttestationHelper — the class responsible for writing to clerk_preferences.xml — was not. The plaintext storage issue persists in v1.0.11.


Environment

  • Platform: Android
  • Clerk SDK version: v1.0.10 and v1.0.11 (both affected)
  • Package: com.clerk:clerk-android-api
  • Device: PAX 920Pro (Android POS terminal) — also reproducible on standard Android emulator
  • Assessment standard: PCI DSS v4.0 / OWASP MASVS

Steps to reproduce

  1. Integrate the Clerk Android SDK in any app
  2. Authenticate a user (valid session)
  3. Access the device file system (rooted device or emulator with elevated privileges)
  4. Navigate to /data/data//shared_prefs/
  5. Open clerk_preferences.xml
  6. Observe DEVICE_TOKEN and DEVICE_ID stored in plaintext

Questions / Suggestions

Suggested remediation
Replace SharedPreferences with EncryptedSharedPreferences for clerk_preferences.xml:

val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val prefs = EncryptedSharedPreferences.create(
    context,
    "clerk_preferences",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

This would store the same data encrypted at rest, with the key protected by the Android Keystore hardware security module with no API surface changes for SDK consumers.


Impact

  • Identified as CWE-312 in a PCI DSS mobile penetration test
  • DEVICE_TOKEN could be used to attempt Clerk session restoration on a different device before the legitimate token rotates
  • Does not allow direct authentication against backend APIs (which require a full JWT), but represents a compliance violation under PCI DSS requirement 3.4 (sensitive data must be unreadable anywhere it is stored)
  • Affects any Android app using the Clerk SDK that operates in a regulated environment (fintech, payments, healthcare)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions