security report
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 2s
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 2s
This commit is contained in:
222
docs/AI_SECURITY_CHECK.md
Normal file
222
docs/AI_SECURITY_CHECK.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# BasedBank — AI Security Audit Report
|
||||
|
||||
> **Generated by:** Claude Sonnet 4.6
|
||||
> **Date:** 2026-05-16
|
||||
> **Scope:** Full source audit — credential storage, lock screen, network layer, data privacy, manifest, dependencies, backup rules.
|
||||
> **Context:** This app is built from reverse-engineered protocol implementations of the official MIB (Faisanet), BML, and Fahipay Android apps. Any cryptographic weaknesses that are part of those upstream protocols are inherited constraints, not design flaws in this codebase.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Rating | Notes |
|
||||
|---|---|---|
|
||||
| Credential storage at rest | **Excellent** | AES-256-GCM, Android Keystore |
|
||||
| Lock screen hashing | **Excellent** | PBKDF2-HMAC-SHA256, 100k iterations |
|
||||
| Screen capture protection | **Good** | FLAG_SECURE on by default |
|
||||
| Backup exclusion | **Excellent** | All sensitive prefs excluded |
|
||||
| Manifest attack surface | **Excellent** | Only MainActivity exported |
|
||||
| Third-party data leakage | **None** | Zero analytics/tracking SDKs |
|
||||
| Certificate pinning | **Missing** | Biggest actionable gap |
|
||||
| Brute-force resistance | **Weak** | 5 attempts / 30s — too short |
|
||||
| Lockout bypass (rooted device) | **Possible** | Counter stored in plain prefs |
|
||||
| MIB transport crypto | **Upstream constraint** | Blowfish/ECB is the MIB protocol |
|
||||
| Code obfuscation | **N/A** | Open-source app, not applicable |
|
||||
|
||||
---
|
||||
|
||||
## 1. Credential Storage
|
||||
|
||||
**File:** `app/src/main/java/sh/sar/basedbank/util/CredentialStore.kt`
|
||||
|
||||
All credentials, session tokens, OTP seeds, and PII are encrypted before being written to SharedPreferences using **AES-256-GCM** with a key generated and stored inside the **Android Keystore** (hardware-backed on supported devices). The key never leaves the secure enclave.
|
||||
|
||||
Encrypted data:
|
||||
- MIB username, password hash, OTP seed, session keys (key1/key2), app ID
|
||||
- BML username, password, OTP seed, access token, device ID
|
||||
- Fahipay ID card, password, authId, session cookie (`__Secure-sess`), device UUID
|
||||
- PIN/pattern PBKDF2 hash + salt
|
||||
- All user profile PII (full name, email, mobile, NID, customer ID)
|
||||
|
||||
The IV is randomly generated per encryption call and stored alongside the ciphertext (`iv:ciphertext` Base64 format), so each encrypted value is unique even with the same plaintext.
|
||||
|
||||
**Cache encryption** (`CacheEncryption.kt`) reuses the same AndroidKeyStore key for all cache SharedPreferences — consistent and correct.
|
||||
|
||||
---
|
||||
|
||||
## 2. Lock Screen
|
||||
|
||||
**File:** `app/src/main/java/sh/sar/basedbank/LockActivity.kt`
|
||||
**File:** `app/src/main/java/sh/sar/basedbank/ui/onboarding/SecuritySetupFragment.kt`
|
||||
|
||||
### PIN / Pattern Hashing
|
||||
- Algorithm: **PBKDF2WithHmacSHA256**
|
||||
- Iterations: **100,000**
|
||||
- Salt: **16 bytes**, `SecureRandom`-generated, unique per setup
|
||||
- Output: 256-bit key stored encrypted in `CredentialStore` (AndroidKeyStore)
|
||||
- Password array is cleared via `PBEKeySpec.clearPassword()` after use
|
||||
|
||||
### Legacy Migration
|
||||
Old installs used raw `SHA-256(salt + input)` stored in plaintext SharedPreferences. On the first successful unlock, the app transparently migrates to PBKDF2 + CredentialStore and removes the legacy plaintext fields.
|
||||
|
||||
### Biometrics
|
||||
- Uses `androidx.biometric` with `BIOMETRIC_WEAK` authenticators
|
||||
- Falls back to PIN/pattern if biometrics unavailable or cancelled
|
||||
|
||||
### Brute-Force Lockout
|
||||
- Max attempts: **5**
|
||||
- Lockout duration: **30 seconds**
|
||||
- Attempt counter stored in `lock_attempts` SharedPreferences
|
||||
|
||||
**Weakness:** The lockout is weaker than ideal for two reasons:
|
||||
1. 30 seconds is very short — an attacker gets ~10 guesses/minute, ~600/hour.
|
||||
2. The `lock_attempts.xml` SharedPreferences file is **not encrypted**. On a rooted device, deleting this file resets the counter entirely, bypassing brute-force protection.
|
||||
|
||||
A stronger policy would use progressive delays (1 min, 5 min, 15 min, 1 hour) and store the counter inside `CredentialStore`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Autolock
|
||||
|
||||
**File:** `app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt`
|
||||
|
||||
- Autolock timer resets on every user interaction (`onUserInteraction`)
|
||||
- Warns the user 10 seconds before locking with a countdown dialog
|
||||
- On resume, checks elapsed pause time and locks immediately if the timeout was exceeded while the app was backgrounded
|
||||
- Default timeout: 60 seconds (configurable in Settings)
|
||||
|
||||
---
|
||||
|
||||
## 4. Screen Capture Protection
|
||||
|
||||
**File:** `app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt:80`
|
||||
|
||||
`FLAG_SECURE` is applied on `HomeActivity` creation, blocking screenshots and hiding app content in the recents/app-switcher. This is **enabled by default** and can be toggled off by the user in Settings.
|
||||
|
||||
---
|
||||
|
||||
## 5. Network Layer
|
||||
|
||||
### Endpoints
|
||||
All network traffic goes exclusively to the banking backends:
|
||||
|
||||
| Backend | Host |
|
||||
|---|---|
|
||||
| MIB (Faisanet) | `https://faisanet.mib.com.mv` |
|
||||
| MIB WebView/KeepAlive | `https://faisamobilex-wv.mib.com.mv` |
|
||||
| BML | `https://www.bankofmaldives.com.mv` |
|
||||
| Fahipay | `https://fahipay.mv` |
|
||||
|
||||
No analytics endpoints, no crash reporters, no CDN telemetry. Zero external data sharing.
|
||||
|
||||
### MIB Transport Cryptography (Upstream Protocol Constraint)
|
||||
**File:** `app/src/main/java/sh/sar/basedbank/api/mib/MibCrypto.kt`
|
||||
|
||||
The MIB Faisanet API uses **Blowfish/ECB/PKCS5Padding** for payload encryption. ECB mode does not use an IV, meaning identical plaintext blocks produce identical ciphertext blocks, and block patterns can be analysed. This is the protocol defined by MIB's own servers and official app — it is an **upstream constraint inherited from the reverse-engineered protocol**, not a choice made in this codebase.
|
||||
|
||||
The session key itself is derived via a **Diffie-Hellman exchange** (2048-bit+ modulus) with SHA-256 key derivation, which provides forward secrecy per session. The hardcoded `DEFAULT_KEY` is only used as the bootstrap key for the initial DH handshake; all subsequent requests use the DH-derived session key.
|
||||
|
||||
### Certificate Pinning
|
||||
**File:** `app/src/main/res/xml/network_security_config.xml`
|
||||
|
||||
The network security config is empty (`<network-security-config/>`), meaning the app relies on Android's default system CA trust store. There is no certificate pinning for any of the three banking backends.
|
||||
|
||||
**Risk:** A device with a malicious or corporate CA certificate installed (e.g. MDM, proxy, or a compromised CA) could perform a man-in-the-middle attack against all API traffic. This is the most significant actionable security gap in the app.
|
||||
|
||||
**Recommendation:** Add `<pin-set>` entries for each banking domain's leaf or intermediate certificate in `network_security_config.xml`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Third-Party Data Leakage
|
||||
|
||||
**No user data is sent to any third party or the developer.**
|
||||
|
||||
### Dependency Audit
|
||||
|
||||
| Library | Purpose | Phones Home? |
|
||||
|---|---|---|
|
||||
| `androidx.*` | UI / Lifecycle / Navigation | No |
|
||||
| `com.google.android.material` | Material Design UI components | No |
|
||||
| `com.squareup.okhttp3:okhttp` | HTTP client | No |
|
||||
| `androidx.camera.*` | QR scanner camera | No |
|
||||
| `com.github.markusfisch:zxing-cpp` | QR barcode decoding | No |
|
||||
| `androidx.biometric` | Biometric authentication | No |
|
||||
| `org.jetbrains.kotlinx:kotlinx-coroutines-android` | Coroutines | No |
|
||||
|
||||
No Firebase, no Crashlytics, no analytics SDKs, no ad networks.
|
||||
|
||||
### Logging
|
||||
There are **zero** `android.util.Log` calls anywhere in the codebase. Nothing is written to logcat. The `okhttp3:logging-interceptor` dependency has been removed.
|
||||
|
||||
### TOTP
|
||||
`Totp.kt` is a self-contained RFC 6238 implementation using only `javax.crypto` from the Android standard library. No third-party OTP SDK is used.
|
||||
|
||||
---
|
||||
|
||||
## 7. Backup & Data Extraction
|
||||
|
||||
**Files:** `app/src/main/res/xml/backup_rules.xml`, `app/src/main/res/xml/data_extraction_rules.xml`
|
||||
|
||||
All sensitive SharedPreferences files are excluded from both Android cloud backup (API 31+) and device-to-device transfer:
|
||||
|
||||
- `credential_store.xml`
|
||||
- `mib_prefs.xml`
|
||||
- `prefs.xml`
|
||||
- `account_cache.xml`
|
||||
- `contacts_cache.xml`
|
||||
- `financing_cache.xml`
|
||||
- `foreign_limits_cache.xml`
|
||||
- `recents_cache.xml`
|
||||
- `lock_attempts.xml`
|
||||
- `cache/` directory
|
||||
|
||||
Note: even if backup were to occur, the AndroidKeyStore encryption key is hardware-bound and **does not leave the device**, so the encrypted data would be unreadable without the original device's secure hardware.
|
||||
|
||||
The manifest has `android:allowBackup="true"`, but the exclusion rules above make the effective backup content empty of anything sensitive.
|
||||
|
||||
---
|
||||
|
||||
## 8. Manifest & Attack Surface
|
||||
|
||||
**File:** `app/src/main/AndroidManifest.xml`
|
||||
|
||||
Only `MainActivity` is `android:exported="true"` (required as the launcher entry point). All other activities (`LockActivity`, `OnboardingActivity`, `LoginActivity`, `HomeActivity`, `QrScannerActivity`) are `exported="false"` and cannot be launched by external apps.
|
||||
|
||||
The `FileProvider` is exported but with `android:grantUriPermissions="true"` scoped correctly — it cannot be accessed without an explicit URI grant.
|
||||
|
||||
### Permissions
|
||||
|
||||
| Permission | Reason |
|
||||
|---|---|
|
||||
| `INTERNET` | Banking API calls |
|
||||
| `CAMERA` | QR scanner |
|
||||
| `USE_BIOMETRIC` | Biometric unlock |
|
||||
| `ACCESS_NETWORK_STATE` | Network availability checks |
|
||||
| `WRITE_EXTERNAL_STORAGE` | Receipt saving, limited to API ≤ 28 |
|
||||
|
||||
No `READ_CONTACTS`, no `READ_SMS`, no `RECEIVE_SMS`, no location permissions.
|
||||
|
||||
---
|
||||
|
||||
## 9. Code Obfuscation
|
||||
|
||||
`isMinifyEnabled = false` in the release build config. This is **not a security concern** because BasedBank is an open-source project — the source code is already publicly available. Obfuscating the APK would provide no additional protection when the source is readable. The only benefit of enabling R8 would be APK size reduction via dead-code elimination.
|
||||
|
||||
---
|
||||
|
||||
## Findings Summary
|
||||
|
||||
### Critical
|
||||
_None._
|
||||
|
||||
### High
|
||||
- **No certificate pinning** — MITM possible on devices with untrusted CA certs installed.
|
||||
|
||||
### Medium
|
||||
- **Weak lockout policy** — 30-second lockout after 5 attempts; no progressive backoff.
|
||||
- **Lockout counter not encrypted** — `lock_attempts.xml` is plaintext; deletable on rooted devices.
|
||||
|
||||
### Low / Informational
|
||||
- **`BIOMETRIC_WEAK`** allows 2D face unlock on some devices, which may be spoofable with a photo.
|
||||
- **MIB Blowfish/ECB** — inherited upstream protocol weakness, not actionable without server-side changes.
|
||||
- **`android:allowBackup="true"`** — flagged by automated scanners but effectively mitigated by the exclusion rules.
|
||||
Reference in New Issue
Block a user