Update: 2026-08-06 22:54:35
This commit is contained in:
+71
-37
@@ -53,48 +53,82 @@ try {
|
||||
$stmt->execute([':id' => $id]);
|
||||
$driver = $stmt->fetch();
|
||||
|
||||
if (!$driver || empty($driver['password'])) {
|
||||
if (!$driver) {
|
||||
unauthorizedDriver();
|
||||
}
|
||||
|
||||
$decPhone = !empty($driver['phone']) ? $encryptionHelper->decryptData($driver['phone']) : null;
|
||||
$decNat = !empty($driver['national_number']) ? $encryptionHelper->decryptData($driver['national_number']) : null;
|
||||
|
||||
if (empty($decPhone)) {
|
||||
securityLog("LoginDriver failed: phone decryption returned null", [
|
||||
'driver_id' => $driver['id'] ?? 'unknown',
|
||||
]);
|
||||
unauthorizedDriver();
|
||||
}
|
||||
|
||||
// ── المحاولة الأولى: طريقة جديدة (قيم خام) ─────────────
|
||||
$newParts = [
|
||||
$driver['id'],
|
||||
trim($decPhone),
|
||||
];
|
||||
if (!empty($decNat)) {
|
||||
$newParts[] = trim($decNat);
|
||||
}
|
||||
$newString = implode('|', $newParts);
|
||||
$newSecret = hash_hmac('sha256', $newString, $pepper, true);
|
||||
|
||||
if (password_verify($newSecret, $driver['password'])) {
|
||||
// ✅ صح - طريقة جديدة
|
||||
} else {
|
||||
// ── المحاولة الثانية: طريقة قديمة (قيم مشفرة) للتوافق ─
|
||||
$oldParts = [
|
||||
$driver['id'],
|
||||
$driver['phone'],
|
||||
];
|
||||
if (!empty($driver['national_number'])) {
|
||||
$oldParts[] = $driver['national_number'];
|
||||
}
|
||||
$oldString = implode('|', $oldParts);
|
||||
$oldSecret = hash_hmac('sha256', $oldString, $pepper, true);
|
||||
|
||||
if (!password_verify($oldSecret, $driver['password'])) {
|
||||
// ── مسار السائقين المسجلين عبر OTP (بدون password في DB) ──
|
||||
if (empty($driver['password'])) {
|
||||
// التحقق من هوية السائق عبر fingerprint المخزن في driverToken
|
||||
if (empty($fingerprint)) {
|
||||
securityLog("LoginDriver(OTP): no fingerprint sent", [
|
||||
'driver_id' => $driver['id'],
|
||||
]);
|
||||
unauthorizedDriver();
|
||||
}
|
||||
|
||||
$stmtFp = $con->prepare('SELECT fingerPrint FROM driverToken WHERE captain_id = :id LIMIT 1');
|
||||
$stmtFp->execute([':id' => $driver['id']]);
|
||||
$fpRow = $stmtFp->fetch();
|
||||
|
||||
if (!$fpRow || empty($fpRow['fingerPrint'])) {
|
||||
securityLog("LoginDriver(OTP): no stored fingerprint found", [
|
||||
'driver_id' => $driver['id'],
|
||||
]);
|
||||
unauthorizedDriver();
|
||||
}
|
||||
|
||||
$fpPepper = getenv('FP_PEPPER') ?: '';
|
||||
$expectedFp = !empty($fpPepper) ? hash('sha256', $fingerprint . $fpPepper) : $fingerprint;
|
||||
|
||||
if (!hash_equals($fpRow['fingerPrint'], $expectedFp)) {
|
||||
securityLog("LoginDriver(OTP): fingerprint mismatch", [
|
||||
'driver_id' => $driver['id'],
|
||||
]);
|
||||
unauthorizedDriver();
|
||||
}
|
||||
// ✅ تم التحقق عبر fingerprint — نتابع لتوليد JWT
|
||||
} else {
|
||||
// ── المسار التقليدي: التحقق عبر password + HMAC ──────────
|
||||
$decPhone = !empty($driver['phone']) ? $encryptionHelper->decryptData($driver['phone']) : null;
|
||||
$decNat = !empty($driver['national_number']) ? $encryptionHelper->decryptData($driver['national_number']) : null;
|
||||
|
||||
if (empty($decPhone)) {
|
||||
securityLog("LoginDriver failed: phone decryption returned null", [
|
||||
'driver_id' => $driver['id'] ?? 'unknown',
|
||||
]);
|
||||
unauthorizedDriver();
|
||||
}
|
||||
|
||||
// ── المحاولة الأولى: طريقة جديدة (قيم خام) ─────────────
|
||||
$newParts = [
|
||||
$driver['id'],
|
||||
trim($decPhone),
|
||||
];
|
||||
if (!empty($decNat)) {
|
||||
$newParts[] = trim($decNat);
|
||||
}
|
||||
$newString = implode('|', $newParts);
|
||||
$newSecret = hash_hmac('sha256', $newString, $pepper, true);
|
||||
|
||||
if (password_verify($newSecret, $driver['password'])) {
|
||||
// ✅ صح - طريقة جديدة
|
||||
} else {
|
||||
// ── المحاولة الثانية: طريقة قديمة (قيم مشفرة) للتوافق ─
|
||||
$oldParts = [
|
||||
$driver['id'],
|
||||
$driver['phone'],
|
||||
];
|
||||
if (!empty($driver['national_number'])) {
|
||||
$oldParts[] = $driver['national_number'];
|
||||
}
|
||||
$oldString = implode('|', $oldParts);
|
||||
$oldSecret = hash_hmac('sha256', $oldString, $pepper, true);
|
||||
|
||||
if (!password_verify($oldSecret, $driver['password'])) {
|
||||
unauthorizedDriver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$limiter->reset(RateLimiter::identifier(), 'login');
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
تشغيل التطبيق على هذا الجهاز.</string>
|
||||
<string name="exit_button">إغلاق التطبيق</string>
|
||||
<string name="device_secure">الجهاز آمن. الاستمرار بشكل طبيعي.</string>
|
||||
<string name="label">سيرو درايفر</string>
|
||||
<string name="label">انطلق كابتن</string>
|
||||
</resources>
|
||||
@@ -376,7 +376,6 @@ class LoginDriverController extends GetxController {
|
||||
|
||||
var payload = {
|
||||
'id': driverId,
|
||||
'password': box.read(BoxName.emailDriver),
|
||||
'aud': '${AK.allowed}$dev',
|
||||
'fingerPrint': box.read(BoxName.deviceFingerprint) ??
|
||||
await DeviceHelper.getDeviceFingerprint(),
|
||||
@@ -750,11 +749,7 @@ class LoginDriverController extends GetxController {
|
||||
var d = jsonDecoeded['data'][0];
|
||||
var jwt = jsonDecoeded['jwt'];
|
||||
|
||||
// حفظ التوكن أولاً
|
||||
// ⚠️ لازم يُخزَّن خام (بدون c()) — CRUD._getJwt() بيقرأه مباشرة
|
||||
// ويحطه بـ Authorization، ولو كان مشفّر بصير كل طلب 401 → حلقة تجديد
|
||||
if (jwt != null) {
|
||||
box.write(BoxName.jwt, jwt);
|
||||
await storage.write(key: BoxName.jwt, value: jwt.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -371,8 +371,7 @@ class RegistrationController extends GetxController {
|
||||
final uri = Uri.parse(currentUrl);
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
|
||||
final _jwt = box.read(BoxName.jwt);
|
||||
final String _token = _jwt != null ? r(_jwt).split(AppInformation.addd)[0] : '';
|
||||
final String _token = await storage.read(key: BoxName.jwt) ?? '';
|
||||
|
||||
String timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
String nonce = timestamp;
|
||||
@@ -581,8 +580,8 @@ class RegistrationController extends GetxController {
|
||||
|
||||
try {
|
||||
// ترويسات مشتركة
|
||||
final _jwt = box.read(BoxName.jwt);
|
||||
final bearer = 'Bearer ${_jwt != null ? r(_jwt).split(AppInformation.addd)[0] : ''}';
|
||||
final String _jwtToken = await storage.read(key: BoxName.jwt) ?? '';
|
||||
final bearer = 'Bearer $_jwtToken';
|
||||
final hmac = '${box.read(BoxName.hmac)}';
|
||||
|
||||
String fingerPrint =
|
||||
|
||||
@@ -233,7 +233,7 @@ class FirebaseMessagesController extends GetxController {
|
||||
|
||||
case 'token change':
|
||||
case 'TOKEN_CHANGE':
|
||||
box.remove(BoxName.jwt);
|
||||
await storage.delete(key: BoxName.jwt);
|
||||
break;
|
||||
|
||||
case 'face detect':
|
||||
|
||||
@@ -479,8 +479,7 @@ class AI extends GetxController {
|
||||
bool isLoadingVehicleFrontSy = false;
|
||||
bool isLoadingVehicleBackSy = false;
|
||||
Future<void> sendToAI(String type, {required File imageFile}) async {
|
||||
final _jwt = box.read(BoxName.jwt);
|
||||
final String _token = _jwt != null ? r(_jwt).split(AppInformation.addd)[0] : '';
|
||||
final String _token = await storage.read(key: BoxName.jwt) ?? '';
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
|
||||
@@ -488,7 +488,7 @@ class ScanDocumentsByApi extends GetxController {
|
||||
update();
|
||||
|
||||
final String token =
|
||||
box.read(BoxName.jwt)?.toString().split(AppInformation.addd)[0] ?? '';
|
||||
await storage.read(key: BoxName.jwt) ?? '';
|
||||
final String fingerPrint =
|
||||
box.read(BoxName.deviceFingerprint)?.toString() ?? '';
|
||||
final String driverID = box.read(BoxName.driverID).toString();
|
||||
|
||||
@@ -312,8 +312,7 @@ class ImageController extends GetxController {
|
||||
required String filename,
|
||||
required String methodLabel,
|
||||
}) async {
|
||||
final jwt = box.read(BoxName.jwt);
|
||||
final String token = jwt != null ? r(jwt).split(AppInformation.addd)[0] : '';
|
||||
final String token = await storage.read(key: BoxName.jwt) ?? '';
|
||||
final String fingerPrint = box.read(BoxName.deviceFingerprint)?.toString() ?? '';
|
||||
final int fileSizeBytes = await file.length();
|
||||
|
||||
|
||||
@@ -87,8 +87,7 @@ class ComplaintController extends GetxController {
|
||||
|
||||
var uri = Uri.parse(AppLink.uploadAudio);
|
||||
var request = http.MultipartRequest('POST', uri);
|
||||
final jwt = box.read(BoxName.jwt);
|
||||
String token = jwt != null ? r(jwt).toString().split(Env.addd)[0] : '';
|
||||
String token = await storage.read(key: BoxName.jwt) ?? '';
|
||||
final String fingerPrint = box.read(BoxName.deviceFingerprint)?.toString() ?? '';
|
||||
|
||||
var mimeType = lookupMimeType(audioFile.path);
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
تشغيل التطبيق على هذا الجهاز.</string>
|
||||
<string name="exit_button">إغلاق التطبيق</string>
|
||||
<string name="device_secure">الجهاز آمن. الاستمرار بشكل طبيعي.</string>
|
||||
<string name="label">سيرو</string>
|
||||
<string name="label">انطلق</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user