Issue
I have deployed an AWS EC2 instance with Apache deployed that hosts a simple registration/login form. When a user registers, their credentials are stored in a MySQL DB that is deployed on a secondary AWS EC2 instance. So when a user hits the login page, they enter their credentials and the PHP code authenticates those with the MySQL DB and subsequently allows the user to enter.
This setup is deployed in two AZs and the MySQL DBs are Master-Master replicated.
I would like to place an application load balancer infront of the two AWS EC2 (Apache) web servers to account for failures in one of the AZs. However when I deploy the AWS Application Load Balancer and use the DNS name to login, the application doesn't seem to authenticate the users credentials and removes the username/password from the form (almost like it refreshes the page).
The application has been built in PHP and is really simple but I cannot workout as to why when using a load balancer my application does not work?
Any suggestions??
My Authentication Method:
<?php
session_start();
// Change this to your connection info.
$DATABASE_HOST = 'XXX.XX.XX.XX';
$DATABASE_USER = 'XXX';
$DATABASE_PASS = 'XXX';
$DATABASE_NAME = 'XXX';
// Try and connect using the info above.
$con = mysqli_connect($DATABASE_HOST, $DATABASE_USER, $DATABASE_PASS, $DATABASE_NAME);
if ( mysqli_connect_errno() ) {
// If there is an error with the connection, stop the script and display the error.
exit('Failed to connect to MySQL: ' . mysqli_connect_error());
}
// Now we check if the data from the login form was submitted, isset() will check if the data exists.
if ( !isset($_POST['username'], $_POST['password']) ) {
// Could not get the data that should have been sent.
exit('Please fill both the username and password fields!');
}
// Prepare our SQL
if ($stmt = $con->prepare('SELECT id, password FROM accounts WHERE username = ?')) {
// Bind parameters (s = string, i = int, b = blob, etc), in our case the username is a string so we use "s"
$stmt->bind_param('s', $_POST['username']);
$stmt->execute();
// Store the result so we can check if the account exists in the database.
$stmt->store_result();
if ($stmt->num_rows > 0) {
$stmt->bind_result($id, $password);
$stmt->fetch();
// Account exists, now we verify the password.
if ($_POST['password'] === $password) {
// Verification success! User has logged-in!
// Create sessions, so we know the user is logged in, they basically act like cookies but remember the data on the server.
session_regenerate_id();
$_SESSION['loggedin'] = TRUE;
$_SESSION['name'] = $_POST['username'];
$_SESSION['id'] = $id;
// echo 'Welcome ' . $_SESSION['name'] . '!';
header('Location: home.php');
} else {
// Incorrect password
echo 'Incorrect username and/or password!';
}
} else {
// Incorrect username
echo 'Incorrect username and/or password!';
}
$stmt->close();
}
?>
Solution
You can't have multiple web servers that require a user to login without either a distributed session store, or sticky sessions enabled. Right now your user session is getting bounced around between multiple servers, but you are only logged into one of those servers.
The easiest way to fix this is to simply go into the Load Balancer settings and enable sticky sessions for now, so all of a user's requests will "stick" to a single server.
Answered By - Mark B