Add security service implementation and device safety checks
parent
a767f082c5
commit
af8df09bf7
Binary file not shown.
@ -0,0 +1,150 @@
|
|||||||
|
import 'package:doctor_app_flutter/core/service/talsec_config.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:freerasp/freerasp.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
|
import 'package:doctor_app_flutter/config/config.dart' as config;
|
||||||
|
|
||||||
|
|
||||||
|
/// Enum to categorize threat severity levels
|
||||||
|
enum ThreatSeverity {
|
||||||
|
critical, // Block app usage
|
||||||
|
warning, // Log only, don't block
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Model to track threat details
|
||||||
|
class ThreatEvent {
|
||||||
|
final String threatType;
|
||||||
|
final ThreatSeverity severity;
|
||||||
|
final DateTime timestamp;
|
||||||
|
final String? additionalInfo;
|
||||||
|
|
||||||
|
ThreatEvent({
|
||||||
|
required this.threatType,
|
||||||
|
required this.severity,
|
||||||
|
this.additionalInfo,
|
||||||
|
}) : timestamp = DateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Callback type for critical threat detection
|
||||||
|
typedef OnCriticalThreatDetected = void Function();
|
||||||
|
|
||||||
|
/// Abstract class defining the security service interface
|
||||||
|
abstract class SecurityService {
|
||||||
|
/// Initialize and start the security monitoring
|
||||||
|
Future<void> initialize();
|
||||||
|
|
||||||
|
/// Check if device is currently safe
|
||||||
|
bool get isSafeDevice;
|
||||||
|
|
||||||
|
/// Get list of detected threats
|
||||||
|
List<ThreatEvent> get detectedThreats;
|
||||||
|
|
||||||
|
/// Set callback for when critical threat is detected
|
||||||
|
void setOnCriticalThreatCallback(OnCriticalThreatDetected callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of SecurityService using Talsec (freeRASP)
|
||||||
|
class SecurityServiceImpl implements SecurityService {
|
||||||
|
Logger logger = Logger(
|
||||||
|
printer: PrettyPrinter(
|
||||||
|
methodCount: 2,
|
||||||
|
errorMethodCount: 5,
|
||||||
|
lineLength: 1000,
|
||||||
|
colors: true,
|
||||||
|
printEmojis: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<ThreatEvent> _detectedThreats = [];
|
||||||
|
bool _isInitialized = false;
|
||||||
|
OnCriticalThreatDetected? _onCriticalThreatCallback;
|
||||||
|
|
||||||
|
SecurityServiceImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isSafeDevice => config.isSafeDevice;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ThreatEvent> get detectedThreats => [..._detectedThreats];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> initialize() async {
|
||||||
|
if (_isInitialized) {
|
||||||
|
logger.d('SecurityService already initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.d('Initializing SecurityService with Talsec');
|
||||||
|
|
||||||
|
// Start the RASP engine
|
||||||
|
await Talsec.instance.start(talsecConfig);
|
||||||
|
|
||||||
|
// Setup threat callbacks
|
||||||
|
final callback = ThreatCallback(
|
||||||
|
onAppIntegrity: () => _handleThreat('App Integrity', ThreatSeverity.critical),
|
||||||
|
onObfuscationIssues: () => _handleThreat('Obfuscation Issues', ThreatSeverity.warning),
|
||||||
|
onDebug: () => _handleThreat('Debug Mode', ThreatSeverity.critical),
|
||||||
|
onDeviceBinding: () => _handleThreat('Device Binding', ThreatSeverity.critical),
|
||||||
|
onDeviceID: () => _handleThreat('Device ID Mismatch', ThreatSeverity.critical),
|
||||||
|
onHooks: () => _handleThreat('Hooks Detected', ThreatSeverity.critical),
|
||||||
|
onPasscode: () => _handleThreat('Passcode Not Set', ThreatSeverity.warning),
|
||||||
|
onPrivilegedAccess: () => _handleThreat('Privileged Access (Root/Jailbreak)', ThreatSeverity.critical),
|
||||||
|
onSecureHardwareNotAvailable: () => _handleThreat('Secure Hardware Not Available', ThreatSeverity.warning),
|
||||||
|
onSimulator: () => _handleThreat('Simulator/Emulator Detected', ThreatSeverity.critical),
|
||||||
|
onSystemVPN: () => _handleThreat('System VPN Active', ThreatSeverity.warning),
|
||||||
|
onDevMode: () => _handleThreat('Developer Mode', ThreatSeverity.warning),
|
||||||
|
onADBEnabled: () => _handleThreat('USB Debugging Enabled', ThreatSeverity.warning),
|
||||||
|
onUnofficialStore: () => _handleThreat('Unofficial Store Installation', ThreatSeverity.critical),
|
||||||
|
onScreenshot: () => _handleThreat('Screenshot Detected', ThreatSeverity.warning),
|
||||||
|
onScreenRecording: () => _handleThreat('Screen Recording Active', ThreatSeverity.warning),
|
||||||
|
onMultiInstance: () => _handleThreat('Multiple Instances', ThreatSeverity.warning),
|
||||||
|
onLocationSpoofing: () => _handleThreat('Location Spoofing', ThreatSeverity.warning),
|
||||||
|
onTimeSpoofing: () => _handleThreat('Time Spoofing', ThreatSeverity.warning),
|
||||||
|
onAutomation: () => _handleThreat('Automation Detected', ThreatSeverity.warning),
|
||||||
|
onBootloader: () => _handleThreat('Unlocked Bootloader', ThreatSeverity.critical),
|
||||||
|
onMalware: (suspiciousApps) => _handleThreat('Malware/Suspicious Apps', ThreatSeverity.critical, additionalInfo: suspiciousApps.toString()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Talsec.instance.attachListener(callback);
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
logger.d('SecurityService initialized successfully');
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('Failed to initialize SecurityService: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setOnCriticalThreatCallback(OnCriticalThreatDetected callback) {
|
||||||
|
_onCriticalThreatCallback = callback;
|
||||||
|
logger.d('Critical threat callback registered');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle detected threats with appropriate severity
|
||||||
|
void _handleThreat(String threatType, ThreatSeverity severity, {String? additionalInfo}) {
|
||||||
|
final threat = ThreatEvent(
|
||||||
|
threatType: threatType,
|
||||||
|
severity: severity,
|
||||||
|
additionalInfo: additionalInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
_detectedThreats.add(threat);
|
||||||
|
|
||||||
|
// Log to console
|
||||||
|
if (severity == ThreatSeverity.critical) {
|
||||||
|
logger.e('🔴 CRITICAL THREAT: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||||
|
} else {
|
||||||
|
logger.d('⚠️ WARNING: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block app if critical threat
|
||||||
|
if (severity == ThreatSeverity.critical) {
|
||||||
|
config.isSafeDevice = false;
|
||||||
|
logger.e('Device marked as UNSAFE due to: $threatType');
|
||||||
|
_onCriticalThreatCallback?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import 'package:freerasp/freerasp.dart';
|
||||||
|
|
||||||
|
final talsecConfig = TalsecConfig(
|
||||||
|
androidConfig: AndroidConfig(
|
||||||
|
packageName: 'com.hmg.hmgDr',
|
||||||
|
signingCertHashes: ['uNLWc6Ces/bVOgSs5vxQtlHdNtDW8hoBxe4FypG697Y=', 'MP0tIs2hPii94DAsiU9Jy9oZJfsEQy0a0PrG0/qVQfo='], // Must be the release cert hash
|
||||||
|
supportedStores: ['com.android.vending'], // Google Play Store
|
||||||
|
),
|
||||||
|
iosConfig: IOSConfig(
|
||||||
|
bundleIds: ['com.hmg.hmgDr'], // iOS Bundle ID
|
||||||
|
teamId: '3A359E86ZF', // Found in Apple Developer portal
|
||||||
|
),
|
||||||
|
watcherMail: '', // Required to receive security alerts
|
||||||
|
isProd: true, // Enforces strict checks for release builds
|
||||||
|
);
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
import 'package:doctor_app_flutter/core/service/security_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:freerasp/freerasp.dart';
|
||||||
|
import 'package:http/http.dart' as getIt;
|
||||||
|
|
||||||
|
import 'locator.dart';
|
||||||
|
|
||||||
|
class UnsafeDevice extends StatefulWidget {
|
||||||
|
const UnsafeDevice({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<UnsafeDevice> createState() => _UnsafeDeviceState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UnsafeDeviceState extends State<UnsafeDevice> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
Talsec.instance.detachListener();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// Logo
|
||||||
|
Center(
|
||||||
|
child: Image.asset('assets/images/dr_app_logo.png'),
|
||||||
|
),
|
||||||
|
SizedBox(height: 32),
|
||||||
|
// Warning Icon
|
||||||
|
Icon(
|
||||||
|
Icons.security,
|
||||||
|
size: 80,
|
||||||
|
color: Color(0xFFED1C2B),
|
||||||
|
),
|
||||||
|
SizedBox(height: 24),
|
||||||
|
// Title
|
||||||
|
Text(
|
||||||
|
'Unsafe Device Detected',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Color(0xFFED1C2B),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Description
|
||||||
|
Text(
|
||||||
|
'For your security, this app cannot run on devices with security vulnerabilities. is ssss',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
SizedBox(height: 24),
|
||||||
|
if (locator.get<SecurityService>().detectedThreats.isNotEmpty) ...[
|
||||||
|
Text(
|
||||||
|
'Detected Issues:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.red.withOpacity(0.3)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: locator
|
||||||
|
.get<SecurityService>()
|
||||||
|
.detectedThreats
|
||||||
|
.where((t) => t.severity == ThreatSeverity.critical)
|
||||||
|
.take(5) // Show max 5 threats
|
||||||
|
.map((threat) => Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.error_outline, size: 16, color: Colors.red),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
threat.threatType,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 24),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue