mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-03 23:53:29 +08:00
136 lines
5.1 KiB
Markdown
136 lines
5.1 KiB
Markdown
---
|
|
paths:
|
|
- "**/*.dart"
|
|
- "**/pubspec.yaml"
|
|
- "**/AndroidManifest.xml"
|
|
- "**/Info.plist"
|
|
---
|
|
# Dart/Flutter Security
|
|
|
|
> This file extends [common/security.md](../common/security.md) with Dart, Flutter, and mobile-specific content.
|
|
|
|
## Secrets Management
|
|
|
|
- Never hardcode API keys, tokens, or credentials in Dart source
|
|
- Use `--dart-define` or `--dart-define-from-file` for compile-time config (values are not truly secret — use a backend proxy for server-side secrets)
|
|
- Use `flutter_dotenv` or equivalent, with `.env` files listed in `.gitignore`
|
|
- Store runtime secrets in platform-secure storage: `flutter_secure_storage` (Keychain on iOS, EncryptedSharedPreferences on Android)
|
|
|
|
```dart
|
|
// BAD
|
|
const apiKey = 'sk-abc123...';
|
|
|
|
// GOOD — compile-time config (not secret, just configurable)
|
|
const apiKey = String.fromEnvironment('API_KEY');
|
|
|
|
// GOOD — runtime secret from secure storage
|
|
final token = await secureStorage.read(key: 'auth_token');
|
|
```
|
|
|
|
## Network Security
|
|
|
|
- Enforce HTTPS — no `http://` calls in production
|
|
- Configure Android `network_security_config.xml` to block cleartext traffic
|
|
- Set `NSAppTransportSecurity` in `Info.plist` to disallow arbitrary loads
|
|
- Set request timeouts on all HTTP clients — never leave defaults
|
|
- Consider certificate pinning for high-security endpoints
|
|
|
|
```dart
|
|
// Dio with timeout and HTTPS enforcement
|
|
final dio = Dio(BaseOptions(
|
|
baseUrl: 'https://api.example.com',
|
|
connectTimeout: const Duration(seconds: 10),
|
|
receiveTimeout: const Duration(seconds: 30),
|
|
));
|
|
```
|
|
|
|
## Input Validation
|
|
|
|
- Validate and sanitize all user input before sending to API or storage
|
|
- Never pass unsanitized input to SQL queries — use parameterized queries (sqflite, drift)
|
|
- Sanitize deep link URLs before navigation — validate scheme, host, and path parameters
|
|
- Use `Uri.tryParse` and validate before navigating
|
|
|
|
```dart
|
|
// BAD — SQL injection
|
|
await db.rawQuery("SELECT * FROM users WHERE email = '$userInput'");
|
|
|
|
// GOOD — parameterized
|
|
await db.query('users', where: 'email = ?', whereArgs: [userInput]);
|
|
|
|
// BAD — unvalidated deep link
|
|
final uri = Uri.parse(incomingLink);
|
|
context.go(uri.path); // could navigate to any route
|
|
|
|
// GOOD — validated deep link
|
|
final uri = Uri.tryParse(incomingLink);
|
|
if (uri != null && uri.host == 'myapp.com' && _allowedPaths.contains(uri.path)) {
|
|
context.go(uri.path);
|
|
}
|
|
```
|
|
|
|
## Data Protection
|
|
|
|
- Store tokens, PII, and credentials only in `flutter_secure_storage`
|
|
- Never write sensitive data to `SharedPreferences` or local files in plaintext
|
|
- Clear auth state on logout: tokens, cached user data, cookies
|
|
- Use biometric authentication (`local_auth`) for sensitive operations
|
|
- Avoid logging sensitive data — no `print(token)` or `debugPrint(password)`
|
|
|
|
## Android-Specific
|
|
|
|
- Declare only required permissions in `AndroidManifest.xml`
|
|
- Export Android components (`Activity`, `Service`, `BroadcastReceiver`) only when necessary; add `android:exported="false"` where not needed
|
|
- Review intent filters — exported components with implicit intent filters are accessible by any app
|
|
- Use `FLAG_SECURE` for screens displaying sensitive data (prevents screenshots)
|
|
|
|
```xml
|
|
<!-- AndroidManifest.xml — restrict exported components -->
|
|
<activity android:name=".MainActivity" android:exported="true">
|
|
<!-- Only the launcher activity needs exported=true -->
|
|
</activity>
|
|
<activity android:name=".SensitiveActivity" android:exported="false" />
|
|
```
|
|
|
|
## iOS-Specific
|
|
|
|
- Declare only required usage descriptions in `Info.plist` (`NSCameraUsageDescription`, etc.)
|
|
- Store secrets in Keychain — `flutter_secure_storage` uses Keychain on iOS
|
|
- Use App Transport Security (ATS) — disallow arbitrary loads
|
|
- Enable data protection entitlement for sensitive files
|
|
|
|
## WebView Security
|
|
|
|
- Use `webview_flutter` v4+ (`WebViewController` / `WebViewWidget`) — the legacy `WebView` widget is removed
|
|
- Disable JavaScript unless explicitly required (`JavaScriptMode.disabled`)
|
|
- Validate URLs before loading — never load arbitrary URLs from deep links
|
|
- Never expose Dart callbacks to JavaScript unless absolutely needed and carefully sandboxed
|
|
- Use `NavigationDelegate.onNavigationRequest` to intercept and validate navigation requests
|
|
|
|
```dart
|
|
// webview_flutter v4+ API (WebViewController + WebViewWidget)
|
|
final controller = WebViewController()
|
|
..setJavaScriptMode(JavaScriptMode.disabled) // disabled unless required
|
|
..setNavigationDelegate(
|
|
NavigationDelegate(
|
|
onNavigationRequest: (request) {
|
|
final uri = Uri.tryParse(request.url);
|
|
if (uri == null || uri.host != 'trusted.example.com') {
|
|
return NavigationDecision.prevent;
|
|
}
|
|
return NavigationDecision.navigate;
|
|
},
|
|
),
|
|
);
|
|
|
|
// In your widget tree:
|
|
WebViewWidget(controller: controller)
|
|
```
|
|
|
|
## Obfuscation and Build Security
|
|
|
|
- Enable obfuscation in release builds: `flutter build apk --obfuscate --split-debug-info=./debug-info/`
|
|
- Keep `--split-debug-info` output out of version control (used for crash symbolication only)
|
|
- Ensure ProGuard/R8 rules don't inadvertently expose serialized classes
|
|
- Run `flutter analyze` and address all warnings before release
|