Authentication system (no browser) with mail verification
$begingroup$
I made an authentication system for my software (no browser is used) that works like this:
- User receives a Key on it's mail after buying the software
- User registers account using that key and inputs username, mail, password.
- registration.php checks if the format is correct then sends an email with a confirmation code using a template and inserts the values into DB (verify table).
- The HTML template contains username (from user input), 2 images from the same directory as the registration.php and the template2.html and a button with a get like this: "www.mysite.com/activate.php?id=$serial&conf=$confirmation_code".
- activate.php checks if everything is valid and then inserts the verify table's row into accounts table. (after it is inserted into accounts table, it becomes a valid account).
Everything works as expected (I'm still making the login.php).
Why I'm posting this here:
As a beginner (this is the first php, mysql and html thing I do) I know there is a lot of things that can be improved. My goal is to make it as secure as possible, I followed OWASP as much as I could and at the same time I read a lot on Stack Overflow and Security.Stackexchange.
Here is my code:
registration.php
<?php
$CurrentVersionHash="asd123";
$CurrentVersion="alpha1";
$CurrentHash="asd123";
$serial_length="10";
$memory_cost="31250";
$time_cost="10";
$threads="1";
require 'mail.php';
if ( ! empty( $_POST ) ) // Check if Post is not empty
{
$hash = $_POST['exe'];
$pw = $_POST['pass'];
$user = $_POST['name'];
$version = $_POST['ver'];
$email = $_POST['mail'];
$confirm = $_POST['pass2'];
$serial = $_POST['key'];
if ( empty($hash) or empty($version))
return;
if ( $hash<>$CurrentHash) // Check if file Hash is valid
{
if ( $version<>$CurrentVersionHash ) // Check if it's because it's outdated
{
echo "Old exe";
}
else
{
echo "Exe not valid";
}
return;
}
// Checking if there is any problem in the format
if ( (empty( $user )) or ( strlen( $user ) < 6 ) or ( strlen( $user ) > 254 ) or ( ! ctype_alnum ( $user )) )
{
echo "Error";
return;
}
if ( (empty( $pw)) or (strlen($pw) < 10) or (strlen($pw) > 254) or (! preg_match("#[0-9]+#", $pw)) or ( !preg_match("#[a-z]+#", $pw )) or ( !preg_match("#[A-Z]+#", $pw )) )
{
echo "Error";
return;
}
if ( $pw != $confirm or empty( $confirm ) )
{
echo "Error";
return;
}
if (( empty( $email )) or ( ! filter_var($email, FILTER_VALIDATE_EMAIL)) or (strlen($email) > 254) )
{
echo "Error";
return;
}
if ( ( empty( $serial ) ) or ( strlen( $serial ) <> $serial_length ) )
{
echo "Error";
return;
}
// Check if Serial exists and is not already taken
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM acc WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( ! empty( $result['username'] )) )
{
echo "Invalid Serial";
$error=1;
}
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial is on verify DB (means that someone already registered using it)
if ( ($num_of_rows<>0) and ($error<>1) )
{
echo "Invalid Serial";
$error=1;
}
if ($error<>1)
{
// Check if Username or E-mail is not already taken
$stmt = $con->prepare("SELECT * FROM acc WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
$stmt = $con->prepare("SELECT * FROM verify WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result2 = $stmt->get_result();
$num_of_rows2 = $result2->num_rows;
$result2 = $result2->fetch_array();
if ( ($num_of_rows) or ($num_of_rows2) ) // If user or email already exists on verify and acc DB
{
if ( (strcasecmp($result['username'], $user) == 0) or (strcasecmp($result2['username'], $user) == 0) ) // If user already exists
{
echo "Username in use";
}
else if ( (strcasecmp($result['email'], $email) == 0) or (strcasecmp($result2['email'], $email) == 0) )// If e-mail is already taken
{
echo "Email in use";
}
}
else
{
// Do registration
$password = password_hash( $pw, PASSWORD_ARGON2ID, [
'memory_cost' => $memory_cost,
'time_cost' => $time_cost,
'threads' => $threads,
]);
$rand_id = random_int(-10000, 10000);
$rand_secret = random_str(32);
$stmt = $con->prepare("INSERT INTO verify (username, password, email, serial, rand_id, rand_secret) VALUES (?,?,?,?,?,?)");
$stmt->bind_param('ssssss', $user, $password, $email, $serial, $rand_id, $rand_secret);
$stmt->execute();
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
$variables = array();
$variables['User'] = $user;
$variables['Serial'] = $serial;
$variables['Code'] = $confirmation_code;
$template = file_get_contents("template2.html");
foreach($variables as $key => $value)
{
$template = str_replace('{{ '.$key.' }}', $value, $template);
}
$mail->addAddress($email, $user);
$mail->msgHTML($template, __DIR__);
$mail->AltBody = "You can activate your account here: www.mysite.com/activate.php?id=$serial&conf=$confirmation_code";
if (!$mail->send())
{
echo 'error, mail not delivered';
}
else
{
echo "Registration OK! mail sent";
}
}
}
$stmt->close();
$con->close();
return;
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
activate.php
<?php
if ( ! empty( $_GET ) )
{
if ( isset($_GET['id']) and isset($_GET['conf']) )
{
$key=$_GET['id'];
$code=$_GET['conf'];
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $key);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( empty( $result['username'] )) or ( empty( $result['rand_id'] )) or ( empty( $result['rand_secret'] )) )
{
$error=1;
echo "Invalid code";
}
if ( $error<>1 )
{
$rand_id = $result['rand_id'];
$rand_secret = $result['rand_secret'];
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
if ($confirmation_code == $code)
{
$user=$result['username'];
$password=$result['password'];
$email=$result['email'];
$stmt = $con->prepare("UPDATE accounts SET username = ?, password = ?, email = ? WHERE Serial = ?");
$stmt->bind_param('ssss', $user, $password, $email, $key);
$stmt->execute();
$stmt = $con->prepare("DELETE from verify WHERE serial = ?");
$stmt->bind_param('s', $key);
$stmt->execute();
echo "Account activated";
}
else
{
$error=1;
echo "Invalid code";
}
}
$stmt->close();
$con->close();
return;
}
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
mail.php
<?php
use PHPMailerPHPMailerPHPMailer;
require 'vendor/autoload.php';
$mail = new PHPMailer;
$mail->isSMTP();
$mail->Host = 'smtphost';
$mail->Port = port;
$mail->SMTPAuth = true;
$mail->Username = 'mymail';
$mail->Password = 'mypassword';
$mail->setFrom('mymail', 'myname');
$mail->addReplyTo('mysupportmail', 'myname');
$mail->AddEmbeddedImage('img/logo.png', 'mylogo');
$mail->AddEmbeddedImage('img/gif.gif', 'mygif');
$mail->Subject = "Activation for account";
?>
Thanks in advance.
beginner php html mysql mysqli
New contributor
$endgroup$
add a comment |
$begingroup$
I made an authentication system for my software (no browser is used) that works like this:
- User receives a Key on it's mail after buying the software
- User registers account using that key and inputs username, mail, password.
- registration.php checks if the format is correct then sends an email with a confirmation code using a template and inserts the values into DB (verify table).
- The HTML template contains username (from user input), 2 images from the same directory as the registration.php and the template2.html and a button with a get like this: "www.mysite.com/activate.php?id=$serial&conf=$confirmation_code".
- activate.php checks if everything is valid and then inserts the verify table's row into accounts table. (after it is inserted into accounts table, it becomes a valid account).
Everything works as expected (I'm still making the login.php).
Why I'm posting this here:
As a beginner (this is the first php, mysql and html thing I do) I know there is a lot of things that can be improved. My goal is to make it as secure as possible, I followed OWASP as much as I could and at the same time I read a lot on Stack Overflow and Security.Stackexchange.
Here is my code:
registration.php
<?php
$CurrentVersionHash="asd123";
$CurrentVersion="alpha1";
$CurrentHash="asd123";
$serial_length="10";
$memory_cost="31250";
$time_cost="10";
$threads="1";
require 'mail.php';
if ( ! empty( $_POST ) ) // Check if Post is not empty
{
$hash = $_POST['exe'];
$pw = $_POST['pass'];
$user = $_POST['name'];
$version = $_POST['ver'];
$email = $_POST['mail'];
$confirm = $_POST['pass2'];
$serial = $_POST['key'];
if ( empty($hash) or empty($version))
return;
if ( $hash<>$CurrentHash) // Check if file Hash is valid
{
if ( $version<>$CurrentVersionHash ) // Check if it's because it's outdated
{
echo "Old exe";
}
else
{
echo "Exe not valid";
}
return;
}
// Checking if there is any problem in the format
if ( (empty( $user )) or ( strlen( $user ) < 6 ) or ( strlen( $user ) > 254 ) or ( ! ctype_alnum ( $user )) )
{
echo "Error";
return;
}
if ( (empty( $pw)) or (strlen($pw) < 10) or (strlen($pw) > 254) or (! preg_match("#[0-9]+#", $pw)) or ( !preg_match("#[a-z]+#", $pw )) or ( !preg_match("#[A-Z]+#", $pw )) )
{
echo "Error";
return;
}
if ( $pw != $confirm or empty( $confirm ) )
{
echo "Error";
return;
}
if (( empty( $email )) or ( ! filter_var($email, FILTER_VALIDATE_EMAIL)) or (strlen($email) > 254) )
{
echo "Error";
return;
}
if ( ( empty( $serial ) ) or ( strlen( $serial ) <> $serial_length ) )
{
echo "Error";
return;
}
// Check if Serial exists and is not already taken
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM acc WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( ! empty( $result['username'] )) )
{
echo "Invalid Serial";
$error=1;
}
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial is on verify DB (means that someone already registered using it)
if ( ($num_of_rows<>0) and ($error<>1) )
{
echo "Invalid Serial";
$error=1;
}
if ($error<>1)
{
// Check if Username or E-mail is not already taken
$stmt = $con->prepare("SELECT * FROM acc WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
$stmt = $con->prepare("SELECT * FROM verify WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result2 = $stmt->get_result();
$num_of_rows2 = $result2->num_rows;
$result2 = $result2->fetch_array();
if ( ($num_of_rows) or ($num_of_rows2) ) // If user or email already exists on verify and acc DB
{
if ( (strcasecmp($result['username'], $user) == 0) or (strcasecmp($result2['username'], $user) == 0) ) // If user already exists
{
echo "Username in use";
}
else if ( (strcasecmp($result['email'], $email) == 0) or (strcasecmp($result2['email'], $email) == 0) )// If e-mail is already taken
{
echo "Email in use";
}
}
else
{
// Do registration
$password = password_hash( $pw, PASSWORD_ARGON2ID, [
'memory_cost' => $memory_cost,
'time_cost' => $time_cost,
'threads' => $threads,
]);
$rand_id = random_int(-10000, 10000);
$rand_secret = random_str(32);
$stmt = $con->prepare("INSERT INTO verify (username, password, email, serial, rand_id, rand_secret) VALUES (?,?,?,?,?,?)");
$stmt->bind_param('ssssss', $user, $password, $email, $serial, $rand_id, $rand_secret);
$stmt->execute();
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
$variables = array();
$variables['User'] = $user;
$variables['Serial'] = $serial;
$variables['Code'] = $confirmation_code;
$template = file_get_contents("template2.html");
foreach($variables as $key => $value)
{
$template = str_replace('{{ '.$key.' }}', $value, $template);
}
$mail->addAddress($email, $user);
$mail->msgHTML($template, __DIR__);
$mail->AltBody = "You can activate your account here: www.mysite.com/activate.php?id=$serial&conf=$confirmation_code";
if (!$mail->send())
{
echo 'error, mail not delivered';
}
else
{
echo "Registration OK! mail sent";
}
}
}
$stmt->close();
$con->close();
return;
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
activate.php
<?php
if ( ! empty( $_GET ) )
{
if ( isset($_GET['id']) and isset($_GET['conf']) )
{
$key=$_GET['id'];
$code=$_GET['conf'];
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $key);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( empty( $result['username'] )) or ( empty( $result['rand_id'] )) or ( empty( $result['rand_secret'] )) )
{
$error=1;
echo "Invalid code";
}
if ( $error<>1 )
{
$rand_id = $result['rand_id'];
$rand_secret = $result['rand_secret'];
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
if ($confirmation_code == $code)
{
$user=$result['username'];
$password=$result['password'];
$email=$result['email'];
$stmt = $con->prepare("UPDATE accounts SET username = ?, password = ?, email = ? WHERE Serial = ?");
$stmt->bind_param('ssss', $user, $password, $email, $key);
$stmt->execute();
$stmt = $con->prepare("DELETE from verify WHERE serial = ?");
$stmt->bind_param('s', $key);
$stmt->execute();
echo "Account activated";
}
else
{
$error=1;
echo "Invalid code";
}
}
$stmt->close();
$con->close();
return;
}
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
mail.php
<?php
use PHPMailerPHPMailerPHPMailer;
require 'vendor/autoload.php';
$mail = new PHPMailer;
$mail->isSMTP();
$mail->Host = 'smtphost';
$mail->Port = port;
$mail->SMTPAuth = true;
$mail->Username = 'mymail';
$mail->Password = 'mypassword';
$mail->setFrom('mymail', 'myname');
$mail->addReplyTo('mysupportmail', 'myname');
$mail->AddEmbeddedImage('img/logo.png', 'mylogo');
$mail->AddEmbeddedImage('img/gif.gif', 'mygif');
$mail->Subject = "Activation for account";
?>
Thanks in advance.
beginner php html mysql mysqli
New contributor
$endgroup$
add a comment |
$begingroup$
I made an authentication system for my software (no browser is used) that works like this:
- User receives a Key on it's mail after buying the software
- User registers account using that key and inputs username, mail, password.
- registration.php checks if the format is correct then sends an email with a confirmation code using a template and inserts the values into DB (verify table).
- The HTML template contains username (from user input), 2 images from the same directory as the registration.php and the template2.html and a button with a get like this: "www.mysite.com/activate.php?id=$serial&conf=$confirmation_code".
- activate.php checks if everything is valid and then inserts the verify table's row into accounts table. (after it is inserted into accounts table, it becomes a valid account).
Everything works as expected (I'm still making the login.php).
Why I'm posting this here:
As a beginner (this is the first php, mysql and html thing I do) I know there is a lot of things that can be improved. My goal is to make it as secure as possible, I followed OWASP as much as I could and at the same time I read a lot on Stack Overflow and Security.Stackexchange.
Here is my code:
registration.php
<?php
$CurrentVersionHash="asd123";
$CurrentVersion="alpha1";
$CurrentHash="asd123";
$serial_length="10";
$memory_cost="31250";
$time_cost="10";
$threads="1";
require 'mail.php';
if ( ! empty( $_POST ) ) // Check if Post is not empty
{
$hash = $_POST['exe'];
$pw = $_POST['pass'];
$user = $_POST['name'];
$version = $_POST['ver'];
$email = $_POST['mail'];
$confirm = $_POST['pass2'];
$serial = $_POST['key'];
if ( empty($hash) or empty($version))
return;
if ( $hash<>$CurrentHash) // Check if file Hash is valid
{
if ( $version<>$CurrentVersionHash ) // Check if it's because it's outdated
{
echo "Old exe";
}
else
{
echo "Exe not valid";
}
return;
}
// Checking if there is any problem in the format
if ( (empty( $user )) or ( strlen( $user ) < 6 ) or ( strlen( $user ) > 254 ) or ( ! ctype_alnum ( $user )) )
{
echo "Error";
return;
}
if ( (empty( $pw)) or (strlen($pw) < 10) or (strlen($pw) > 254) or (! preg_match("#[0-9]+#", $pw)) or ( !preg_match("#[a-z]+#", $pw )) or ( !preg_match("#[A-Z]+#", $pw )) )
{
echo "Error";
return;
}
if ( $pw != $confirm or empty( $confirm ) )
{
echo "Error";
return;
}
if (( empty( $email )) or ( ! filter_var($email, FILTER_VALIDATE_EMAIL)) or (strlen($email) > 254) )
{
echo "Error";
return;
}
if ( ( empty( $serial ) ) or ( strlen( $serial ) <> $serial_length ) )
{
echo "Error";
return;
}
// Check if Serial exists and is not already taken
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM acc WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( ! empty( $result['username'] )) )
{
echo "Invalid Serial";
$error=1;
}
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial is on verify DB (means that someone already registered using it)
if ( ($num_of_rows<>0) and ($error<>1) )
{
echo "Invalid Serial";
$error=1;
}
if ($error<>1)
{
// Check if Username or E-mail is not already taken
$stmt = $con->prepare("SELECT * FROM acc WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
$stmt = $con->prepare("SELECT * FROM verify WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result2 = $stmt->get_result();
$num_of_rows2 = $result2->num_rows;
$result2 = $result2->fetch_array();
if ( ($num_of_rows) or ($num_of_rows2) ) // If user or email already exists on verify and acc DB
{
if ( (strcasecmp($result['username'], $user) == 0) or (strcasecmp($result2['username'], $user) == 0) ) // If user already exists
{
echo "Username in use";
}
else if ( (strcasecmp($result['email'], $email) == 0) or (strcasecmp($result2['email'], $email) == 0) )// If e-mail is already taken
{
echo "Email in use";
}
}
else
{
// Do registration
$password = password_hash( $pw, PASSWORD_ARGON2ID, [
'memory_cost' => $memory_cost,
'time_cost' => $time_cost,
'threads' => $threads,
]);
$rand_id = random_int(-10000, 10000);
$rand_secret = random_str(32);
$stmt = $con->prepare("INSERT INTO verify (username, password, email, serial, rand_id, rand_secret) VALUES (?,?,?,?,?,?)");
$stmt->bind_param('ssssss', $user, $password, $email, $serial, $rand_id, $rand_secret);
$stmt->execute();
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
$variables = array();
$variables['User'] = $user;
$variables['Serial'] = $serial;
$variables['Code'] = $confirmation_code;
$template = file_get_contents("template2.html");
foreach($variables as $key => $value)
{
$template = str_replace('{{ '.$key.' }}', $value, $template);
}
$mail->addAddress($email, $user);
$mail->msgHTML($template, __DIR__);
$mail->AltBody = "You can activate your account here: www.mysite.com/activate.php?id=$serial&conf=$confirmation_code";
if (!$mail->send())
{
echo 'error, mail not delivered';
}
else
{
echo "Registration OK! mail sent";
}
}
}
$stmt->close();
$con->close();
return;
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
activate.php
<?php
if ( ! empty( $_GET ) )
{
if ( isset($_GET['id']) and isset($_GET['conf']) )
{
$key=$_GET['id'];
$code=$_GET['conf'];
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $key);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( empty( $result['username'] )) or ( empty( $result['rand_id'] )) or ( empty( $result['rand_secret'] )) )
{
$error=1;
echo "Invalid code";
}
if ( $error<>1 )
{
$rand_id = $result['rand_id'];
$rand_secret = $result['rand_secret'];
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
if ($confirmation_code == $code)
{
$user=$result['username'];
$password=$result['password'];
$email=$result['email'];
$stmt = $con->prepare("UPDATE accounts SET username = ?, password = ?, email = ? WHERE Serial = ?");
$stmt->bind_param('ssss', $user, $password, $email, $key);
$stmt->execute();
$stmt = $con->prepare("DELETE from verify WHERE serial = ?");
$stmt->bind_param('s', $key);
$stmt->execute();
echo "Account activated";
}
else
{
$error=1;
echo "Invalid code";
}
}
$stmt->close();
$con->close();
return;
}
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
mail.php
<?php
use PHPMailerPHPMailerPHPMailer;
require 'vendor/autoload.php';
$mail = new PHPMailer;
$mail->isSMTP();
$mail->Host = 'smtphost';
$mail->Port = port;
$mail->SMTPAuth = true;
$mail->Username = 'mymail';
$mail->Password = 'mypassword';
$mail->setFrom('mymail', 'myname');
$mail->addReplyTo('mysupportmail', 'myname');
$mail->AddEmbeddedImage('img/logo.png', 'mylogo');
$mail->AddEmbeddedImage('img/gif.gif', 'mygif');
$mail->Subject = "Activation for account";
?>
Thanks in advance.
beginner php html mysql mysqli
New contributor
$endgroup$
I made an authentication system for my software (no browser is used) that works like this:
- User receives a Key on it's mail after buying the software
- User registers account using that key and inputs username, mail, password.
- registration.php checks if the format is correct then sends an email with a confirmation code using a template and inserts the values into DB (verify table).
- The HTML template contains username (from user input), 2 images from the same directory as the registration.php and the template2.html and a button with a get like this: "www.mysite.com/activate.php?id=$serial&conf=$confirmation_code".
- activate.php checks if everything is valid and then inserts the verify table's row into accounts table. (after it is inserted into accounts table, it becomes a valid account).
Everything works as expected (I'm still making the login.php).
Why I'm posting this here:
As a beginner (this is the first php, mysql and html thing I do) I know there is a lot of things that can be improved. My goal is to make it as secure as possible, I followed OWASP as much as I could and at the same time I read a lot on Stack Overflow and Security.Stackexchange.
Here is my code:
registration.php
<?php
$CurrentVersionHash="asd123";
$CurrentVersion="alpha1";
$CurrentHash="asd123";
$serial_length="10";
$memory_cost="31250";
$time_cost="10";
$threads="1";
require 'mail.php';
if ( ! empty( $_POST ) ) // Check if Post is not empty
{
$hash = $_POST['exe'];
$pw = $_POST['pass'];
$user = $_POST['name'];
$version = $_POST['ver'];
$email = $_POST['mail'];
$confirm = $_POST['pass2'];
$serial = $_POST['key'];
if ( empty($hash) or empty($version))
return;
if ( $hash<>$CurrentHash) // Check if file Hash is valid
{
if ( $version<>$CurrentVersionHash ) // Check if it's because it's outdated
{
echo "Old exe";
}
else
{
echo "Exe not valid";
}
return;
}
// Checking if there is any problem in the format
if ( (empty( $user )) or ( strlen( $user ) < 6 ) or ( strlen( $user ) > 254 ) or ( ! ctype_alnum ( $user )) )
{
echo "Error";
return;
}
if ( (empty( $pw)) or (strlen($pw) < 10) or (strlen($pw) > 254) or (! preg_match("#[0-9]+#", $pw)) or ( !preg_match("#[a-z]+#", $pw )) or ( !preg_match("#[A-Z]+#", $pw )) )
{
echo "Error";
return;
}
if ( $pw != $confirm or empty( $confirm ) )
{
echo "Error";
return;
}
if (( empty( $email )) or ( ! filter_var($email, FILTER_VALIDATE_EMAIL)) or (strlen($email) > 254) )
{
echo "Error";
return;
}
if ( ( empty( $serial ) ) or ( strlen( $serial ) <> $serial_length ) )
{
echo "Error";
return;
}
// Check if Serial exists and is not already taken
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM acc WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( ! empty( $result['username'] )) )
{
echo "Invalid Serial";
$error=1;
}
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $serial);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial is on verify DB (means that someone already registered using it)
if ( ($num_of_rows<>0) and ($error<>1) )
{
echo "Invalid Serial";
$error=1;
}
if ($error<>1)
{
// Check if Username or E-mail is not already taken
$stmt = $con->prepare("SELECT * FROM acc WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
$stmt = $con->prepare("SELECT * FROM verify WHERE username= ? OR email= ? LIMIT 1");
$stmt->bind_param('ss', $user, $email);
$stmt->execute();
$result2 = $stmt->get_result();
$num_of_rows2 = $result2->num_rows;
$result2 = $result2->fetch_array();
if ( ($num_of_rows) or ($num_of_rows2) ) // If user or email already exists on verify and acc DB
{
if ( (strcasecmp($result['username'], $user) == 0) or (strcasecmp($result2['username'], $user) == 0) ) // If user already exists
{
echo "Username in use";
}
else if ( (strcasecmp($result['email'], $email) == 0) or (strcasecmp($result2['email'], $email) == 0) )// If e-mail is already taken
{
echo "Email in use";
}
}
else
{
// Do registration
$password = password_hash( $pw, PASSWORD_ARGON2ID, [
'memory_cost' => $memory_cost,
'time_cost' => $time_cost,
'threads' => $threads,
]);
$rand_id = random_int(-10000, 10000);
$rand_secret = random_str(32);
$stmt = $con->prepare("INSERT INTO verify (username, password, email, serial, rand_id, rand_secret) VALUES (?,?,?,?,?,?)");
$stmt->bind_param('ssssss', $user, $password, $email, $serial, $rand_id, $rand_secret);
$stmt->execute();
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
$variables = array();
$variables['User'] = $user;
$variables['Serial'] = $serial;
$variables['Code'] = $confirmation_code;
$template = file_get_contents("template2.html");
foreach($variables as $key => $value)
{
$template = str_replace('{{ '.$key.' }}', $value, $template);
}
$mail->addAddress($email, $user);
$mail->msgHTML($template, __DIR__);
$mail->AltBody = "You can activate your account here: www.mysite.com/activate.php?id=$serial&conf=$confirmation_code";
if (!$mail->send())
{
echo 'error, mail not delivered';
}
else
{
echo "Registration OK! mail sent";
}
}
}
$stmt->close();
$con->close();
return;
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
activate.php
<?php
if ( ! empty( $_GET ) )
{
if ( isset($_GET['id']) and isset($_GET['conf']) )
{
$key=$_GET['id'];
$code=$_GET['conf'];
$con = new mysqli($host, $username, $password, $database);
$stmt = $con->prepare("SELECT * FROM verify WHERE serial=?");
$stmt->bind_param('s', $key);
$stmt->execute();
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$result = $result->fetch_array();
// If Serial doesn't exist or is already taken
if ( ($num_of_rows==0) or ( empty( $result['username'] )) or ( empty( $result['rand_id'] )) or ( empty( $result['rand_secret'] )) )
{
$error=1;
echo "Invalid code";
}
if ( $error<>1 )
{
$rand_id = $result['rand_id'];
$rand_secret = $result['rand_secret'];
$confirmation_code = hash_hmac('sha256', $rand_id, $rand_secret);
if ($confirmation_code == $code)
{
$user=$result['username'];
$password=$result['password'];
$email=$result['email'];
$stmt = $con->prepare("UPDATE accounts SET username = ?, password = ?, email = ? WHERE Serial = ?");
$stmt->bind_param('ssss', $user, $password, $email, $key);
$stmt->execute();
$stmt = $con->prepare("DELETE from verify WHERE serial = ?");
$stmt->bind_param('s', $key);
$stmt->execute();
echo "Account activated";
}
else
{
$error=1;
echo "Invalid code";
}
}
$stmt->close();
$con->close();
return;
}
}
function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$pieces = ;
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces = $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
?>
mail.php
<?php
use PHPMailerPHPMailerPHPMailer;
require 'vendor/autoload.php';
$mail = new PHPMailer;
$mail->isSMTP();
$mail->Host = 'smtphost';
$mail->Port = port;
$mail->SMTPAuth = true;
$mail->Username = 'mymail';
$mail->Password = 'mypassword';
$mail->setFrom('mymail', 'myname');
$mail->addReplyTo('mysupportmail', 'myname');
$mail->AddEmbeddedImage('img/logo.png', 'mylogo');
$mail->AddEmbeddedImage('img/gif.gif', 'mygif');
$mail->Subject = "Activation for account";
?>
Thanks in advance.
beginner php html mysql mysqli
beginner php html mysql mysqli
New contributor
New contributor
edited 1 min ago
Ludisposed
7,48921959
7,48921959
New contributor
asked 26 mins ago
Roberto CarlosRoberto Carlos
62
62
New contributor
New contributor
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Roberto Carlos is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212134%2fauthentication-system-no-browser-with-mail-verification%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Roberto Carlos is a new contributor. Be nice, and check out our Code of Conduct.
Roberto Carlos is a new contributor. Be nice, and check out our Code of Conduct.
Roberto Carlos is a new contributor. Be nice, and check out our Code of Conduct.
Roberto Carlos is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212134%2fauthentication-system-no-browser-with-mail-verification%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown