The Songs List App uses Firebase services for data storage and authentication. Security is a critical aspect of any cloud-connected application. This document outlines our security practices, how to report vulnerabilities, and best practices for deploying the app.
We release security updates for the following versions:
| Version | Supported |
|---|---|
| 1.0.x | ✅ |
| < 1.0 | ❌ |
If you discover a security vulnerability, please DO NOT open a public issue. Instead, report it privately:
- Email: Send details to the project maintainer
- GitHub Security Advisory: Use GitHub's private vulnerability reporting feature
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if available)
- Initial Response: Within 48 hours
- Status Update: Within 7 days
- Fix Deployment: Varies by severity
- Critical: Within 24-48 hours
- High: Within 7 days
- Medium: Within 14 days
- Low: Next scheduled release
- Acknowledgment of your report
- Regular updates on our progress
- Credit in the security advisory (if desired)
- Notification when the issue is resolved
The current app uses test mode Firebase rules, which are NOT secure for production. Implement proper security rules before deploying:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Songs collection rules
match /songs/{songId} {
// Allow anyone to read songs
allow read: if true;
// Only authenticated users can write
allow write: if request.auth != null;
// Validate data structure
allow create, update: if request.resource.data.keys().hasAll(['title'])
&& request.resource.data.title is string
&& request.resource.data.title.size() > 0
&& request.resource.data.title.size() <= 100;
// Only owner can delete (if you add user ownership)
allow delete: if request.auth != null
&& resource.data.userId == request.auth.uid;
}
}
}rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// User-specific songs
match /users/{userId}/songs/{songId} {
// Users can only read their own songs
allow read: if request.auth != null
&& request.auth.uid == userId;
// Users can only write to their own songs
allow write: if request.auth != null
&& request.auth.uid == userId;
// Validate song data
allow create, update: if request.resource.data.title is string
&& request.resource.data.title.size() > 0
&& request.resource.data.title.size() <= 100;
}
}
}The app currently has Firebase credentials hardcoded in lib/main.dart:
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: "AIza...", // ⚠️ EXPOSED
// ...
),
);Option 1: FlutterFire CLI (Best Practice)
# Install FlutterFire CLI
dart pub global activate flutterfire_cli
# Auto-generate secure configuration
flutterfire configure
# This creates firebase_options.dart with proper platform-specific configsThen use in your app:
import 'firebase_options.dart';
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);Option 2: Environment Variables
- Add
flutter_dotenvtopubspec.yaml - Create
.envfile (add to.gitignore) - Load environment variables:
import 'package:flutter_dotenv/flutter_dotenv.dart';
await dotenv.load(fileName: ".env");
await Firebase.initializeApp(
options: FirebaseOptions(
apiKey: dotenv.env['FIREBASE_API_KEY']!,
appId: dotenv.env['FIREBASE_APP_ID']!,
// ...
),
);When implementing Firebase Authentication:
// ✅ Good: Validate input before authentication
Future<UserCredential> signInUser(String email, String password) async {
// Validate email format
if (!email.contains('@')) {
throw FirebaseAuthException(
code: 'invalid-email',
message: 'Email address is not valid',
);
}
// Validate password length
if (password.length < 6) {
throw FirebaseAuthException(
code: 'weak-password',
message: 'Password must be at least 6 characters',
);
}
return await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
}- ❌ NEVER store passwords in SharedPreferences or local storage
- ✅ Use Firebase Auth tokens (automatically managed)
- ✅ Implement biometric authentication for sensitive operations
Always validate user input before sending to Firebase:
// ✅ Good: Input validation
Future<void> addSong(String title) async {
// Check for empty title
if (title.trim().isEmpty) {
throw ArgumentError('Song title cannot be empty');
}
// Check for maximum length
if (title.length > 100) {
throw ArgumentError('Song title too long (max 100 characters)');
}
// Sanitize input (remove potentially harmful characters)
final sanitizedTitle = title.trim().replaceAll(RegExp(r'[<>]'), '');
await songs.add({'title': sanitizedTitle});
}Firebase automatically uses HTTPS, but ensure your app:
- Only makes secure connections
- Validates SSL certificates
- Doesn't bypass certificate validation
Add to android/app/src/main/res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- Block all cleartext (HTTP) traffic -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>Reference in AndroidManifest.xml:
<application
android:networkSecurityConfig="@xml/network_security_config"># Check for outdated packages
flutter pub outdated
# Update dependencies
flutter pub upgrade- Regularly review
pubspec.yamldependencies - Remove unused dependencies
- Check for known vulnerabilities in packages
- Use
pub.devpackage scores and verified publishers
Never expose sensitive information in error messages:
// ❌ Bad: Exposes internal details
catch (e) {
print('Firebase error: ${e.toString()}');
showDialog(context: context, builder: (_) => AlertDialog(
title: Text('Error'),
content: Text('Database error: ${e.toString()}'), // ⚠️ Too much info
));
}
// ✅ Good: User-friendly error messages
catch (e) {
// Log detailed error for debugging (remove in production)
debugPrint('Firestore error: $e');
// Show generic error to user
showDialog(context: context, builder: (_) => AlertDialog(
title: Text('Error'),
content: Text('Failed to load songs. Please check your internet connection.'),
));
}For production builds, enable code obfuscation:
# Android
flutter build apk --release --obfuscate --split-debug-info=/<project-directory>/symbols
# iOS
flutter build ios --release --obfuscate --split-debug-info=/<project-directory>/symbols- Only collect data necessary for app functionality
- Don't store sensitive user information unnecessarily
- Implement data retention policies
- Provide data export functionality
- Implement account deletion
- Obtain user consent for data processing
Create a privacy policy that discloses:
- What data is collected
- How data is used
- How data is stored
- Third-party services used (Firebase)
- User rights (access, deletion)
Before deploying to production, ensure:
- Firestore security rules configured (not test mode)
- Firebase API keys secured (not hardcoded)
- Firebase Auth configured if using authentication
- App Check enabled for API abuse prevention
- Input validation on all user inputs
- Error handling doesn't expose sensitive info
- No hardcoded secrets or credentials
- Dependencies up to date and audited
- Code obfuscation enabled for release builds
- Debug logs removed from production code
- Network security config set (Android)
- App Transport Security configured (iOS)
- Security testing performed
- Penetration testing conducted (for sensitive apps)
- Code review completed
- Security practices documented
- Privacy policy created
- Terms of service defined
- Data retention policy established
We appreciate security researchers who responsibly disclose vulnerabilities. Contributors who report valid security issues will be acknowledged in our security advisories (unless they prefer to remain anonymous).
This security policy is part of the Songs List App and is licensed under the Apache License 2.0.
Last Updated: January 29, 2025
Next Review: July 2025
For questions about this security policy, please open a GitHub Discussion or contact the maintainer directly.