Files
Kordant/android/docs/security-practices.md

280 lines
9.3 KiB
Markdown

# Security Practices — Kordant Android
> **Last updated:** 2026-06-01
> **Package:** `com.kordant.android`
> **Purpose:** Document security practices for Play Store Data Safety form and user transparency
---
## 1. Encryption in Transit
### TLS Configuration
All network communication between the Kordant Android app and backend servers is encrypted using **TLS 1.2 or higher**.
**Implementation:**
- `network_security_config.xml` enforces `cleartextTrafficPermitted="false"` for all domains
- Debug builds allow cleartext for local development via `<debug-overrides>`
- API base URL uses HTTPS (`https://api.kordant.com`)
### Certificate Pinning
The Android app implements **SHA-256 certificate pinning** for production and staging domains:
| Domain | Pin 1 (Primary) | Pin 2 (Backup) |
|--------|----------------|----------------|
| `api.kordant.com` | Primary SHA-256 hash | Backup SHA-256 hash |
| `staging.api.kordant.com` | Staging SHA-256 hash | Staging backup SHA-256 hash |
**File:** `res/xml/network_security_config.xml`
**Rotation:** Pins include a backup for graceful certificate rotation. Update pins before certificate expiry. Expiration set to 2027-06-01.
### TLS Enforcement Points
| Component | Enforcement |
|-----------|------------|
| OkHttpClient | HTTPS URLs only (BuildConfig.API_BASE_URL) |
| AUTH interceptor | All auth requests via HTTPS |
| API service | Retrofit base URL uses HTTPS |
| Image loading | Coil via OkHttp with TLS |
---
## 2. Encryption at Rest
### EncryptedSharedPreferences
All sensitive data is stored in **EncryptedSharedPreferences** using:
| Property | Value |
|----------|-------|
| **Key encryption** | AES256-SIV (deterministic, allows key lookup) |
| **Value encryption** | AES256-GCM (authenticated encryption) |
| **Master key** | Android Keystore (`MasterKey.Builder` with `KeyScheme.AES256_GCM`) |
| **Library** | `androidx.security:security-crypto` |
### Data Stored Encrypted
| Data | Key | File |
|------|-----|------|
| Access token | `access_token` | `SecureStorageManager.kt` |
| Refresh token | `refresh_token` | `SecureStorageManager.kt` |
| User profile (PII) | `user_profile_json` | `SecureStorageManager.kt` |
| FCM device token | `fcm_device_token` | `SecureStorageManager.kt` |
| Biometric preference | `biometric_enabled` | `SecureStorageManager.kt` |
### Non-Sensitive Data (Unencrypted)
User preferences that do not contain PII are stored in Android's standard **Preferences DataStore**:
- Theme selection (system/light/dark)
- Language/locale
- Notification preferences (alerts/marketing/system toggles)
- Onboarding completion status
- App version for migration tracking
- Background sync toggle
- Last sync timestamp
### Spam Database (Hashed)
Phone numbers in the local SQLite spam database are **SHA-256 hashed** before storage.
| Field | Storage |
|-------|---------|
| `number_hash` | SHA-256 hash of normalized phone number |
| `pattern` | Wildcard pattern (e.g., `+1-800-*`) |
| `action` | `block`, `flag`, `allow` |
| `category` | `scam`, `telemarketer`, `robocall`, `spam` |
| `spam_score` | 0-100 confidence score |
Raw phone numbers are **never written to disk** in the spam database.
---
## 3. Secure Deletion
### Overwrite-Then-Remove
The app implements **secure deletion** for sensitive keys to mitigate forensic recovery:
```
secureOverwriteAndRemove(key) {
for (i in 0 until 3) {
overwrite with random data → apply()
}
remove(key) → apply()
}
```
### Deletion Methods
| Method | What It Clears | Use Case |
|--------|---------------|----------|
| `clearAllAuthData()` | Access token, refresh token, user profile | Logout |
| `clearAllData()` | All encrypted preferences including biometric | Account deletion (GDPR) |
| `clearAll()` (DataStore) | All user preferences | Reset to defaults |
| `clearAll()` (CacheManager) | API response cache | Logout / cache clear |
| `clearAll()` (SpamDatabase) | Spam numbers + call logs | Full resync / account deletion |
---
## 4. Root Detection & Anti-Tampering
### Detection Methods
| Check | Detection Target |
|-------|-----------------|
| SU binary paths | `/system/bin/su`, `/system/xbin/su`, `/data/local/su`, etc. |
| Busybox paths | `/system/xbin/busybox`, `/data/local/bin/busybox` |
| Dangerous props | `ro.debuggable=1`, `ro.secure=0` |
| Build tags | `test-keys`, `dev-keys` |
| Magisk indicators | `/sbin/.magisk`, `/data/adb/magisk`, Magisk packages |
| Root management packages | Magisk, SuperSU, KingRoot, LuckyPatcher, etc. |
| SU command execution | `su -c id` — checks if uid=0 |
| App signature verification | SHA-256 hash of signing certificate |
| Debugger detection | `android.os.Debug.isDebuggerConnected()` |
| ADB over network | `service.adb.tcp.port` system property |
| Emulator detection | Known properties, model, manufacturer, fingerprint |
| Installer source verification | Play Store, Amazon App Store, Samsung Galaxy Store |
### Response to Detection
| Detection | Behavior |
|-----------|----------|
| Root detected | Features degraded; reported to backend and Crashlytics |
| Tampering detected | Biometric and payment features disabled |
| Emulator detected | Features may be restricted |
| Untrusted install | Warning logged, security restrictions applied |
---
## 5. Log Sanitization
All network logs are sanitized before writing to prevent PII exposure:
| Pattern | Redacted To |
|---------|-------------|
| `Bearer <token>` | `Bearer [REDACTED]` |
| `\b\d{10,15}\b` (phone numbers) | `[PHONE_REDACTED]` |
| Email addresses | `[EMAIL_REDACTED]` |
| Refresh tokens in bodies | `"refreshToken":"[REDACTED]"` |
| Access tokens in bodies | `"accessToken":"[REDACTED]"` |
| ID tokens in bodies | `"idToken":"[REDACTED]"` |
| Passwords in bodies | `"password":"[REDACTED]"` |
**Implementation:** `NetworkModule.kt``provideLoggingInterceptor()`
**Log levels:**
- **Debug builds:** Full headers + sanitized bodies
- **Release builds:** Headers only (no body logging)
---
## 6. Token Refresh Security
### Automatic Silent Refresh
| Property | Value |
|----------|-------|
| **Trigger** | HTTP 401 Unauthorized |
| **Mechanism** | `AuthInterceptor.intercept()``refreshAccessToken()` |
| **Concurrency** | Synchronized via `refreshLock` to prevent race conditions |
| **Fallback** | Clears tokens on refresh failure → user re-authenticates |
### Token Storage
| Token | Storage | Encryption |
|-------|---------|------------|
| Access token | EncryptedSharedPreferences | AES256-GCM |
| Refresh token | EncryptedSharedPreferences | AES256-GCM |
---
## 7. Network Security
### OkHttp Configuration
| Property | Value |
|----------|-------|
| Connect timeout | 30 seconds |
| Read timeout | 30 seconds |
| Write timeout | 30 seconds |
| TLS enforcement | Platform default (TLS 1.2+) |
| Certificate pinning | SHA-256 pins for api.kordant.com |
### Retrofit API Configuration
| Property | Value |
|----------|-------|
| Base URL | `https://api.kordant.com/` (production) |
| Converter | Kotlinx Serialization JSON |
| Headers | `X-Request-ID`, `X-Client-Version`, `X-Client-Platform` |
---
## 8. Biometric Authentication
| Property | Value |
|----------|-------|
| **Library** | `androidx.biometric:biometric` |
| **Storage** | Preference flag in EncryptedSharedPreferences |
| **Root check** | Biometric disabled on rooted/tampered devices |
| **Fallback** | Device credentials (PIN/pattern/password) |
---
## 9. Data Collection Compliance
### Data Minimization
The app collects only the data necessary for its core functionality:
| Feature | Minimum Data Required |
|---------|----------------------|
| Authentication | Email, password (or Google account ID), name |
| Call Screening | Incoming phone number (temporary, hashed for storage) |
| VoicePrint | Voice recording samples |
| DarkWatch | Watchlist items (email, phone, name to monitor) |
| Analytics | Device info, app version (no personal identifiers) |
| Crash reporting | Crash stack trace, device model, OS version |
### User Consent
| Data Type | Consent Mechanism |
|-----------|------------------|
| Account creation | Explicit signup form |
| Google Sign-In | OAuth consent screen |
| Voice recordings | `RECORD_AUDIO` permission + in-app rationale |
| Call screening | `READ_PHONE_STATE` permission + in-app rationale |
| Notifications | `POST_NOTIFICATIONS` (Android 13+) + in-app toggles |
| Crash reporting | Crashlytics opt-out (configured in manifest) |
| Marketing communications | Explicit opt-in via notification settings |
---
## 10. Independent Security Review
**Status:** ⚠️ Pending
An independent third-party security audit is planned before the production launch.
The audit will cover:
- Penetration testing of the mobile application
- API security assessment
- Cryptographic implementation review
- Privacy compliance review
---
## 11. Compliance Standards
| Standard | Status | Notes |
|----------|--------|-------|
| **GDPR** | ✅ Compliant | Data deletion, portability, consent, breach notification |
| **CCPA** | ✅ Compliant | Right to know, delete, opt-out, non-discrimination |
| **COPPA** | ✅ Compliant | No children under 13 data collection |
| **Play Store Data Safety** | ✅ Complete | All data types accurately declared |
| **Android Target API 36** | ✅ Compliant | No deprecated API usage |
| **TLS 1.2/1.3** | ✅ Enforced | Cleartext traffic blocked |
| **OWASP MASVS** | ⚠️ Partial | Security audit planned for full certification |