PHP の知識を深めるために簡単な cms を作成していますが、残念ながら Cookie が機能していないようです。問題の核心であると思われるファイルを以下に示します。
<?php
// First we execute our common code to connection to the database and start the session
require("common.php");
// Detect if the user has a cookie available. If a cookie is availbale, and a valid
// session is found then redirect the user to the appropriate QUEST landing page
if(isset($_COOKIE["qcore"]))
{
$query = "
SELECT TOP 1
u.*
FROM dbo.[User] AS u
INNER JOIN dbo.UserSession AS us
ON us.UserId = u.UserId
WHERE us.SessionId = :sessiontoken";
// The parameter values
$query_params = array(
':sessiontoken' => $_COOKIE["qcore"]
);
try
{
// Execute the query against the database
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// Retrieve the user data from the database. If $row is false, then the session has
// likely expired and the user will be presented with the login form again.
$row = $stmt->fetch();
// This statement checks if data was available when retreiving user information
// using our session token.
if($row)
{
// Here I am preparing to store the $row array into the $_SESSION by
// removing the salt and password values from it. Although $_SESSION is
// stored on the server-side, there is no reason to store sensitive values
// in it unless you have to. Thus, it is best practice to remove these
// sensitive values first.
unset($row['Salt']);
unset($row['Password']);
// This stores the user's data into the session at the index 'user'.
// We will check this index on the private members-only page to determine whether
// or not the user is logged in. We can also use it to retrieve
// the user's details.
$_SESSION['user'] = $row;
// Redirect the user to the private members-only page.
// This will need to be changed once we have the QUEST logic flow sorted out
// to be the landing quest page.
header("Location: private.php");
die("Redirecting to: private.php");
}
else
{
// If no data is found then the session has expired, or been terminated
// and we need to remove the cookie to present the login form to the user
// To do this we set the cookie expiration date to one hour ago
setcookie("qcore", "", time()-3600);
}
}
// This variable will be used to re-display the user's username to them in the
// login form if they fail to enter the correct password. It is initialized here
// to an empty value, which will be shown if the user has not submitted the form.
$submitted_username = '';
// This if statement checks to determine whether the login form has been submitted
// If it has, then the login code is run, otherwise the form is displayed
if(!empty($_POST))
{
// This query retreives the user's information from the database using
// their username. SELECT TOP 1 prevents people from being able to edit
// their HTTP POST to fetch the entire table.
$query = "
SELECT TOP 1
*
FROM dbo.[User]
WHERE
Username = :username
";
$query_params = array(
':username' => $_POST['username']
);
try
{
// Execute the query against the database
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This variable tells us whether the user has successfully logged in or not.
// We initialize it to false, assuming they have not.
// If we determine that they have entered the right details, then we switch it to true.
$login_ok = false;
// Retrieve the user data from the database. If $row is false, then the username
// they entered is not registered.
$row = $stmt->fetch();
if($row)
{
// Using the password submitted by the user and the salt stored in the database,
// we now check to see whether the passwords match by hashing the submitted password
// and comparing it to the hashed version already stored in the database.
$check_password = hash('sha256', $_POST['password'] . $row['Salt']);
for($round = 0; $round < 65536; $round++)
{
$check_password = hash('sha256', $check_password . $row['Salt']);
}
if($check_password === $row['Password'])
{
// If they do, then we flip this to true
$login_ok = true;
}
}
// If the user logged in successfully, then we send them to the private members-only page
// Otherwise, we display a login failed message and show the login form again
if($login_ok)
{
// Here I am preparing to store the $row array into the $_SESSION by
// removing the salt and password values from it. Although $_SESSION is
// stored on the server-side, there is no reason to store sensitive values
// in it unless you have to. Thus, it is best practice to remove these
// sensitive values first.
if(!empty($_POST))
{
unset($row['Salt']);
unset($row['Password']);
}
// This stores the user's data into the session at the index 'user'.
// We will check this index on the private members-only page to determine whether
// or not the user is logged in. We can also use it to retrieve
// the user's details.
$_SESSION['user'] = $row;
// Generate a session token which is used locally as a key between the users cookie
// and their UserID, this prevents the user from being able to edit their cookie
// to login as another user.
$sessiontoken = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// Save our cookie 'qcore' with the users session id
setcookie("qcore", $sessiontoken);
// Insert a new session ID record, or update if one already exists. One should never
// exist but this is added as a precaution in case a session has expired whilst the login
// form was being filled out (EXTREMELY unlikely), but hey, why not.
$query = "
DECLARE @userid AS INTEGER = :userid
DECLARE @sessionid AS varchar(500) = :sessionid
IF EXISTS ( SELECT TOP 1 *
FROM dbo.UserSession
WHERE UserId = @userid )
UPDATE dbo.UserSession
SET SessionId = @sessionid
WHERE UserId = @userid
ELSE
INSERT INTO dbo.UserSession (
UserId ,
SessionId
) VALUES (
@userid ,
@sessionid)";
$query_params = array(
':userid' => $row['UserId'],
':sessionid' => $sessiontoken
);
try
{
// Execute the query to insert a new user session or update
// an existing one
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
// die("Failed to run query: " . $ex->getMessage());
die("Failed to run query: " . $ex->getMessage());
}
// Redirect the user to the private members-only page.
// This will need to be changed once we have the QUEST logic flow sorted out
// to be the landing quest page.
header("Location: private.php");
die("Redirecting to: private.php");
}
else
{
// Tell the user they failed
print("Login Failed.");
// Show them their username again so all they have to do is enter a new
// password. The use of htmlentities prevents XSS attacks. You should
// always use htmlentities on user submitted values before displaying them
// to any users (including the user that submitted them). For more information:
// http://en.wikipedia.org/wiki/XSS_attack
$submitted_username = htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8');
}
}
?>
<h1>Login</h1>
<form action="login.php" method="post">
Username:<br />
<input type="text" name="username" value="<?php echo $submitted_username; ?>" />
<br /><br />
Password:<br />
<input type="password" name="password" value="" />
<br /><br />
<input type="submit" value="Login" />
</form>
<a href="register.php">Register</a>
そして、セッションを取得した後、この領域で何か単純な問題が発生していると思われますが、何が原因なのかわかりません:
// This statement checks if data was available when retreiving user information
// using our session token.
if($row)
{