Here's a way to do it:
small security issue (minor nitpicking point so don't fret about it too much)
super-globals are the least safe variable so you should be as specific as possible when choosing which to use - and I'm assuming your are using $_POST to pass on variables from form to code so you should use $_POST to test as well
you don't need to specify the boolean action in a separate line
so you initial check could look like this instead
if ($_POST['action']) {
// rest of code here
}
next minor nitpicking issue (again don't sweat to much about it) - avoid switch-statements like the plague and 'switch' to if-statements instead
switch-statements quickly becomes hard to read/understand as they get longer (for anyone not you that later have to look at and maybe change the code)
if-statements read like normal language making for easier read/understanding of code for others later (ie. if employee is stealing fire employee) even a noob will understand this
so the next code becomes
if ($_POST(['action']) === 'login') {
login($pdo);
} elseif ('something else') {
// do something else
} // etc {
} else {
// throw error-statement
header('Location: index.php');
}
a more serious security point is when cleaning variables/arrays there is a order of operations
- sanitize
- validate
- escape
- (if possible) preg_match
Do all of these and do not change the order of these - and different types (ie. string, number, email, etc.) require different ways of sanitizing and validating
So at the level you are cleaning here you should make sanitize_data into cleanInteger + cleanEmail + cleanString, etc.) and switch to using the appropriate filters for each type
So your sanitize_data() function becomes
function cleanInteger($data) {
$data = trim($data);
$data = filter_var($data, FILTER_SANITIZE_NUMBER_INT);
$data = filter_var($data, FILTER_VALIDATE_INT);
$data = htmlspecialchars($data);
return $data;
}
function cleanEmail($data) {
$data = trim($data);
$data = filter_var($data, FILTER_SANITIZE_EMAIL);
$data = filter_var($data, FILTER_VALIDATE_EMAIL);
$data = htmlspecialchars($data);
return $data;
}
function cleanString($data) {
$data = trim($data);
$data = filter_var($data, FILTER_SANITIZE_STRING);
$data = htmlspecialchars($data);
return $data;
}
Now to your login function:
function login($pdo) {
// first set you variables
// no need to clean here
// as you are only using them for comparison
// in the error handling
// and you only need to preg_match the mobile no. for that
$account_type = $_POST['account_type'];
// only accepts the result if it's a 10-digit number
$mobileno = preg_match('/^\d{10}$/', $_POST['mobileno']) ? $_POST['mobileno'] : NULL;
$email = $_POST['email']);
$password = $_POST['password']);
// then clear out your session cookies
// so you won't have any leftover error messages floating around
// and you are always getting the resent error messages
if ($_SESSION['error']) {
unset($_SESSION['error']);
}
// then you error handle and no need to check for empty()
// when !variable will do
// and always remember to exit function
// as there is no need for the users computer or your server
// to spend more time and resources running code if the checks fails
if (!$account_type) {
$_SESSION['error'] = "Please select account type";
header('location:../login');
exit;
} elseif ($account_type === 1) {
// no need to run any more checks
// as if it passes (doesn't exist)
// then it wasn't a 10-digit number using (0-9)
if (!$mobileno) {
$_SESSION['error'] = "Please input valid mobile no., mobile no. can only contain the numbers 0-9 and have to be 10 digits";
header('location:../login');
exit;
} else {
// you try to login
// first you test if mobile no. exists
try {
// always make the code as readable as possible = cleaner code
$sql="
SELECT
reg_id, mobileno, account_type, password // and get password for comparison to input later
FROM
tbl_register
WHERE
mobileno = :mobileno AND is_active = 1
";
$stmt= $pdo->prepare($sql);
// using named variables using bindParam
// you should always be as specific as possible
// using appropriate parameters and length of expected variable
$stmt->bindParam(':mobileno', $mobileno, PDO::PARAM_INT, 10);
$stmt->execute();
$result = $stmt->fetch();
// as you are not using the count for anything like a loop
// there is no need for it
// just check if $result has a value
} catch (Exception $e) {
// never print or echo or ... the $e error message
// as it hold valuable database info
// that a hacker can use
// instead write it to a text-file
// you keep above your root/html/public_html-directory
// so only you can read
file_put_contents("../error/e.txt", $e->getMessage(), FILE_APPEND);
}
if (!$result) {
// here you should never be specific as it will help a hacker
$_SESSION['error']="Mobile no and/or password does not match";
header('location:../login');
exit;
} else {
// then you verify the password
// for this you should you should use whatever method
// you used to hash the user when they were created in the first place
// and if you are not hashing but storing passwords in plain text
// you have way bigger security issues than this script
// but if not already using the password_hash function
// you should look into it
// and then your hashing (and later password_verify)
// would look like this
// first set the database password hash into a variable
$hash = $result['password'];
// then add a cost and use PASSWORD_DEFAULT for hashing
$options = [
'cost' => 10
}
if (!password_verify($password, $hash, PASSWORD_DEFAULT, $options)) {
// here again you should never be specific as it will help a hacker
$_SESSION['error']="Mobile no and/or password does not match";
header('location:../login');
exit;
} else {
// now check if updates have been made
// to the default hash-algorithm
// or the options have changed since last time
// this has the added benefit
// that even if you so far havent used any cost
// or used PASSWORD_DEFAULT
// this will update all your user password hashes for you
// automatically every time the user logs in
// so if you use this login code
// your users passwords are now as secure as can be
if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
$newHash = password_hash($password, PASSWORD_DEFAULT, $options);
try {
$sql = "
UPDATE
tbl_register
SET
password = :newpassword
WHERE
password = :oldpassword
";
$stmt= $pdo->prepare($sql);
$stmt->bindParam(':newpassword', $newHash, PDO::PARAM_STR);
$stmt->bindParam(':oldpassword', $hash, PDO::PARAM_STR);
$stmt->execute();
} catch (Exception $e) {
file_put_contents("../error/e.txt", $e->getMessage(), FILE_APPEND);
}
}
// now and ONLY now do you clean and preg_match the rest
// just before it hits the webpage to be used
// except mobile no. as it was already preg_matched
// and the value in the database matched
$_SESSION['reg_id'] = preg_match('/^[0-9]+$/', $result['reg_id']) ? cleanInteger($result['reg_id']) : NULL;
$_SESSION['mobileno'] = cleanInteger($result['mobileno']);
$_SESSION['role'] = preg_match('/^[1-2]{1}$/', $result['account_type']) ? cleanInteger($result['account_type']) : NULL;
header("Location:../buyer-dashboard.php");
}
}
} elseif ($account_type === 2) {
// do the same as above just with email instead of mobile no.
// as for cleaning and preg_matching results
// no need to preg_match email
// as FILTER_SANITIZE_EMAIL and FILTER_VALIDATE_EMAIL
// does that for you
// and just copy the clean and preg_match from above for the other 2
$_SESSION['email'] = cleanEmail($result['email']);
// if $account_type is niether 1 or 2
} else {
$_SESSION['error'] = "Please chose a valid account type, account type can only be the numbers 1 or 2";
}
}
}
So your final code should look like this:
function cleanInteger($data) {
$data = trim($data);
$data = filter_var($data, FILTER_SANITIZE_NUMBER_INT);
$data = filter_var($data, FILTER_VALIDATE_INT);
$data = htmlspecialchars($data);
return $data;
}
function cleanEmail($data) {
$data = trim($data);
$data = filter_var($data, FILTER_SANITIZE_EMAIL);
$data = filter_var($data, FILTER_VALIDATE_EMAIL);
$data = htmlspecialchars($data);
return $data;
}
function cleanString($data) {
$data = trim($data);
$data = filter_var($data, FILTER_SANITIZE_STRING);
$data = htmlspecialchars($data);
return $data;
}
function login($pdo) {
$account_type = $_POST['account_type'];
$mobileno = preg_match('/^\d{10}$/', $_POST['mobileno']) ? $_POST['mobileno'] : NULL;
$email = $_POST['email']);
$password = $_POST['password']);
if ($_SESSION['error']) {
unset($_SESSION['error']);
}
if (!$account_type) {
$_SESSION['error'] = "Please select account type, account type can only be the numbers 1 or 2";
header('location:../login');
exit;
} elseif ($account_type === 1) {
if (!$mobileno) {
$_SESSION['error'] = "Please input valid mobile no., mobile no. can only contain the numbers 0-9 and have to be 10 digits";
header('location:../login');
exit;
} else {
try {
$sql="
SELECT
reg_id, mobileno, account_type, password // and get password for comparison to input later
FROM
tbl_register
WHERE
mobileno = :mobileno AND is_active = 1
";
$stmt= $pdo->prepare($sql);
$stmt->bindParam(':mobileno', $mobileno, PDO::PARAM_INT, 10);
$stmt->execute();
$result = $stmt->fetch();
} catch (Exception $e) {
file_put_contents("../error/e.txt", $e->getMessage(), FILE_APPEND);
}
if (!$result) {
$_SESSION['error']="Mobile no and/or password does not match";
header('location:../login');
exit;
} else {
$hash = $result['password'];
$options = [
'cost' => 10
}
if (!password_verify($password, $hash, PASSWORD_DEFAULT, $options)) {
$_SESSION['error']="Mobile no and/or password does not match";
header('location:../login');
exit;
} else {
if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
$newHash = password_hash($password, PASSWORD_DEFAULT, $options);
try {
$sql = "
UPDATE
tbl_register
SET
password = :newpassword
WHERE
password = :oldpassword
";
$stmt= $pdo->prepare($sql);
$stmt->bindParam(':newpassword', $newHash, PDO::PARAM_STR);
$stmt->bindParam(':oldpassword', $hash, PDO::PARAM_STR);
$stmt->execute();
} catch (Exception $e) {
file_put_contents("../error/e.txt", $e->getMessage(), FILE_APPEND);
}
}
$_SESSION['reg_id'] = preg_match('/^[0-9]+$/', $result['reg_id']) ? cleanInteger($result['reg_id']) : NULL;
$_SESSION['mobileno'] = cleanInteger($result['mobileno']);
$_SESSION['role'] = preg_match('/^[1-2]{1}$/', $result['account_type']) ? cleanInteger($result['account_type']) : NULL;
header("Location:../buyer-dashboard.php");
}
}
} elseif ($account_type === 2) {
// do the same as above just with email instead of mobile no.
$_SESSION['email'] = cleanEmail($result['email']);
} else {
$_SESSION['error'] = "Please chose a valid account type, account type can only be the numbers 1 or 2";
}
}
}
if ($_POST['action']) {
if ($_POST(['action']) === 'login') {
login($pdo);
} elseif ('something else') {
// do something else
} // etc {
} else {
// throw error-statement
header('Location: index.php');
}
}