@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1265 @@
|
|
|
1
|
+
# Mobile Data Storage Security
|
|
2
|
+
|
|
3
|
+
> Expertise module for AI agents implementing secure local data storage on iOS, Android, and cross-platform mobile applications.
|
|
4
|
+
|
|
5
|
+
**OWASP References:** MASVS-STORAGE, Mobile Top 10 2024 M9 (Insecure Data Storage)
|
|
6
|
+
**Last Updated:** 2026-03-08
|
|
7
|
+
**Applies To:** iOS (Swift/ObjC), Android (Kotlin/Java), Flutter (Dart), React Native (JS/TS)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Threat Landscape
|
|
12
|
+
|
|
13
|
+
Mobile devices present a unique attack surface for data storage. Unlike server environments,
|
|
14
|
+
mobile devices are physically accessible, frequently lost or stolen, and run in environments
|
|
15
|
+
where other apps, malware, and forensic tools can access improperly stored data.
|
|
16
|
+
|
|
17
|
+
### 1.1 Primary Threat Vectors
|
|
18
|
+
|
|
19
|
+
| Threat | Description | Risk Level |
|
|
20
|
+
|--------|-------------|------------|
|
|
21
|
+
| Unencrypted local databases | SQLite databases stored as plaintext files readable by root/jailbreak | Critical |
|
|
22
|
+
| Plaintext credentials | Passwords, tokens, API keys stored in SharedPreferences/UserDefaults | Critical |
|
|
23
|
+
| Backup extraction | iTunes/iCloud/adb backups containing app data extractable on desktop | High |
|
|
24
|
+
| Forensic data recovery | Deleted data recoverable from flash storage via forensic tools | High |
|
|
25
|
+
| Screenshot/snapshot leakage | OS-generated app switcher snapshots capturing sensitive screens | Medium |
|
|
26
|
+
| Clipboard exposure | Sensitive data copied to system clipboard accessible by other apps | Medium |
|
|
27
|
+
| Log file exposure | Sensitive data written to system logs (NSLog, Logcat) | Medium |
|
|
28
|
+
| WebView cache/cookies | Authentication tokens persisted in WebView storage | Medium |
|
|
29
|
+
| SD card storage (Android) | External storage world-readable on older Android versions | High |
|
|
30
|
+
| Keyboard cache | Autocomplete dictionaries caching sensitive input | Low |
|
|
31
|
+
|
|
32
|
+
### 1.2 Real-World Incidents
|
|
33
|
+
|
|
34
|
+
**Strava Global Heatmap (2018, 2024)**
|
|
35
|
+
Strava published a heatmap visualizing fitness-tracking data from its global user base. In
|
|
36
|
+
January 2018, researchers discovered the heatmap exposed the locations of secret military
|
|
37
|
+
bases, including US forward operating bases in Syria and Afghanistan. In October 2024, Le
|
|
38
|
+
Monde revealed the app exposed movements of security teams protecting French President
|
|
39
|
+
Macron and US President Biden. The data was stored and aggregated from locally cached GPS
|
|
40
|
+
data that synced to Strava servers.
|
|
41
|
+
Source: https://en.wikipedia.org/wiki/Strava
|
|
42
|
+
|
|
43
|
+
**Uber Data Breaches (2022-2023)**
|
|
44
|
+
Uber suffered multiple breaches where driver and employee data was exfiltrated. In September
|
|
45
|
+
2022, an attacker compromised an employee's credentials and accessed internal systems. In
|
|
46
|
+
December 2022, data from 77,000 Uber employees surfaced on breach forums. In April 2023,
|
|
47
|
+
driver Social Security numbers and Tax IDs were stolen via a third-party law firm breach.
|
|
48
|
+
Hardcoded credentials and excessive local data retention contributed to the attack surface.
|
|
49
|
+
Source: https://firewalltimes.com/uber-data-breach-timeline/
|
|
50
|
+
|
|
51
|
+
**SK Telecom SIM Data Breach (2025)**
|
|
52
|
+
Attackers deployed a BPFDoor RAT on 28 internal servers, exfiltrating SIM management data,
|
|
53
|
+
IMSI numbers, and authentication keys for 27 million subscribers. The breach resulted in a
|
|
54
|
+
US$96.9 million fine. Mobile identity data stored without adequate encryption or access
|
|
55
|
+
controls was a contributing factor.
|
|
56
|
+
Source: https://www.pkware.com/blog/recent-data-breaches
|
|
57
|
+
|
|
58
|
+
**Catwatchful Spyware Data Leak (2025)**
|
|
59
|
+
An Android spyware app marketed as "child monitoring" leaked data from 26,000 victim
|
|
60
|
+
devices, including the email addresses and passwords of customers who purchased the app.
|
|
61
|
+
The locally collected data was stored without proper encryption on the backend.
|
|
62
|
+
Source: https://www.eff.org/deeplinks/2025/12/breachies-2025-worst-weirdest-most-impactful-data-breaches-year
|
|
63
|
+
|
|
64
|
+
**Industry-Wide: 76% of Mobile Apps Have Insecure Data Storage**
|
|
65
|
+
Research cited by OWASP and multiple security vendors indicates that as many as 76% of
|
|
66
|
+
mobile applications have insecure data storage vulnerabilities, making it one of the most
|
|
67
|
+
pervasive mobile security issues despite dropping from M2 to M9 in the 2024 OWASP
|
|
68
|
+
Mobile Top 10 ranking.
|
|
69
|
+
Source: https://approov.io/blog/2024-owasp-mobile-top-ten-risks
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 2. Core Security Principles
|
|
74
|
+
|
|
75
|
+
### 2.1 Encrypt Sensitive Data at Rest
|
|
76
|
+
|
|
77
|
+
All sensitive data persisted on the device MUST be encrypted. This includes databases, files,
|
|
78
|
+
preferences, and cached content. Never rely on OS-level full-disk encryption alone -- use
|
|
79
|
+
application-layer encryption for defense in depth.
|
|
80
|
+
|
|
81
|
+
**What counts as sensitive data:**
|
|
82
|
+
- Authentication tokens, session IDs, refresh tokens
|
|
83
|
+
- User credentials (passwords, PINs, biometric templates)
|
|
84
|
+
- Personal Identifiable Information (PII): names, emails, phone numbers, SSNs
|
|
85
|
+
- Financial data: card numbers, account details, transaction history
|
|
86
|
+
- Health data (PHI under HIPAA)
|
|
87
|
+
- Cryptographic keys and certificates
|
|
88
|
+
- Location data and movement patterns
|
|
89
|
+
|
|
90
|
+
### 2.2 Use Platform Secure Storage
|
|
91
|
+
|
|
92
|
+
Always prefer platform-provided secure storage mechanisms:
|
|
93
|
+
- **iOS:** Keychain Services with appropriate `kSecAttrAccessible` protection class
|
|
94
|
+
- **Android:** Android Keystore for keys, EncryptedSharedPreferences for small values
|
|
95
|
+
- **Cross-platform:** flutter_secure_storage, react-native-keychain
|
|
96
|
+
|
|
97
|
+
These systems provide hardware-backed security (Secure Enclave on iOS, TEE/StrongBox on
|
|
98
|
+
Android) that makes key extraction significantly harder than software-only solutions.
|
|
99
|
+
|
|
100
|
+
### 2.3 Minimize Local Data
|
|
101
|
+
|
|
102
|
+
Store only what is strictly necessary on the device. Prefer server-side storage for sensitive
|
|
103
|
+
data and fetch on demand. When local storage is required, enforce data retention policies
|
|
104
|
+
and automatic expiration.
|
|
105
|
+
|
|
106
|
+
### 2.4 Clear Sensitive Data on Logout
|
|
107
|
+
|
|
108
|
+
Implement secure logout that zeroes out all sensitive data from memory and persistent
|
|
109
|
+
storage. This includes tokens, cached PII, session data, cookies, and WebView state.
|
|
110
|
+
|
|
111
|
+
### 2.5 Disable Backups for Sensitive Data
|
|
112
|
+
|
|
113
|
+
Configure apps to exclude sensitive files from iTunes/iCloud backups (iOS) and adb backups
|
|
114
|
+
(Android). Backup extraction is a well-documented attack vector for credential theft.
|
|
115
|
+
|
|
116
|
+
### 2.6 Protect Against Screenshot Capture
|
|
117
|
+
|
|
118
|
+
Prevent the OS from capturing screenshots of sensitive screens in the app switcher. On
|
|
119
|
+
Android, use `FLAG_SECURE`. On iOS, overlay sensitive views during `applicationWillResignActive`.
|
|
120
|
+
|
|
121
|
+
### 2.7 Defense in Depth
|
|
122
|
+
|
|
123
|
+
Layer multiple protections. Even if one control fails (e.g., an attacker bypasses file
|
|
124
|
+
encryption), other controls (access restrictions, Keystore-backed keys, backup exclusion)
|
|
125
|
+
should limit the blast radius.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 3. Implementation Patterns
|
|
130
|
+
|
|
131
|
+
### 3.1 iOS: Keychain Services
|
|
132
|
+
|
|
133
|
+
The iOS Keychain is an encrypted, OS-managed credential store backed by the Secure Enclave.
|
|
134
|
+
|
|
135
|
+
**Key attributes:**
|
|
136
|
+
- `kSecAttrService` -- identifies your app's service
|
|
137
|
+
- `kSecAttrAccount` -- identifies the specific credential
|
|
138
|
+
- `kSecAttrAccessible` -- defines when the item is accessible (protection class)
|
|
139
|
+
|
|
140
|
+
**Protection classes (kSecAttrAccessible):**
|
|
141
|
+
|
|
142
|
+
| Constant | Availability | Use Case |
|
|
143
|
+
|----------|-------------|----------|
|
|
144
|
+
| `kSecAttrAccessibleWhenUnlocked` | Only when device unlocked | Default for most data |
|
|
145
|
+
| `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` | Unlocked, no migration | High-security tokens |
|
|
146
|
+
| `kSecAttrAccessibleAfterFirstUnlock` | After first unlock until reboot | Background-accessible data |
|
|
147
|
+
| `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` | After first unlock, no migration | Background + non-migratable |
|
|
148
|
+
| `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` | Only with passcode set | Highest security items |
|
|
149
|
+
|
|
150
|
+
**Keychain Access Groups:**
|
|
151
|
+
Apps from the same developer team can share Keychain items via access groups configured
|
|
152
|
+
in the app's entitlements. Use `kSecAttrAccessGroup` to scope items. Items without an
|
|
153
|
+
explicit group default to the app's own bundle identifier group.
|
|
154
|
+
|
|
155
|
+
### 3.2 iOS: Data Protection API
|
|
156
|
+
|
|
157
|
+
iOS provides file-level encryption via Data Protection classes set on files and directories:
|
|
158
|
+
|
|
159
|
+
| Class | Constant | Behavior |
|
|
160
|
+
|-------|----------|----------|
|
|
161
|
+
| Complete Protection | `NSFileProtectionComplete` | Encrypted when locked; key wiped from memory |
|
|
162
|
+
| Complete Until First Auth | `NSFileProtectionCompleteUntilFirstUserAuthentication` | Available after first unlock |
|
|
163
|
+
| Complete Unless Open | `NSFileProtectionCompleteUnlessOpen` | Existing handles remain valid when locked |
|
|
164
|
+
| No Protection | `NSFileProtectionNone` | Always accessible (avoid for sensitive data) |
|
|
165
|
+
|
|
166
|
+
Set via `FileManager` attributes or `URLResourceValues`.
|
|
167
|
+
|
|
168
|
+
### 3.3 iOS: CoreData Encryption
|
|
169
|
+
|
|
170
|
+
CoreData does not natively encrypt its SQLite store. Options:
|
|
171
|
+
1. Use SQLCipher as the backing store via a custom `NSPersistentStoreCoordinator`
|
|
172
|
+
2. Apply `NSFileProtectionComplete` to the store file
|
|
173
|
+
3. Encrypt individual sensitive fields before storing
|
|
174
|
+
4. Use Apple's encrypted CoreData store (available in newer iOS versions with Data Protection)
|
|
175
|
+
|
|
176
|
+
### 3.4 Android: Keystore System
|
|
177
|
+
|
|
178
|
+
The Android Keystore provides hardware-backed key storage (TEE or StrongBox Secure Element).
|
|
179
|
+
|
|
180
|
+
**Key features:**
|
|
181
|
+
- Keys never leave the secure hardware
|
|
182
|
+
- Supports RSA, AES, HMAC, EC algorithms
|
|
183
|
+
- Biometric binding via `setUserAuthenticationRequired(true)`
|
|
184
|
+
- Key validity periods via `setKeyValidityStart()` / `setKeyValidityEnd()`
|
|
185
|
+
- Hardware attestation to verify keys are hardware-backed
|
|
186
|
+
|
|
187
|
+
**Verify hardware backing:**
|
|
188
|
+
```kotlin
|
|
189
|
+
val keyInfo = keyFactory.getKeySpec(key, KeyInfo::class.java)
|
|
190
|
+
val isHardwareBacked = keyInfo.isInsideSecureHardware
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 3.5 Android: EncryptedSharedPreferences
|
|
194
|
+
|
|
195
|
+
**Status (2025):** The original `androidx.security:security-crypto` library was deprecated at
|
|
196
|
+
version 1.1.0-alpha07. A community-maintained fork at
|
|
197
|
+
`io.github.nicholasgasior.android:encrypted-shared-preferences` continues development. The
|
|
198
|
+
API remains functional and is still widely used; the deprecation means no further official
|
|
199
|
+
patches from Google.
|
|
200
|
+
|
|
201
|
+
**Encryption details:**
|
|
202
|
+
- Key encryption: AES256-SIV (deterministic AEAD)
|
|
203
|
+
- Value encryption: AES256-GCM (authenticated encryption)
|
|
204
|
+
- Master key stored in Android Keystore
|
|
205
|
+
- Uses Google Tink cryptographic library internally
|
|
206
|
+
|
|
207
|
+
### 3.6 Android: EncryptedFile
|
|
208
|
+
|
|
209
|
+
For encrypting entire files on Android:
|
|
210
|
+
```kotlin
|
|
211
|
+
val encryptedFile = EncryptedFile.Builder(
|
|
212
|
+
context,
|
|
213
|
+
file,
|
|
214
|
+
masterKey,
|
|
215
|
+
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
|
|
216
|
+
).build()
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 3.7 Cross-Platform: Flutter (flutter_secure_storage)
|
|
220
|
+
|
|
221
|
+
Uses Keychain on iOS and EncryptedSharedPreferences (with Tink) on Android. Provides a
|
|
222
|
+
simple key-value API. On iOS, configure accessibility via `IOSOptions`:
|
|
223
|
+
|
|
224
|
+
```dart
|
|
225
|
+
final storage = FlutterSecureStorage();
|
|
226
|
+
// Set iOS accessibility
|
|
227
|
+
const iOSOptions = IOSOptions(
|
|
228
|
+
accessibility: KeychainAccessibility.first_unlock_this_device,
|
|
229
|
+
);
|
|
230
|
+
await storage.write(key: 'token', value: jwt, iOptions: iOSOptions);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Important:** As of 2025, flutter_secure_storage on Android depends on the deprecated
|
|
234
|
+
`androidx.security:security-crypto`. Monitor for migration to the community fork or
|
|
235
|
+
alternative implementations.
|
|
236
|
+
|
|
237
|
+
### 3.8 Cross-Platform: React Native (react-native-keychain)
|
|
238
|
+
|
|
239
|
+
Wraps iOS Keychain and Android Keystore/EncryptedSharedPreferences:
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
import * as Keychain from 'react-native-keychain';
|
|
243
|
+
|
|
244
|
+
// Store credentials
|
|
245
|
+
await Keychain.setGenericPassword('username', 'password', {
|
|
246
|
+
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
|
|
247
|
+
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Retrieve credentials
|
|
251
|
+
const credentials = await Keychain.getGenericPassword();
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Alternative:** `react-native-encrypted-storage` provides a simpler key-value interface
|
|
255
|
+
backed by Keychain (iOS) and EncryptedSharedPreferences (Android).
|
|
256
|
+
|
|
257
|
+
**Critical rule:** Never use AsyncStorage for sensitive data. It provides zero encryption or
|
|
258
|
+
access control.
|
|
259
|
+
|
|
260
|
+
### 3.9 Secure Caching Strategies
|
|
261
|
+
|
|
262
|
+
- Cache sensitive data in memory only; never write to disk cache
|
|
263
|
+
- Use `URLCache` policies to prevent HTTP response caching for sensitive endpoints
|
|
264
|
+
- Clear `WKWebView` / `WebView` data stores on logout
|
|
265
|
+
- Set `Cache-Control: no-store` headers for sensitive API responses
|
|
266
|
+
- Disable autocomplete on sensitive text fields (`textContentType = .none` on iOS,
|
|
267
|
+
`android:importantForAutofill="no"` on Android)
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 4. Vulnerability Catalog
|
|
272
|
+
|
|
273
|
+
### V01: Credentials in SharedPreferences / UserDefaults
|
|
274
|
+
|
|
275
|
+
**Risk:** Critical | **MASVS:** MASVS-STORAGE-1
|
|
276
|
+
|
|
277
|
+
Storing passwords, API keys, or tokens in plaintext SharedPreferences (Android) or
|
|
278
|
+
UserDefaults (iOS). These are XML/plist files readable on rooted/jailbroken devices or via
|
|
279
|
+
backup extraction.
|
|
280
|
+
|
|
281
|
+
**Detection:** Search for `getSharedPreferences()`, `UserDefaults.standard.set()` with
|
|
282
|
+
sensitive keys. MobSF flags this automatically in static analysis.
|
|
283
|
+
|
|
284
|
+
**Fix:** Use EncryptedSharedPreferences (Android) or Keychain (iOS).
|
|
285
|
+
|
|
286
|
+
### V02: SQLite Database Without Encryption
|
|
287
|
+
|
|
288
|
+
**Risk:** Critical | **MASVS:** MASVS-STORAGE-1
|
|
289
|
+
|
|
290
|
+
SQLite databases stored as plaintext `.db` files in the app sandbox. Extractable via backup,
|
|
291
|
+
root access, or forensic tools.
|
|
292
|
+
|
|
293
|
+
**Detection:** Check for `.db` files in app data directory. Open with `sqlite3` to verify no
|
|
294
|
+
encryption. Frida script: hook `sqlite3_open()` to log database paths.
|
|
295
|
+
|
|
296
|
+
**Fix:** Use SQLCipher with 256-bit AES. Store the encryption key in Keychain/Keystore.
|
|
297
|
+
|
|
298
|
+
### V03: Cache Files Containing Sensitive Data
|
|
299
|
+
|
|
300
|
+
**Risk:** High | **MASVS:** MASVS-STORAGE-1
|
|
301
|
+
|
|
302
|
+
HTTP response caches, image caches, and WebView caches storing sensitive data in the
|
|
303
|
+
app's cache directory. Persists even after user logs out.
|
|
304
|
+
|
|
305
|
+
**Detection:** Inspect `Library/Caches/` (iOS) and `cache/` directory (Android) after using
|
|
306
|
+
the app. Look for response bodies containing tokens, PII, or session data.
|
|
307
|
+
|
|
308
|
+
**Fix:** Set `Cache-Control: no-store` for sensitive endpoints. Clear caches on logout. Use
|
|
309
|
+
ephemeral URL sessions for sensitive requests.
|
|
310
|
+
|
|
311
|
+
### V04: Backup Including Credentials
|
|
312
|
+
|
|
313
|
+
**Risk:** High | **MASVS:** MASVS-STORAGE-2
|
|
314
|
+
|
|
315
|
+
App data included in iTunes/iCloud (iOS) or adb (Android) backups. Attackers with physical
|
|
316
|
+
access or paired-device lockdown certificates can extract full app sandboxes.
|
|
317
|
+
|
|
318
|
+
**Detection:** Create a backup and inspect contents. Use `iExplorer` (iOS) or Android Backup
|
|
319
|
+
Extractor to inspect `.ab` files.
|
|
320
|
+
|
|
321
|
+
**Fix:** iOS: Set `isExcludedFromBackup = true` on sensitive file URLs. Android: Set
|
|
322
|
+
`android:allowBackup="false"` in `AndroidManifest.xml` or use `android:fullBackupContent`
|
|
323
|
+
to exclude specific paths.
|
|
324
|
+
|
|
325
|
+
### V05: Screenshot Exposure in App Switcher
|
|
326
|
+
|
|
327
|
+
**Risk:** Medium | **MASVS:** MASVS-STORAGE-2
|
|
328
|
+
|
|
329
|
+
OS captures a screenshot of the current screen when the app enters background. Visible in
|
|
330
|
+
the app switcher and stored as a file. May capture passwords, financial data, medical info.
|
|
331
|
+
|
|
332
|
+
**Detection:** Open a sensitive screen, press Home, check recent apps. On iOS, inspect
|
|
333
|
+
`Library/SplashBoard/Snapshots/`. On Android, check task thumbnails.
|
|
334
|
+
|
|
335
|
+
**Fix:** Android: `window.setFlags(FLAG_SECURE, FLAG_SECURE)`. iOS: overlay a blur or
|
|
336
|
+
splash view in `applicationWillResignActive()` / `sceneWillResignActive()`.
|
|
337
|
+
|
|
338
|
+
### V06: Pasteboard / Clipboard Leaks
|
|
339
|
+
|
|
340
|
+
**Risk:** Medium | **MASVS:** MASVS-STORAGE-1
|
|
341
|
+
|
|
342
|
+
Sensitive data copied to the system clipboard is accessible to all apps. On iOS 14+, a
|
|
343
|
+
notification appears when another app reads the clipboard, but the data is still exposed.
|
|
344
|
+
|
|
345
|
+
**Detection:** Copy sensitive data from the app, then use a clipboard viewer app to confirm
|
|
346
|
+
accessibility.
|
|
347
|
+
|
|
348
|
+
**Fix:** Use `UIPasteboard.general.setItems(_, options: [.expirationDate: Date(...)])` on
|
|
349
|
+
iOS to set expiration. On Android, use `ClipboardManager` with auto-clear. Disable copy on
|
|
350
|
+
sensitive fields where possible.
|
|
351
|
+
|
|
352
|
+
### V07: Sensitive Data in Logs
|
|
353
|
+
|
|
354
|
+
**Risk:** Medium | **MASVS:** MASVS-STORAGE-1
|
|
355
|
+
|
|
356
|
+
Using `NSLog`, `print()`, `Log.d()`, or `debugPrint()` with sensitive values. System logs
|
|
357
|
+
are accessible via Xcode console, `adb logcat`, or crash reporting tools.
|
|
358
|
+
|
|
359
|
+
**Detection:** Search codebase for logging statements. Run `adb logcat` while using the app
|
|
360
|
+
and grep for tokens, passwords, PII. MobSF flags common patterns.
|
|
361
|
+
|
|
362
|
+
**Fix:** Use a logging framework with severity levels. Strip sensitive logs in release builds.
|
|
363
|
+
Never log tokens, passwords, or PII. Use `os_log` with `.private` redaction on iOS.
|
|
364
|
+
|
|
365
|
+
### V08: WebView Cache and Cookies
|
|
366
|
+
|
|
367
|
+
**Risk:** Medium | **MASVS:** MASVS-STORAGE-1
|
|
368
|
+
|
|
369
|
+
`WKWebView` (iOS) and `WebView` (Android) cache HTML, cookies, and `localStorage` in the
|
|
370
|
+
app sandbox. Authentication tokens in cookies persist after logout.
|
|
371
|
+
|
|
372
|
+
**Detection:** Inspect WebView cache directories after authentication. Check for
|
|
373
|
+
`Cookies.binarycookies` (iOS) or `WebView/` directory (Android).
|
|
374
|
+
|
|
375
|
+
**Fix:** Clear WebView data on logout. Use `WKWebsiteDataStore.nonPersistent()` for
|
|
376
|
+
sensitive sessions. Set `CookieManager.removeAllCookies()` on Android.
|
|
377
|
+
|
|
378
|
+
### V09: External Storage Exposure (Android)
|
|
379
|
+
|
|
380
|
+
**Risk:** High | **MASVS:** MASVS-STORAGE-1
|
|
381
|
+
|
|
382
|
+
Files written to external storage (`/sdcard/`) are world-readable on Android < 10. Even
|
|
383
|
+
with scoped storage (Android 10+), other apps with `READ_EXTERNAL_STORAGE` can access files.
|
|
384
|
+
|
|
385
|
+
**Detection:** Check for `getExternalStorageDirectory()`, `getExternalFilesDir()` usage
|
|
386
|
+
with sensitive data.
|
|
387
|
+
|
|
388
|
+
**Fix:** Always use internal storage (`getFilesDir()`, `getCacheDir()`) for sensitive data.
|
|
389
|
+
If external storage is required, encrypt files before writing.
|
|
390
|
+
|
|
391
|
+
### V10: Hardcoded Secrets in Application Binary
|
|
392
|
+
|
|
393
|
+
**Risk:** Critical | **MASVS:** MASVS-STORAGE-1
|
|
394
|
+
|
|
395
|
+
API keys, encryption keys, passwords, or signing secrets embedded directly in source code.
|
|
396
|
+
Extractable via reverse engineering (strings, class-dump, jadx).
|
|
397
|
+
|
|
398
|
+
**Detection:** Run `strings` on the binary. Use MobSF or Semgrep to scan for hardcoded
|
|
399
|
+
patterns. Decompile with jadx (Android) or class-dump (iOS).
|
|
400
|
+
|
|
401
|
+
**Fix:** Fetch secrets from secure backend at runtime. Use platform Keystore for
|
|
402
|
+
locally-generated keys. For API keys that must ship with the app, use obfuscation as a
|
|
403
|
+
secondary measure (not a substitute for server-side controls).
|
|
404
|
+
|
|
405
|
+
### V11: Insecure Key Storage
|
|
406
|
+
|
|
407
|
+
**Risk:** Critical | **MASVS:** MASVS-CRYPTO-1
|
|
408
|
+
|
|
409
|
+
Encryption keys stored alongside encrypted data, or in SharedPreferences/UserDefaults.
|
|
410
|
+
Defeats the purpose of encryption entirely.
|
|
411
|
+
|
|
412
|
+
**Detection:** Trace the key used for encryption. If it is in the same directory, in
|
|
413
|
+
preferences, or hardcoded, the encryption is worthless.
|
|
414
|
+
|
|
415
|
+
**Fix:** Store encryption keys exclusively in iOS Keychain or Android Keystore. Derive
|
|
416
|
+
keys using PBKDF2 with user input (password) when appropriate.
|
|
417
|
+
|
|
418
|
+
### V12: Residual Data After Deletion
|
|
419
|
+
|
|
420
|
+
**Risk:** Medium | **MASVS:** MASVS-STORAGE-1
|
|
421
|
+
|
|
422
|
+
Calling `delete()` or `removeItem()` does not zero out storage sectors. Data remains
|
|
423
|
+
recoverable via forensic tools until overwritten.
|
|
424
|
+
|
|
425
|
+
**Detection:** Delete sensitive data, then use forensic tools (Cellebrite, Oxygen) to
|
|
426
|
+
attempt recovery from a device image.
|
|
427
|
+
|
|
428
|
+
**Fix:** Overwrite data before deletion when feasible. Use encrypted storage so deleted
|
|
429
|
+
encrypted data is unrecoverable without the key. Destroy the encryption key on logout.
|
|
430
|
+
|
|
431
|
+
### V13: Keyboard Cache / Autocomplete Dictionary
|
|
432
|
+
|
|
433
|
+
**Risk:** Low | **MASVS:** MASVS-STORAGE-1
|
|
434
|
+
|
|
435
|
+
Custom keyboard dictionaries learn and cache user input, including passwords and sensitive
|
|
436
|
+
data entered in text fields.
|
|
437
|
+
|
|
438
|
+
**Detection:** Type sensitive data in text fields, then check if suggestions appear in
|
|
439
|
+
other apps.
|
|
440
|
+
|
|
441
|
+
**Fix:** Set `autocorrectionType = .no` and `textContentType = .none` on iOS. Set
|
|
442
|
+
`android:inputType="textNoSuggestions"` on Android for sensitive fields.
|
|
443
|
+
|
|
444
|
+
### V14: Temporary Files with Sensitive Data
|
|
445
|
+
|
|
446
|
+
**Risk:** Medium | **MASVS:** MASVS-STORAGE-1
|
|
447
|
+
|
|
448
|
+
Temp files created during processing (PDF generation, image manipulation, data export) may
|
|
449
|
+
contain sensitive data and persist after use.
|
|
450
|
+
|
|
451
|
+
**Detection:** Monitor the tmp directory during and after app operations involving
|
|
452
|
+
sensitive data.
|
|
453
|
+
|
|
454
|
+
**Fix:** Use secure temp file creation. Delete immediately after use. Apply file protection
|
|
455
|
+
attributes. Prefer in-memory processing where possible.
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## 5. Security Checklist
|
|
460
|
+
|
|
461
|
+
### Data Classification and Storage
|
|
462
|
+
|
|
463
|
+
- [ ] All sensitive data types identified and classified (tokens, PII, PHI, financial)
|
|
464
|
+
- [ ] Sensitive data encrypted at rest using AES-256 or platform-equivalent
|
|
465
|
+
- [ ] Encryption keys stored in hardware-backed secure storage (Keychain/Keystore)
|
|
466
|
+
- [ ] No credentials stored in SharedPreferences, UserDefaults, or plist files (plaintext)
|
|
467
|
+
- [ ] No sensitive data stored on external storage (Android SD card)
|
|
468
|
+
- [ ] Database encryption enabled (SQLCipher or equivalent) for databases containing PII
|
|
469
|
+
|
|
470
|
+
### Platform Secure Storage
|
|
471
|
+
|
|
472
|
+
- [ ] iOS Keychain used for credentials with appropriate `kSecAttrAccessible` class
|
|
473
|
+
- [ ] Android Keystore used for cryptographic keys with hardware backing verified
|
|
474
|
+
- [ ] EncryptedSharedPreferences (or successor) used for encrypted key-value storage
|
|
475
|
+
- [ ] Keychain/Keystore items scoped to `ThisDeviceOnly` where migration is not needed
|
|
476
|
+
- [ ] Cross-platform: flutter_secure_storage / react-native-keychain configured correctly
|
|
477
|
+
|
|
478
|
+
### Backup and Export Protection
|
|
479
|
+
|
|
480
|
+
- [ ] Sensitive files excluded from iCloud/iTunes backup (`isExcludedFromBackup`)
|
|
481
|
+
- [ ] Android `android:allowBackup="false"` or `fullBackupContent` excludes sensitive paths
|
|
482
|
+
- [ ] No sensitive data in auto-generated backups verified by extraction test
|
|
483
|
+
|
|
484
|
+
### Screen and UI Protection
|
|
485
|
+
|
|
486
|
+
- [ ] `FLAG_SECURE` set on Android activities displaying sensitive data
|
|
487
|
+
- [ ] iOS app switcher snapshot obscured via blur/overlay in lifecycle callbacks
|
|
488
|
+
- [ ] Sensitive text fields disable autocomplete and autocorrection
|
|
489
|
+
- [ ] Copy/paste disabled or time-limited on sensitive fields
|
|
490
|
+
- [ ] Secure text entry (`isSecureTextEntry`) used for password fields
|
|
491
|
+
|
|
492
|
+
### Session and Lifecycle
|
|
493
|
+
|
|
494
|
+
- [ ] All sensitive data cleared from memory and storage on logout
|
|
495
|
+
- [ ] WebView cookies, cache, and localStorage cleared on logout
|
|
496
|
+
- [ ] Session tokens have expiration and are refreshed securely
|
|
497
|
+
- [ ] Inactive sessions time out and trigger secure data cleanup
|
|
498
|
+
- [ ] Clipboard cleared after timeout when sensitive data is copied
|
|
499
|
+
|
|
500
|
+
### Logging and Debugging
|
|
501
|
+
|
|
502
|
+
- [ ] No sensitive data in log output (NSLog, Logcat, print, debugPrint)
|
|
503
|
+
- [ ] Debug logging disabled in release builds
|
|
504
|
+
- [ ] Crash reports do not contain PII or credentials
|
|
505
|
+
- [ ] Analytics events do not include sensitive field values
|
|
506
|
+
|
|
507
|
+
### Network Cache
|
|
508
|
+
|
|
509
|
+
- [ ] HTTP response caching disabled for sensitive API endpoints
|
|
510
|
+
- [ ] URL cache cleared on logout
|
|
511
|
+
- [ ] Ephemeral URL sessions used for sensitive requests where supported
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## 6. Tools and Automation
|
|
516
|
+
|
|
517
|
+
### 6.1 Static Analysis
|
|
518
|
+
|
|
519
|
+
**MobSF (Mobile Security Framework)**
|
|
520
|
+
- Open-source automated analysis for APK, IPA, and source code
|
|
521
|
+
- Detects hardcoded secrets, insecure storage patterns, missing encryption
|
|
522
|
+
- Flags `SharedPreferences` / `UserDefaults` usage with sensitive keys
|
|
523
|
+
- Identifies weak encryption algorithms and insecure file permissions
|
|
524
|
+
- URL: https://github.com/MobSF/Mobile-Security-Framework-MobSF
|
|
525
|
+
|
|
526
|
+
**Semgrep**
|
|
527
|
+
- Custom rules for detecting insecure storage patterns in source code
|
|
528
|
+
- Example rule: flag `UserDefaults.standard.set()` with keys matching `password|token|secret`
|
|
529
|
+
- Integrates into CI/CD pipelines
|
|
530
|
+
|
|
531
|
+
**Android Lint / SwiftLint**
|
|
532
|
+
- Custom lint rules for insecure storage usage
|
|
533
|
+
- Flag hardcoded strings matching credential patterns
|
|
534
|
+
|
|
535
|
+
### 6.2 Dynamic Analysis
|
|
536
|
+
|
|
537
|
+
**Frida**
|
|
538
|
+
- Runtime instrumentation for iOS and Android
|
|
539
|
+
- Hook `SecItemAdd` / `SecItemCopyMatching` to monitor Keychain operations
|
|
540
|
+
- Hook `sqlite3_open()` to identify unencrypted database access
|
|
541
|
+
- Hook `SharedPreferences.edit()` to log preference writes
|
|
542
|
+
- Hook `NSLog` / `Log.d()` to detect sensitive log output at runtime
|
|
543
|
+
- URL: https://frida.re
|
|
544
|
+
|
|
545
|
+
**Example Frida script -- monitor SharedPreferences writes:**
|
|
546
|
+
```javascript
|
|
547
|
+
Java.perform(function() {
|
|
548
|
+
var SharedPrefsEditor = Java.use(
|
|
549
|
+
'android.content.SharedPreferences$Editor'
|
|
550
|
+
);
|
|
551
|
+
SharedPrefsEditor.putString.implementation = function(key, value) {
|
|
552
|
+
console.log('[SharedPrefs] putString: ' + key + ' = ' + value);
|
|
553
|
+
return this.putString(key, value);
|
|
554
|
+
};
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**Objection**
|
|
559
|
+
- Built on Frida; provides pre-built commands for mobile pentesting
|
|
560
|
+
- `ios keychain dump` -- extract all Keychain items
|
|
561
|
+
- `ios cookies get` -- extract stored cookies
|
|
562
|
+
- `android sslpinning disable` -- bypass certificate pinning for traffic analysis
|
|
563
|
+
- `android keystore list` -- enumerate Keystore entries
|
|
564
|
+
- URL: https://github.com/sensepost/objection
|
|
565
|
+
|
|
566
|
+
### 6.3 Backup Extraction and Forensics
|
|
567
|
+
|
|
568
|
+
**iExplorer / iMazing (iOS)**
|
|
569
|
+
- Browse iOS app sandboxes without jailbreak (for paired devices)
|
|
570
|
+
- Extract databases, preferences, caches from iTunes backups
|
|
571
|
+
|
|
572
|
+
**Android Backup Extractor**
|
|
573
|
+
- Convert `.ab` backup files to `.tar` format
|
|
574
|
+
- Inspect SharedPreferences XML, SQLite databases, cached files
|
|
575
|
+
- Command: `java -jar abe.jar unpack backup.ab backup.tar`
|
|
576
|
+
|
|
577
|
+
**adb (Android Debug Bridge)**
|
|
578
|
+
- `adb backup -apk -shared com.example.app` -- create app backup
|
|
579
|
+
- `adb shell run-as com.example.app ls /data/data/com.example.app/`
|
|
580
|
+
- Inspect databases: `adb shell run-as com.example.app cat databases/app.db`
|
|
581
|
+
|
|
582
|
+
### 6.4 Automated CI/CD Integration
|
|
583
|
+
|
|
584
|
+
- Run MobSF static analysis on every build
|
|
585
|
+
- Integrate Semgrep rules for insecure storage patterns into PR checks
|
|
586
|
+
- Automate backup extraction tests as part of security regression suite
|
|
587
|
+
- Use Frida scripts in automated E2E security tests on emulators
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## 7. Platform-Specific Guidance
|
|
592
|
+
|
|
593
|
+
### 7.1 iOS
|
|
594
|
+
|
|
595
|
+
**Keychain Best Practices:**
|
|
596
|
+
- Default to `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` for most credentials
|
|
597
|
+
- Use `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` for highest-security items (only
|
|
598
|
+
available when device has a passcode set; item is deleted if passcode is removed)
|
|
599
|
+
- Scope items with `kSecAttrAccessGroup` only when cross-app sharing is genuinely needed
|
|
600
|
+
- Require biometric authentication via `SecAccessControl` with `.biometryCurrentSet`
|
|
601
|
+
|
|
602
|
+
**Data Protection API:**
|
|
603
|
+
- Set `NSFileProtectionComplete` on files containing sensitive data
|
|
604
|
+
- CoreData stores default to `NSFileProtectionCompleteUntilFirstUserAuthentication`; override
|
|
605
|
+
to `Complete` for sensitive stores
|
|
606
|
+
- Background operations that need file access must use `CompleteUntilFirstUserAuthentication`
|
|
607
|
+
or `CompleteUnlessOpen`
|
|
608
|
+
|
|
609
|
+
**Backup Exclusion:**
|
|
610
|
+
```swift
|
|
611
|
+
var url = fileURL
|
|
612
|
+
var resourceValues = URLResourceValues()
|
|
613
|
+
resourceValues.isExcludedFromBackup = true
|
|
614
|
+
try url.setResourceValues(resourceValues)
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
**App Transport Security (ATS):**
|
|
618
|
+
Enabled by default. Never disable globally. Any exceptions must be justified and documented.
|
|
619
|
+
|
|
620
|
+
### 7.2 Android
|
|
621
|
+
|
|
622
|
+
**Keystore Provider Types:**
|
|
623
|
+
- `AndroidKeyStore` -- default provider; uses TEE (Trusted Execution Environment)
|
|
624
|
+
- StrongBox (`setIsStrongBoxBacked(true)`) -- uses dedicated secure element; highest
|
|
625
|
+
security but not available on all devices; fall back to TEE when unavailable
|
|
626
|
+
- Always verify hardware backing: `KeyInfo.isInsideSecureHardware`
|
|
627
|
+
|
|
628
|
+
**File-Based Encryption (FBE):**
|
|
629
|
+
Android 10+ uses file-based encryption by default. Files are encrypted with per-user
|
|
630
|
+
credentials. However, app-layer encryption remains essential because:
|
|
631
|
+
- FBE protects against offline attacks on powered-off devices only
|
|
632
|
+
- Root access on a running device bypasses FBE
|
|
633
|
+
- App-layer encryption protects against compromised OS components
|
|
634
|
+
|
|
635
|
+
**Manifest Hardening:**
|
|
636
|
+
```xml
|
|
637
|
+
<application
|
|
638
|
+
android:allowBackup="false"
|
|
639
|
+
android:fullBackupContent="false"
|
|
640
|
+
android:dataExtractionRules="@xml/data_extraction_rules">
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**data_extraction_rules.xml (Android 12+):**
|
|
644
|
+
```xml
|
|
645
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
646
|
+
<data-extraction-rules>
|
|
647
|
+
<cloud-backup>
|
|
648
|
+
<exclude domain="sharedpref" path="."/>
|
|
649
|
+
<exclude domain="database" path="."/>
|
|
650
|
+
</cloud-backup>
|
|
651
|
+
<device-transfer>
|
|
652
|
+
<exclude domain="sharedpref" path="."/>
|
|
653
|
+
<exclude domain="database" path="."/>
|
|
654
|
+
</device-transfer>
|
|
655
|
+
</data-extraction-rules>
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### 7.3 Flutter
|
|
659
|
+
|
|
660
|
+
**flutter_secure_storage:**
|
|
661
|
+
- Uses Keychain (iOS) and EncryptedSharedPreferences (Android)
|
|
662
|
+
- Set `IOSOptions.accessibility` for appropriate Keychain protection class
|
|
663
|
+
- On Android, consider setting `AndroidOptions(encryptedSharedPreferences: true)` explicitly
|
|
664
|
+
- Monitor the underlying `androidx.security:security-crypto` deprecation status
|
|
665
|
+
|
|
666
|
+
**Database Encryption:**
|
|
667
|
+
- Use `sqflite_sqlcipher` as a drop-in replacement for `sqflite`
|
|
668
|
+
- Store the SQLCipher encryption key in flutter_secure_storage
|
|
669
|
+
- `sqlcipher_flutter_libs` provides native SQLCipher libraries for all platforms
|
|
670
|
+
- SQLCipher v4 uses PBKDF2 with 256,000 iterations and 256-bit AES
|
|
671
|
+
|
|
672
|
+
**Secure Preferences Pattern:**
|
|
673
|
+
```dart
|
|
674
|
+
// DO NOT: store sensitive data with shared_preferences
|
|
675
|
+
// final prefs = await SharedPreferences.getInstance();
|
|
676
|
+
// prefs.setString('token', jwt); // INSECURE: plaintext XML/plist
|
|
677
|
+
|
|
678
|
+
// DO: use flutter_secure_storage
|
|
679
|
+
final secureStorage = FlutterSecureStorage();
|
|
680
|
+
await secureStorage.write(key: 'token', value: jwt);
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### 7.4 React Native
|
|
684
|
+
|
|
685
|
+
**react-native-keychain:**
|
|
686
|
+
- Maps to iOS Keychain and Android Keystore
|
|
687
|
+
- Supports biometric access control
|
|
688
|
+
- Set `accessible` to `WHEN_UNLOCKED_THIS_DEVICE_ONLY` for tokens
|
|
689
|
+
- Use `setGenericPassword` / `getGenericPassword` for credential pairs
|
|
690
|
+
|
|
691
|
+
**react-native-encrypted-storage:**
|
|
692
|
+
- Simpler key-value API over Keychain (iOS) and EncryptedSharedPreferences (Android)
|
|
693
|
+
- Good for encrypted settings, tokens, and configuration
|
|
694
|
+
|
|
695
|
+
**Critical: Never use AsyncStorage for sensitive data.** AsyncStorage is a plaintext
|
|
696
|
+
JSON-backed store with no encryption or access control. It is appropriate only for
|
|
697
|
+
non-sensitive user preferences and UI state.
|
|
698
|
+
|
|
699
|
+
**Expo SecureStore:**
|
|
700
|
+
- For Expo-managed projects, `expo-secure-store` wraps Keychain (iOS) and
|
|
701
|
+
EncryptedSharedPreferences (Android)
|
|
702
|
+
- Provides `setItemAsync` / `getItemAsync` with size limit of 2048 bytes per value
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
## 8. Incident Patterns
|
|
707
|
+
|
|
708
|
+
### 8.1 Mobile Data Leak Discovery
|
|
709
|
+
|
|
710
|
+
**How mobile data leaks are typically discovered:**
|
|
711
|
+
|
|
712
|
+
1. **Security researcher disclosure** -- researcher examines app binary, finds plaintext
|
|
713
|
+
database or credentials in backup, reports to vendor
|
|
714
|
+
2. **Breach forum data dump** -- stolen data appears on underground forums; forensic
|
|
715
|
+
investigation traces source to mobile app local storage
|
|
716
|
+
3. **Automated scanning** -- MobSF or similar tools in CI/CD catch insecure storage patterns
|
|
717
|
+
before release (prevention) or during audit (detection)
|
|
718
|
+
4. **User report** -- user discovers personal data visible in files, exported backups, or
|
|
719
|
+
screenshots shared inadvertently
|
|
720
|
+
5. **Regulatory audit** -- HIPAA, PCI-DSS, or GDPR audit identifies non-compliant data
|
|
721
|
+
storage practices in mobile apps
|
|
722
|
+
|
|
723
|
+
### 8.2 Backup Extraction Attack Pattern
|
|
724
|
+
|
|
725
|
+
**Attack sequence:**
|
|
726
|
+
1. Attacker gains physical access to device (theft, borrowed, corporate)
|
|
727
|
+
2. Creates backup via iTunes (iOS) or `adb backup` (Android)
|
|
728
|
+
3. If unencrypted, directly accesses app sandbox files
|
|
729
|
+
4. If encrypted (iOS), attempts password brute-force against backup keybag
|
|
730
|
+
5. Extracts SQLite databases, SharedPreferences, Keychain backup
|
|
731
|
+
6. Searches for tokens, passwords, PII, API keys
|
|
732
|
+
7. Uses extracted credentials for account takeover or lateral movement
|
|
733
|
+
|
|
734
|
+
**Detection indicators:**
|
|
735
|
+
- Unexpected iTunes pairing records on corporate devices
|
|
736
|
+
- USB debugging enabled on managed Android devices
|
|
737
|
+
- Backup files appearing on unauthorized machines (MDM can detect this)
|
|
738
|
+
- Credential reuse from data only present in mobile app local storage
|
|
739
|
+
|
|
740
|
+
### 8.3 Response Playbook
|
|
741
|
+
|
|
742
|
+
1. **Contain:** Force token revocation server-side for affected accounts
|
|
743
|
+
2. **Assess:** Determine what data was stored locally and potentially exposed
|
|
744
|
+
3. **Remediate:** Push app update with encrypted storage; rotate all affected credentials
|
|
745
|
+
4. **Notify:** If PII/PHI was exposed, follow breach notification requirements (72 hours
|
|
746
|
+
under GDPR, "without unreasonable delay" under HIPAA)
|
|
747
|
+
5. **Prevent:** Add backup extraction and storage audit to security testing pipeline
|
|
748
|
+
6. **Verify:** Perform backup extraction test on updated app to confirm fix
|
|
749
|
+
|
|
750
|
+
---
|
|
751
|
+
|
|
752
|
+
## 9. Compliance and Standards
|
|
753
|
+
|
|
754
|
+
### 9.1 OWASP MASVS-STORAGE (v2.0)
|
|
755
|
+
|
|
756
|
+
The Mobile Application Security Verification Standard (MASVS) v2.0 simplified storage
|
|
757
|
+
requirements to two primary controls:
|
|
758
|
+
|
|
759
|
+
**MASVS-STORAGE-1: Secure Storage of Sensitive Data**
|
|
760
|
+
- Sensitive data is not stored in plaintext
|
|
761
|
+
- Appropriate platform secure storage mechanisms are used
|
|
762
|
+
- Data is encrypted at rest with strong algorithms
|
|
763
|
+
- Encryption keys are properly managed (not hardcoded or co-located with data)
|
|
764
|
+
|
|
765
|
+
**MASVS-STORAGE-2: Prevention of Data Leakage**
|
|
766
|
+
- App does not leak sensitive data via backups, logs, screenshots, clipboard, or caches
|
|
767
|
+
- Third-party libraries and SDKs do not store sensitive data insecurely
|
|
768
|
+
- Keyboard cache is disabled for sensitive input fields
|
|
769
|
+
|
|
770
|
+
### 9.2 OWASP Mobile Top 10 2024 -- M9: Insecure Data Storage
|
|
771
|
+
|
|
772
|
+
**Moved from M2 (2016) to M9 (2024)** -- not because the risk decreased, but because
|
|
773
|
+
other categories were reorganized. Insecure data storage remains one of the most prevalent
|
|
774
|
+
mobile vulnerabilities.
|
|
775
|
+
|
|
776
|
+
**M9 covers:**
|
|
777
|
+
- Plaintext storage of credentials and tokens
|
|
778
|
+
- Weak or absent encryption on local databases
|
|
779
|
+
- Failure to use platform-native secure storage
|
|
780
|
+
- Data leakage through logs, backups, and caches
|
|
781
|
+
- Sensitive data visible in app snapshots
|
|
782
|
+
|
|
783
|
+
### 9.3 HIPAA Mobile Data Requirements
|
|
784
|
+
|
|
785
|
+
For mobile apps handling Protected Health Information (PHI):
|
|
786
|
+
|
|
787
|
+
- **Encryption mandate:** PHI must be encrypted at rest using AES-128/192/256 (per NIST)
|
|
788
|
+
- **Access controls:** Biometric or strong PIN required to access PHI on device
|
|
789
|
+
- **Audit logging:** Record all access to PHI stored on device
|
|
790
|
+
- **Session timeout:** Auto-logout after configurable inactivity period
|
|
791
|
+
- **Remote wipe:** Capability to remotely delete PHI from lost/stolen devices
|
|
792
|
+
- **Business Associate Agreements (BAAs):** Required for all vendors with PHI access,
|
|
793
|
+
including cloud storage, analytics, and crash reporting services
|
|
794
|
+
- **Backup encryption:** All backups containing PHI must be encrypted
|
|
795
|
+
- **No SMS/MMS:** PHI must not be transmitted via SMS or MMS
|
|
796
|
+
- **Breach notification:** Report breaches affecting 500+ individuals within 60 days to HHS
|
|
797
|
+
|
|
798
|
+
### 9.4 PCI-DSS Mobile Considerations
|
|
799
|
+
|
|
800
|
+
For apps handling payment card data:
|
|
801
|
+
- Never store full track data, CVV, or PIN on the device
|
|
802
|
+
- PAN (card number) must be rendered unreadable when stored (encryption, truncation, hashing)
|
|
803
|
+
- Encryption keys must be stored separately from encrypted data
|
|
804
|
+
- Periodic scanning for unprotected PANs in local storage
|
|
805
|
+
|
|
806
|
+
### 9.5 GDPR Data Minimization
|
|
807
|
+
|
|
808
|
+
- Store only data strictly necessary for the app's stated purpose
|
|
809
|
+
- Implement data retention policies with automatic purging
|
|
810
|
+
- Provide users the ability to export and delete their data from local storage
|
|
811
|
+
- Document legal basis for each category of locally stored personal data
|
|
812
|
+
|
|
813
|
+
---
|
|
814
|
+
|
|
815
|
+
## 10. Code Examples
|
|
816
|
+
|
|
817
|
+
### 10.1 iOS Keychain -- Store and Retrieve Credentials (Swift)
|
|
818
|
+
|
|
819
|
+
```swift
|
|
820
|
+
import Security
|
|
821
|
+
import Foundation
|
|
822
|
+
|
|
823
|
+
enum KeychainError: Error {
|
|
824
|
+
case duplicateItem
|
|
825
|
+
case itemNotFound
|
|
826
|
+
case unexpectedStatus(OSStatus)
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// SECURE: Store a token in Keychain
|
|
830
|
+
func storeToken(_ token: String, for account: String) throws {
|
|
831
|
+
let data = Data(token.utf8)
|
|
832
|
+
|
|
833
|
+
let query: [String: Any] = [
|
|
834
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
835
|
+
kSecAttrService as String: "com.example.app.auth",
|
|
836
|
+
kSecAttrAccount as String: account,
|
|
837
|
+
kSecValueData as String: data,
|
|
838
|
+
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
|
|
839
|
+
]
|
|
840
|
+
|
|
841
|
+
let status = SecItemAdd(query as CFDictionary, nil)
|
|
842
|
+
|
|
843
|
+
switch status {
|
|
844
|
+
case errSecSuccess:
|
|
845
|
+
return
|
|
846
|
+
case errSecDuplicateItem:
|
|
847
|
+
// Update existing item
|
|
848
|
+
let updateQuery: [String: Any] = [
|
|
849
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
850
|
+
kSecAttrService as String: "com.example.app.auth",
|
|
851
|
+
kSecAttrAccount as String: account
|
|
852
|
+
]
|
|
853
|
+
let attributes: [String: Any] = [
|
|
854
|
+
kSecValueData as String: data
|
|
855
|
+
]
|
|
856
|
+
let updateStatus = SecItemUpdate(
|
|
857
|
+
updateQuery as CFDictionary,
|
|
858
|
+
attributes as CFDictionary
|
|
859
|
+
)
|
|
860
|
+
guard updateStatus == errSecSuccess else {
|
|
861
|
+
throw KeychainError.unexpectedStatus(updateStatus)
|
|
862
|
+
}
|
|
863
|
+
default:
|
|
864
|
+
throw KeychainError.unexpectedStatus(status)
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// SECURE: Retrieve a token from Keychain
|
|
869
|
+
func retrieveToken(for account: String) throws -> String? {
|
|
870
|
+
let query: [String: Any] = [
|
|
871
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
872
|
+
kSecAttrService as String: "com.example.app.auth",
|
|
873
|
+
kSecAttrAccount as String: account,
|
|
874
|
+
kSecReturnData as String: true,
|
|
875
|
+
kSecMatchLimit as String: kSecMatchLimitOne
|
|
876
|
+
]
|
|
877
|
+
|
|
878
|
+
var result: AnyObject?
|
|
879
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
880
|
+
|
|
881
|
+
switch status {
|
|
882
|
+
case errSecSuccess:
|
|
883
|
+
guard let data = result as? Data else { return nil }
|
|
884
|
+
return String(data: data, encoding: .utf8)
|
|
885
|
+
case errSecItemNotFound:
|
|
886
|
+
return nil
|
|
887
|
+
default:
|
|
888
|
+
throw KeychainError.unexpectedStatus(status)
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// SECURE: Delete token from Keychain
|
|
893
|
+
func deleteToken(for account: String) throws {
|
|
894
|
+
let query: [String: Any] = [
|
|
895
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
896
|
+
kSecAttrService as String: "com.example.app.auth",
|
|
897
|
+
kSecAttrAccount as String: account
|
|
898
|
+
]
|
|
899
|
+
let status = SecItemDelete(query as CFDictionary)
|
|
900
|
+
guard status == errSecSuccess || status == errSecItemNotFound else {
|
|
901
|
+
throw KeychainError.unexpectedStatus(status)
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
**INSECURE pattern (what NOT to do):**
|
|
907
|
+
```swift
|
|
908
|
+
// INSECURE: Never store tokens in UserDefaults
|
|
909
|
+
UserDefaults.standard.set(token, forKey: "auth_token") // Plaintext plist!
|
|
910
|
+
UserDefaults.standard.set(password, forKey: "password") // Extractable via backup!
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
### 10.2 Android EncryptedSharedPreferences (Kotlin)
|
|
914
|
+
|
|
915
|
+
```kotlin
|
|
916
|
+
import androidx.security.crypto.EncryptedSharedPreferences
|
|
917
|
+
import androidx.security.crypto.MasterKey
|
|
918
|
+
|
|
919
|
+
// SECURE: Create encrypted preferences
|
|
920
|
+
fun getSecurePrefs(context: Context): SharedPreferences {
|
|
921
|
+
val masterKey = MasterKey.Builder(context)
|
|
922
|
+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
923
|
+
.setUserAuthenticationRequired(false)
|
|
924
|
+
.build()
|
|
925
|
+
|
|
926
|
+
return EncryptedSharedPreferences.create(
|
|
927
|
+
context,
|
|
928
|
+
"secure_prefs",
|
|
929
|
+
masterKey,
|
|
930
|
+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
931
|
+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
932
|
+
)
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// SECURE: Store a token
|
|
936
|
+
fun storeToken(context: Context, token: String) {
|
|
937
|
+
getSecurePrefs(context)
|
|
938
|
+
.edit()
|
|
939
|
+
.putString("auth_token", token)
|
|
940
|
+
.apply()
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// SECURE: Retrieve a token
|
|
944
|
+
fun retrieveToken(context: Context): String? {
|
|
945
|
+
return getSecurePrefs(context)
|
|
946
|
+
.getString("auth_token", null)
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// SECURE: Clear all sensitive data on logout
|
|
950
|
+
fun secureLogout(context: Context) {
|
|
951
|
+
getSecurePrefs(context).edit().clear().apply()
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
**INSECURE pattern (what NOT to do):**
|
|
956
|
+
```kotlin
|
|
957
|
+
// INSECURE: Never store tokens in plain SharedPreferences
|
|
958
|
+
val prefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
|
959
|
+
prefs.edit().putString("auth_token", token).apply() // Plaintext XML!
|
|
960
|
+
prefs.edit().putString("password", password).apply() // Readable via adb backup!
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
### 10.3 Flutter / Dart -- Secure Storage
|
|
964
|
+
|
|
965
|
+
```dart
|
|
966
|
+
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
967
|
+
|
|
968
|
+
class SecureStorageService {
|
|
969
|
+
final _storage = const FlutterSecureStorage(
|
|
970
|
+
aOptions: AndroidOptions(encryptedSharedPreferences: true),
|
|
971
|
+
iOptions: IOSOptions(
|
|
972
|
+
accessibility: KeychainAccessibility.unlocked_this_device,
|
|
973
|
+
),
|
|
974
|
+
);
|
|
975
|
+
|
|
976
|
+
// SECURE: Store authentication token
|
|
977
|
+
Future<void> storeToken(String token) async {
|
|
978
|
+
await _storage.write(key: 'auth_token', value: token);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// SECURE: Retrieve authentication token
|
|
982
|
+
Future<String?> getToken() async {
|
|
983
|
+
return await _storage.read(key: 'auth_token');
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// SECURE: Clear all data on logout
|
|
987
|
+
Future<void> clearAll() async {
|
|
988
|
+
await _storage.deleteAll();
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// SECURE: Check if token exists
|
|
992
|
+
Future<bool> hasToken() async {
|
|
993
|
+
return await _storage.containsKey(key: 'auth_token');
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
**INSECURE pattern (what NOT to do):**
|
|
999
|
+
```dart
|
|
1000
|
+
// INSECURE: Never use shared_preferences for sensitive data
|
|
1001
|
+
import 'package:shared_preferences/shared_preferences.dart';
|
|
1002
|
+
|
|
1003
|
+
final prefs = await SharedPreferences.getInstance();
|
|
1004
|
+
prefs.setString('auth_token', token); // Plaintext! Extractable via backup!
|
|
1005
|
+
prefs.setString('password', password); // Never store passwords locally!
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
### 10.4 SQLCipher Setup (Flutter)
|
|
1009
|
+
|
|
1010
|
+
```dart
|
|
1011
|
+
import 'package:sqflite_sqlcipher/sqflite.dart';
|
|
1012
|
+
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
1013
|
+
|
|
1014
|
+
class SecureDatabase {
|
|
1015
|
+
static Database? _db;
|
|
1016
|
+
static const _storage = FlutterSecureStorage();
|
|
1017
|
+
static const _dbKeyName = 'db_encryption_key';
|
|
1018
|
+
|
|
1019
|
+
// Generate or retrieve the database encryption key
|
|
1020
|
+
static Future<String> _getOrCreateKey() async {
|
|
1021
|
+
String? key = await _storage.read(key: _dbKeyName);
|
|
1022
|
+
if (key == null) {
|
|
1023
|
+
// Generate a cryptographically random key
|
|
1024
|
+
final random = Random.secure();
|
|
1025
|
+
final bytes = List<int>.generate(32, (_) => random.nextInt(256));
|
|
1026
|
+
key = base64Url.encode(bytes);
|
|
1027
|
+
await _storage.write(key: _dbKeyName, value: key);
|
|
1028
|
+
}
|
|
1029
|
+
return key;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// SECURE: Open encrypted database
|
|
1033
|
+
static Future<Database> getDatabase() async {
|
|
1034
|
+
if (_db != null) return _db!;
|
|
1035
|
+
|
|
1036
|
+
final dbKey = await _getOrCreateKey();
|
|
1037
|
+
final dbPath = await getDatabasesPath();
|
|
1038
|
+
final path = '$dbPath/secure_app.db';
|
|
1039
|
+
|
|
1040
|
+
_db = await openDatabase(
|
|
1041
|
+
path,
|
|
1042
|
+
password: dbKey, // SQLCipher encryption password
|
|
1043
|
+
version: 1,
|
|
1044
|
+
onCreate: (db, version) async {
|
|
1045
|
+
await db.execute('''
|
|
1046
|
+
CREATE TABLE users (
|
|
1047
|
+
id INTEGER PRIMARY KEY,
|
|
1048
|
+
name TEXT NOT NULL,
|
|
1049
|
+
email TEXT NOT NULL
|
|
1050
|
+
)
|
|
1051
|
+
''');
|
|
1052
|
+
},
|
|
1053
|
+
);
|
|
1054
|
+
return _db!;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// SECURE: Destroy database and key on logout
|
|
1058
|
+
static Future<void> destroyDatabase() async {
|
|
1059
|
+
await _db?.close();
|
|
1060
|
+
_db = null;
|
|
1061
|
+
final dbPath = await getDatabasesPath();
|
|
1062
|
+
await deleteDatabase('$dbPath/secure_app.db');
|
|
1063
|
+
await _storage.delete(key: _dbKeyName);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
### 10.5 Screenshot Prevention
|
|
1069
|
+
|
|
1070
|
+
**Android (Kotlin):**
|
|
1071
|
+
```kotlin
|
|
1072
|
+
// SECURE: Prevent screenshots and app switcher preview
|
|
1073
|
+
class SensitiveActivity : AppCompatActivity() {
|
|
1074
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
1075
|
+
super.onCreate(savedInstanceState)
|
|
1076
|
+
// Set FLAG_SECURE to prevent screenshots and screen recording
|
|
1077
|
+
window.setFlags(
|
|
1078
|
+
WindowManager.LayoutParams.FLAG_SECURE,
|
|
1079
|
+
WindowManager.LayoutParams.FLAG_SECURE
|
|
1080
|
+
)
|
|
1081
|
+
setContentView(R.layout.activity_sensitive)
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
**iOS (Swift):**
|
|
1087
|
+
```swift
|
|
1088
|
+
// SECURE: Obscure sensitive content in app switcher
|
|
1089
|
+
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
1090
|
+
var window: UIWindow?
|
|
1091
|
+
private var blurView: UIVisualEffectView?
|
|
1092
|
+
|
|
1093
|
+
func sceneWillResignActive(_ scene: UIScene) {
|
|
1094
|
+
// Add blur overlay when entering background
|
|
1095
|
+
guard let window = window else { return }
|
|
1096
|
+
let blurEffect = UIBlurEffect(style: .light)
|
|
1097
|
+
let blurView = UIVisualEffectView(effect: blurEffect)
|
|
1098
|
+
blurView.frame = window.bounds
|
|
1099
|
+
blurView.tag = 999
|
|
1100
|
+
window.addSubview(blurView)
|
|
1101
|
+
self.blurView = blurView
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
func sceneDidBecomeActive(_ scene: UIScene) {
|
|
1105
|
+
// Remove blur overlay when returning to foreground
|
|
1106
|
+
window?.viewWithTag(999)?.removeFromSuperview()
|
|
1107
|
+
blurView = nil
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
**Flutter (Dart):**
|
|
1113
|
+
```dart
|
|
1114
|
+
import 'package:flutter/services.dart';
|
|
1115
|
+
|
|
1116
|
+
// Android-only: prevent screenshots via platform channel
|
|
1117
|
+
class ScreenshotPrevention {
|
|
1118
|
+
static const _channel = MethodChannel('com.example.app/security');
|
|
1119
|
+
|
|
1120
|
+
static Future<void> enableSecureMode() async {
|
|
1121
|
+
if (Platform.isAndroid) {
|
|
1122
|
+
await _channel.invokeMethod('enableSecureMode');
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
static Future<void> disableSecureMode() async {
|
|
1127
|
+
if (Platform.isAndroid) {
|
|
1128
|
+
await _channel.invokeMethod('disableSecureMode');
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// In the corresponding Android MainActivity.kt:
|
|
1134
|
+
// override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
|
1135
|
+
// MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
|
|
1136
|
+
// "com.example.app/security").setMethodCallHandler { call, result ->
|
|
1137
|
+
// when (call.method) {
|
|
1138
|
+
// "enableSecureMode" -> {
|
|
1139
|
+
// window.setFlags(FLAG_SECURE, FLAG_SECURE)
|
|
1140
|
+
// result.success(null)
|
|
1141
|
+
// }
|
|
1142
|
+
// "disableSecureMode" -> {
|
|
1143
|
+
// window.clearFlags(FLAG_SECURE)
|
|
1144
|
+
// result.success(null)
|
|
1145
|
+
// }
|
|
1146
|
+
// }
|
|
1147
|
+
// }
|
|
1148
|
+
// }
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
### 10.6 Secure Logout -- Clearing All Sensitive Data
|
|
1152
|
+
|
|
1153
|
+
```swift
|
|
1154
|
+
// iOS: Comprehensive secure logout
|
|
1155
|
+
func performSecureLogout() {
|
|
1156
|
+
// 1. Delete Keychain items
|
|
1157
|
+
let secClasses = [
|
|
1158
|
+
kSecClassGenericPassword,
|
|
1159
|
+
kSecClassInternetPassword,
|
|
1160
|
+
kSecClassCertificate,
|
|
1161
|
+
kSecClassKey,
|
|
1162
|
+
kSecClassIdentity
|
|
1163
|
+
]
|
|
1164
|
+
for secClass in secClasses {
|
|
1165
|
+
let query: [String: Any] = [kSecClass as String: secClass]
|
|
1166
|
+
SecItemDelete(query as CFDictionary)
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
// 2. Clear UserDefaults (non-sensitive prefs)
|
|
1170
|
+
if let bundleId = Bundle.main.bundleIdentifier {
|
|
1171
|
+
UserDefaults.standard.removePersistentDomain(forName: bundleId)
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// 3. Clear cookies
|
|
1175
|
+
if let cookies = HTTPCookieStorage.shared.cookies {
|
|
1176
|
+
for cookie in cookies {
|
|
1177
|
+
HTTPCookieStorage.shared.deleteCookie(cookie)
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// 4. Clear URL cache
|
|
1182
|
+
URLCache.shared.removeAllCachedResponses()
|
|
1183
|
+
|
|
1184
|
+
// 5. Clear WebView data
|
|
1185
|
+
let dataStore = WKWebsiteDataStore.default()
|
|
1186
|
+
let dataTypes = WKWebsiteDataStore.allWebsiteDataTypes()
|
|
1187
|
+
dataStore.fetchDataRecords(ofTypes: dataTypes) { records in
|
|
1188
|
+
dataStore.removeData(
|
|
1189
|
+
ofTypes: dataTypes,
|
|
1190
|
+
for: records,
|
|
1191
|
+
completionHandler: {}
|
|
1192
|
+
)
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// 6. Clear temp files
|
|
1196
|
+
let tmpDir = NSTemporaryDirectory()
|
|
1197
|
+
if let files = try? FileManager.default.contentsOfDirectory(atPath: tmpDir) {
|
|
1198
|
+
for file in files {
|
|
1199
|
+
try? FileManager.default.removeItem(atPath: tmpDir + file)
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
```kotlin
|
|
1206
|
+
// Android: Comprehensive secure logout
|
|
1207
|
+
fun performSecureLogout(context: Context) {
|
|
1208
|
+
// 1. Clear encrypted preferences
|
|
1209
|
+
getSecurePrefs(context).edit().clear().apply()
|
|
1210
|
+
|
|
1211
|
+
// 2. Clear regular preferences
|
|
1212
|
+
context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
|
1213
|
+
.edit().clear().apply()
|
|
1214
|
+
|
|
1215
|
+
// 3. Clear cookies
|
|
1216
|
+
CookieManager.getInstance().removeAllCookies(null)
|
|
1217
|
+
CookieManager.getInstance().flush()
|
|
1218
|
+
|
|
1219
|
+
// 4. Clear WebView cache and data
|
|
1220
|
+
WebView(context).apply {
|
|
1221
|
+
clearCache(true)
|
|
1222
|
+
clearHistory()
|
|
1223
|
+
clearFormData()
|
|
1224
|
+
}
|
|
1225
|
+
WebStorage.getInstance().deleteAllData()
|
|
1226
|
+
|
|
1227
|
+
// 5. Clear app cache
|
|
1228
|
+
context.cacheDir.deleteRecursively()
|
|
1229
|
+
|
|
1230
|
+
// 6. Delete databases
|
|
1231
|
+
context.databaseList().forEach { dbName ->
|
|
1232
|
+
context.deleteDatabase(dbName)
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// 7. Clear clipboard
|
|
1236
|
+
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE)
|
|
1237
|
+
as ClipboardManager
|
|
1238
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
1239
|
+
clipboard.clearPrimaryClip()
|
|
1240
|
+
} else {
|
|
1241
|
+
clipboard.setPrimaryClip(ClipData.newPlainText("", ""))
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
---
|
|
1247
|
+
|
|
1248
|
+
## References
|
|
1249
|
+
|
|
1250
|
+
- OWASP MASVS v2.0: https://mas.owasp.org/MASVS/
|
|
1251
|
+
- OWASP Mobile Top 10 (2024): https://owasp.org/www-project-mobile-top-10/
|
|
1252
|
+
- OWASP MASTG (Mobile Application Security Testing Guide): https://mas.owasp.org/MASTG/
|
|
1253
|
+
- Apple Keychain Services Documentation: https://developer.apple.com/documentation/security/keychain_services
|
|
1254
|
+
- Apple Data Protection: https://support.apple.com/guide/security/keychain-data-protection-secb0694df1a/web
|
|
1255
|
+
- Android Keystore: https://developer.android.com/training/articles/keystore
|
|
1256
|
+
- EncryptedSharedPreferences: https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
|
|
1257
|
+
- EncryptedSharedPreferences Community Fork: https://github.com/ed-george/encrypted-shared-preferences
|
|
1258
|
+
- SQLCipher: https://www.zetetic.net/sqlcipher/
|
|
1259
|
+
- flutter_secure_storage: https://pub.dev/packages/flutter_secure_storage
|
|
1260
|
+
- react-native-keychain: https://github.com/oblador/react-native-keychain
|
|
1261
|
+
- MobSF: https://github.com/MobSF/Mobile-Security-Framework-MobSF
|
|
1262
|
+
- Frida: https://frida.re
|
|
1263
|
+
- Objection: https://github.com/sensepost/objection
|
|
1264
|
+
- NIST SP 800-175B (Encryption Guidance): https://csrc.nist.gov/publications/detail/sp/800-175b/rev-1/final
|
|
1265
|
+
- Verizon 2025 DBIR: https://www.verizon.com/business/resources/reports/dbir/
|