Security changes implemented
parent
e93771df33
commit
83b17532d0
@ -0,0 +1,15 @@
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
|
||||
final talsecConfig = TalsecConfig(
|
||||
androidConfig: AndroidConfig(
|
||||
packageName: 'com.cloudsolutions.HMGPatientApp',
|
||||
signingCertHashes: ['6tvWaoN5coG4SnfxGbdQlcLmM0J4ePQwDjrKIg+QkV0=', 'j6VEqVhrypHMIiXiFdRLDdGwjGaMGWY7KAdBJA+Z4Pc='], // Must be the release cert hash
|
||||
supportedStores: ['com.android.vending'], // Google Play Store
|
||||
),
|
||||
iosConfig: IOSConfig(
|
||||
bundleIds: ['com.cloudsolutions.HMGPatientApp'], // 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,157 @@
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/talsec_config.dart';
|
||||
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
||||
|
||||
/// 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 {
|
||||
final AppState appState;
|
||||
final LoggerService loggerService;
|
||||
|
||||
final List<ThreatEvent> _detectedThreats = [];
|
||||
bool _isInitialized = false;
|
||||
OnCriticalThreatDetected? _onCriticalThreatCallback;
|
||||
|
||||
SecurityServiceImpl({
|
||||
required this.appState,
|
||||
required this.loggerService,
|
||||
});
|
||||
|
||||
@override
|
||||
bool get isSafeDevice => appState.isSafeDevice;
|
||||
|
||||
@override
|
||||
List<ThreatEvent> get detectedThreats => List.unmodifiable(_detectedThreats);
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
if (_isInitialized) {
|
||||
loggerService.logInfo('SecurityService already initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loggerService.logInfo('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;
|
||||
loggerService.logInfo('SecurityService initialized successfully');
|
||||
} catch (e) {
|
||||
loggerService.logError('Failed to initialize SecurityService: $e');
|
||||
if (!kDebugMode) {
|
||||
FirebaseCrashlytics.instance.recordError(
|
||||
e,
|
||||
StackTrace.current,
|
||||
reason: 'SecurityService initialization failed',
|
||||
fatal: false,
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void setOnCriticalThreatCallback(OnCriticalThreatDetected callback) {
|
||||
_onCriticalThreatCallback = callback;
|
||||
loggerService.logInfo('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) {
|
||||
loggerService.logError('🔴 CRITICAL THREAT: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||
} else {
|
||||
loggerService.logInfo('⚠️ WARNING: $threatType ${additionalInfo != null ? "- $additionalInfo" : ""}');
|
||||
}
|
||||
|
||||
// Block app if critical threat
|
||||
if (severity == ThreatSeverity.critical) {
|
||||
appState.setIsSafeDevice = false;
|
||||
loggerService.logError('Device marked as UNSAFE due to: $threatType');
|
||||
|
||||
// Trigger callback to navigate to unsafe device page
|
||||
if (_onCriticalThreatCallback != null) {
|
||||
_onCriticalThreatCallback!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/services/security_service.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.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: AppColors.whiteColor,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24.w),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Logo
|
||||
Utils.buildImgWithAssets(icon: AppAssets.hmgLogo, width: MediaQuery.of(context).size.width * 0.7, height: 90.h, fit: BoxFit.contain),
|
||||
SizedBox(height: 32.h),
|
||||
// Warning Icon
|
||||
Icon(
|
||||
Icons.security,
|
||||
size: 80.h,
|
||||
color: AppColors.primaryRedColor,
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
// Title
|
||||
Text(
|
||||
'Unsafe Device Detected',
|
||||
style: TextStyle(
|
||||
fontSize: 24.f,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.primaryRedColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Description
|
||||
Text(
|
||||
'For your security, this app cannot run on devices with security vulnerabilities.',
|
||||
style: TextStyle(
|
||||
fontSize: 16.f,
|
||||
color: Colors.black87,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
// if (getIt.get<SecurityService>().detectedThreats.isNotEmpty) ...[
|
||||
// Text(
|
||||
// 'Detected Issues:',
|
||||
// style: TextStyle(
|
||||
// fontSize: 14.f,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.black87,
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 8.h),
|
||||
// Container(
|
||||
// padding: EdgeInsets.all(12.w),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Colors.red.withOpacity(0.1),
|
||||
// borderRadius: BorderRadius.circular(8.r),
|
||||
// border: Border.all(color: Colors.red.withOpacity(0.3)),
|
||||
// ),
|
||||
// child: Column(
|
||||
// children: getIt
|
||||
// .get<SecurityService>()
|
||||
// .detectedThreats
|
||||
// .where((t) => t.severity == ThreatSeverity.critical)
|
||||
// .take(5) // Show max 5 threats
|
||||
// .map((threat) => Padding(
|
||||
// padding: EdgeInsets.symmetric(vertical: 4.h),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Icon(Icons.error_outline, size: 16.f, color: Colors.red),
|
||||
// SizedBox(width: 8.w),
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// threat.threatType,
|
||||
// style: TextStyle(fontSize: 12.f),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ))
|
||||
// .toList(),
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 24.h),
|
||||
// ],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue