fix(security): add SSL public key pinning for iOS and Android#717
Merged
RUKAYAT-CODER merged 1 commit intoJun 28, 2026
Merged
Conversation
All API traffic relied on OS-level TLS verification alone, leaving bearer tokens, PII, and payment receipts exposed to MITM attacks via corporate MDM proxies or rogue access points that present forged certificates the OS will accept. - config/security.ts: add SSL_PINNING constants (domain, primary/backup SPKI SHA-256 hashes, bypassEnabled flag tied to EXPO_PUBLIC_APP_ENV) - plugins/withSSLPinning.js: Expo config plugin that injects NSPinnedDomains into Info.plist (iOS 14+) and writes network_security_config.xml with <pin-set> + <debug-overrides> (Android 7+); registers android:networkSecurityConfig on <application> - app.json: register withSSLPinning plugin with domain and pin hash slots - axios.config.ts: isCertPinFailure() detects SSL handshake errors by message/cause keywords, forces full logout, reports to Sentry with endpoint + method only (no token, headers, or body), rejects with SSL_PIN_FAILURE before the ERR_NETWORK retry path - docs/security/pin-rotation.md: zero-downtime rotation runbook (generate next keypair → deploy backup pin build → rotate server cert → promote backup to primary); includes emergency rollback and Sentry monitoring guidance - tests: unit tests for pin failure detection, logout side-effect, Sentry payload shape, ordinary-network-error exclusion, bypass in dev Closes rinafcode#577
|
@Vox-d-glitch Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Contributor
|
Thank you for contributing to the project. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #578
All API traffic relied on OS-level TLS verification alone, leaving bearer tokens, user PII, and payment receipts exposed to MITM attacks via corporate MDM proxies, intercepting proxies (Burp Suite, Charles), and rogue Wi-Fi access points that present forged certificates the OS will accept. This adds SHA-256 public key pinning at the native TLS layer for iOS and Android, with a JS-layer failure handler that forces logout and reports to Sentry without leaking sensitive data.
Summary
SSL_PINNINGconstants tosrc/config/security.ts— domain, primary SPKI SHA-256 hash, backup hash (for zero-downtime rotation), andbypassEnabledflag driven byEXPO_PUBLIC_APP_ENV; pin hashes are never hardcoded inlineplugins/withSSLPinning.jsExpo config plugin following the existingwithProguard.jspattern: injectsNSPinnedDomainsintoInfo.plist(iOS 14+) and writesres/xml/network_security_config.xmlwith a<pin-set>and<debug-overrides>trusting user CAs in debug builds (Android 7+); setsandroid:networkSecurityConfigon<application>inAndroidManifest.xmlapp.jsonwithdomainand both pin hash slots; pinning is skipped entirely for non-production build profilesisCertPinFailure()tosrc/services/api/axios.config.tsthat detects SSL/TLS/certificate keywords inerror.messageanderror.cause, short-circuits in bypass mode, and intercepts before theERR_NETWORKretry path; on failure: reports to Sentry with endpoint and method only (no token, headers, or body), forceslogout(), and rejects withSSL_PIN_FAILUREdocs/security/pin-rotation.mdzero-downtime rotation runbook covering: keypair generation, fingerprint commands, backup-pin build → 80% adoption gate → server cert rotation → primary-pin promotion, emergency rollback, and Sentry alert thresholdstoken/bodykeys), ordinary network errors excluded from pin-failure path, and bypass mode disabling detection; manual device test steps for native pinning are documented in the test fileType of Change
Testing Done
Security Considerations
src/config/security.tsandapp.json; private keys are never committed — see rotation runbookendpointandmethod; no token, Authorization header, request body, or response data is includedPerformance Considerations
useCallback,useMemo) used appropriately to prevent unnecessary renders? — N/A to this changeFlatListoptimized (e.g., usinggetItemLayout,keyExtractor)? — N/A to this changeuseEffectcleanup to avoid memory leaks)? — Pin failure handler is synchronous; no timers or subscriptions introducedChecklist
docs/security/pin-rotation.mdcreated with full rotation runbook