almasumdev/awesome-mobile-security-agent-skills

privacy-by-design

Privacy-by-design principles on mobile — data minimization, purpose binding, consent.

View source
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

Privacy by Design

Instructions

Privacy by Design is an engineering posture, not a legal afterthought. Decisions made in the first week of a feature outweigh months of compliance paperwork.

1. The Three Questions

Before adding any data collection, answer in the PR description:

  1. What exactly are we collecting (field list, types, precision)?
  2. Why — which user-facing feature does it enable?
  3. For how long — retention and deletion policy.

If you can't answer cleanly, don't add it.

2. Data Minimization

Collect the least-precise data that still makes the feature work:

  • Location: need it only for "find nearest store"? Use CLLocationManager at reduced accuracy (desiredAccuracy = kCLLocationAccuracyKilometer) or Android's PRIORITY_BALANCED_POWER_ACCURACY. Consider iOS 14+ approximate location (requestWhenInUseAuthorization + user choice).
  • Contacts: need a phone number only to check membership? Hash client-side, ping server with prefix-truncated hashes (k-anonymity) — don't upload the full contact book.
  • Photos: use the PhotoPicker (PHPickerViewController on iOS, ActivityResultContracts.PickVisualMedia on Android) — you get the chosen asset without full library access.
swift
var config = PHPickerConfiguration(photoLibrary: .shared())
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
// No Photos permission prompt required for this path.
kotlin
val launcher = registerForActivityResult(
    ActivityResultContracts.PickVisualMedia()
) { uri -> /* process one image; no READ_MEDIA_IMAGES needed */ }
launcher.launch(
    PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
)

3. Purpose Binding

  • Each data field has one documented purpose.
  • Reuse for a new purpose requires a new consent event, not a silent expansion.
  • Analytics events that "might be useful later" are a red flag; collect when you have a concrete use.

4. On-Device Over Off-Device

  • Perform processing on-device when feasible (ML classification, thumbnailing, redaction).
  • Ship only derived / aggregate signals off-device.
  • Apple's App Store policy, Google's Play Data Safety, and GDPR all reward this architecturally.

5. Identifiers

IdentifierUse with caution
IDFA / GAIDAdvertising only; needs ATT on iOS, UMP consent on EEA Android.
ASIdentifierManager / advertising IDSame as above.
UIDevice.identifierForVendor / Settings.Secure.ANDROID_IDScoped identifiers; still PII under GDPR.
Server-issued user IDPrefer this for analytics / crash correlation.
IMEI / MAC / serialDo not use on modern OSes; restricted and often unavailable.

Use the weakest identifier that works. Generate a random install ID scoped to your app (UUID.randomUUID() stored in Keychain / Keystore) for things like A/B bucketing.

6. Third-Party SDKs

Each SDK added is a data-sharing decision:

  • Enumerate what it sends (many SDKs send IP, install ID, app events by default).
  • Add to Apple's Privacy Manifest (PrivacyInfo.xcprivacy) and Google Play's Data Safety form.
  • Prefer SDKs that support delayed initialization until after consent.
  • Audit at least yearly — SDKs expand scope between releases.

7. Logs, Crash Reports, Analytics

These are the three places PII leaks most often.

  • Never log raw request / response bodies with user data in release builds.
  • Strip PII from crash reports (stack traces are fine; user input in a toString() is not).
  • For analytics, log categories ("filter_applied") not content ("searched for: alice@example.com").
kotlin
// Don't
Timber.d("User profile: $profile")
// Do
Timber.d("Loaded profile uid=${profile.id.hashPrefix(8)}")

8. Default Settings

  • Telemetry / crash reporting: opt-in in regulated regions, clearly disclosed elsewhere.
  • Personalization / recommendations that rely on behavior: opt-in.
  • Background location, microphone, camera: never "always" by default.

9. Data Export and Deletion

Shipping a feature → shipping an export path and a delete path. Build them alongside, not after launch. See gdpr-mobile.

Checklist

  • [ ] Every new field has a documented purpose and retention period.
  • [ ] Location / photos / contacts use the least-privileged API (reduced accuracy, PhotoPicker, hashed lookup).
  • [ ] No feature reuses data for a new purpose without fresh consent.
  • [ ] Processing happens on-device where feasible.
  • [ ] Identifiers used are the weakest that work; no deprecated hardware identifiers.
  • [ ] Every third-party SDK is entered in the iOS Privacy Manifest and Play Data Safety form.
  • [ ] Logs, crash reports, and analytics do not carry raw PII.
  • [ ] Export and delete paths ship with the feature, not later.
from this repository

More skills

All skills