fix(security): remove JWT role extraction without signature, add OTP replay protection, fix user enumeration

This commit is contained in:
Hamza-Ayed
2026-06-17 06:45:53 +03:00
parent 81376a2245
commit 3dad979eb5
3 changed files with 15 additions and 37 deletions

View File

@@ -54,14 +54,12 @@ if ($count > 0) {
]); ]);
// jsonError("Incorrect password."); // jsonError("Incorrect password.");
} }
} else { } else {
// The user does not exist echo json_encode([
echo json_encode([ "status" => "Failure",
"status" => "Failure", "data" => "Invalid credentials."
"data" => "User does not exist." ]);
]); }
// jsonError("User does not exist."); $con = null;
}
$conn->close();
?> ?>

View File

@@ -18,17 +18,7 @@ if (empty($receiver)) {
$user_type = filterRequest("user_type"); $user_type = filterRequest("user_type");
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? (function_exists('apache_request_headers') ? (apache_request_headers()['Authorization'] ?? null) : null); // user_type is taken from request only (JWT not trusted without signature verification)
if (!empty($authHeader) && preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
$jwtToken = $matches[1];
$tokenParts = explode('.', $jwtToken);
if (count($tokenParts) === 3) {
$payload = json_decode(base64_decode($tokenParts[1]), true);
if (isset($payload['role'])) {
$user_type = $payload['role'];
}
}
}
$country = filterRequest("country"); // Egypt | Syria | Jordan $country = filterRequest("country"); // Egypt | Syria | Jordan
$method = filterRequest("method"); // whatsapp | sms | voice | flash_call | bearer_send $method = filterRequest("method"); // whatsapp | sms | voice | flash_call | bearer_send

View File

@@ -23,17 +23,7 @@ if (empty($token_code)) {
$user_type = filterRequest("user_type"); $user_type = filterRequest("user_type");
$context = filterRequest("context"); // token_change | login (default) $context = filterRequest("context"); // token_change | login (default)
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? (function_exists('apache_request_headers') ? (apache_request_headers()['Authorization'] ?? null) : null); // user_type is taken from request only (JWT not trusted without signature verification)
if (!empty($authHeader) && preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
$jwtToken = $matches[1];
$tokenParts = explode('.', $jwtToken);
if (count($tokenParts) === 3) {
$payload = json_decode(base64_decode($tokenParts[1]), true);
if (isset($payload['role'])) {
$user_type = $payload['role'];
}
}
}
if (empty($phone_number)) { if (empty($phone_number)) {
jsonError("Phone number is required."); jsonError("Phone number is required.");
@@ -75,7 +65,7 @@ try {
if ($user_type === 'admin') { if ($user_type === 'admin') {
$sql = "SELECT * FROM token_verification_admin $sql = "SELECT * FROM token_verification_admin
WHERE phone_number = :phone AND token = :token WHERE phone_number = :phone AND token = :token
AND expiration_time >= NOW()"; AND expiration_time >= NOW() AND verified = 0";
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR); $stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR); $stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
@@ -103,7 +93,7 @@ try {
} elseif ($user_type === 'service') { } elseif ($user_type === 'service') {
$sql = "SELECT `id` FROM `phone_verification_service` $sql = "SELECT `id` FROM `phone_verification_service`
WHERE `phone_number` = :phone AND `token_code` = :token WHERE `phone_number` = :phone AND `token_code` = :token
AND `expiration_time` > NOW()"; AND `expiration_time` > NOW() AND `is_verified` = 0";
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR); $stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
$stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR); $stmt->bindParam(':token', $encryptedToken, PDO::PARAM_STR);
@@ -124,7 +114,7 @@ try {
$sql = "SELECT `id` FROM `token_verification_driver` $sql = "SELECT `id` FROM `token_verification_driver`
WHERE `phone_number` = :phone WHERE `phone_number` = :phone
AND `token` = :token AND `token` = :token
AND `expiration_time` > NOW()"; AND `expiration_time` > NOW() AND `verified` = 0";
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR); $stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
@@ -147,7 +137,7 @@ try {
$sql = "SELECT `id` FROM `phone_verification` $sql = "SELECT `id` FROM `phone_verification`
WHERE `phone_number` = :phone WHERE `phone_number` = :phone
AND `token_code` = :token AND `token_code` = :token
AND `expiration_time` > NOW()"; AND `expiration_time` > NOW() AND `is_verified` = 0";
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR); $stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
@@ -172,7 +162,7 @@ try {
$sql = "SELECT `id` FROM `token_verification` $sql = "SELECT `id` FROM `token_verification`
WHERE `phone_number` = :phone WHERE `phone_number` = :phone
AND `token` = :token AND `token` = :token
AND `expiration_time` > NOW()"; AND `expiration_time` > NOW() AND `verified` = 0";
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR); $stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);
@@ -195,7 +185,7 @@ try {
$sql = "SELECT `id` FROM `phone_verification_passenger` $sql = "SELECT `id` FROM `phone_verification_passenger`
WHERE `phone_number` = :phone WHERE `phone_number` = :phone
AND `token` = :token AND `token` = :token
AND `expiration_time` > NOW()"; AND `expiration_time` > NOW() AND `verified` = 0";
$stmt = $con->prepare($sql); $stmt = $con->prepare($sql);
$stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR); $stmt->bindParam(':phone', $encryptedPhone, PDO::PARAM_STR);