对于TL; DR版本...转到底部并阅读加粗的内容
好的,所以我有一段时间了这个问题,并且我做了很多研究,做了一些更改,但仍然不知道我的代码有什么问题。我的问题是,当用户提交注册表格时,会话总是恢复为空。逻辑如下:
1.用户导航到mysite / register /,并通过同一目录中的index.php提供register.php的服务。
2. index.php处理所有内容(调用具有这种性质的逻辑和内容的类,但所有内容都在顶层通过index.php运行)
3.用户通过vanilla.js ajax调用提交登录表单,并返回console.logged响应。
现在您已经了解了逻辑……让我开始介绍代码:
这是index.php:
<?php
// Allowing PHP to be strictly typed and start session
declare(strict_types=1);
session_start();
// Requiring the necessary classes
require_once "../vendor/autoload.php";
require_once "../model/EditSession.php";
require_once "../model/DatabaseConfig.php";
require_once "../model/ServerValidation.php";
require_once "../model/RegisterUser.php";
require_once "../model/Verify.php";
// Creating the new objects
$validatingServer = new ServerValidation();
$sessionToEdit = new EditSession();
$sessionToEdit->create_new_session_id();
// Checks the request protocol
try {
$validatingServer->checkHttps();
} catch (Exception $ex) {
header("Location: /NotSecure.php");
}
// Setting CSRF token for protection
try {
$csrfToken = $sessionToEdit->store_secure_key_in_session("CSRFTOKEN");
} catch (Exception $ex) {
echo "You have a problem setting your session. $ex";
}
// Handling a navigation to the webpage
$validatingServer->navigateToWebsite("register.php", "Register", $csrfToken);
// For when a user submits the form
try {
$validatingServer->checkRequestType("POST");
$validatingServer->checkContentType("application/json");
$registerFormData = json_decode(file_get_contents("php://input"), true);
$csrfTokenFromForm = $registerFormData["csrfToken"];
$csrfTokenFromSession = $sessionToEdit->get_from_session("CSRFTOKEN");
} catch (Exception $ex) {
echo "Bad request data. $ex";
}
//$validatingServer->checkToken($csrfTokenFromForm, $csrfTokenFromSession);
// Call to make original register user object
try {
$register = new RegisterUser($registerFormData["firstName"], $registerFormData["lastName"], $registerFormData["email"], $registerFormData["password"]);
} catch (Exception $ex) {
echo $ex;
}
// Check email and register the user
try {
$register->checkEmail();
$register->register();
} catch (Exception $ex) {
echo $ex;
}
// Sending registration email to the user
try {
$register->sendRegistrationEmail("http://localhost/");
} catch (Exception $ex) {
echo $ex;
}
echo "Successful Register";
这是前端
<!doctype html>
<html>
<head>
<title><?=$pageTitle;?></title>
</head>
<body>
<main>
<form id="registerForm">
<input type="text" id="firstName" name="firstName" autocomplete="given-name" placeholder="First Name" pattern="^[A-Za-z.\s_-]+$" autofocus required>
<input type="text" id="lastName" name="lastName" autocomplete="family-name" placeholder="Last Name" pattern="^[A-Za-z.\s_-]+$" required>
<input type="email" id="email" name="email" autocomplete="email" placeholder="Email" required>
<input type="password" id="password" name="password" placeholder="Password" pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*]).{8,}" required>
<input type="hidden" id="csrfToken" value="<?=$csrfToken;?>">
<button type="submit" id="registerSubmit">Submit</button>
</form>
</main>
<script src="index.js"></script>
</body>
</html>
这是index.js
const registerForm = document.getElementById("registerForm");
registerForm.addEventListener("submit", function(e) {
e.preventDefault();
const firstName = document.getElementById("firstName").value;
const lastName = document.getElementById("lastName").value;
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
const csrfToken = document.getElementById("csrfToken").value;
const registerFormData = {
"firstName":firstName,
"lastName":lastName,
"email":email,
"password":password,
"csrfToken":csrfToken
};
const ajax = new XMLHttpRequest();
ajax.open("POST", "index.php");
ajax.setRequestHeader("Content-Type", "application/json");
ajax.withCredentials = true;
ajax.send(JSON.stringify(registerFormData));
ajax.onload = function() {
console.log(this.response);
}
}, false);
这是EditSession.php
<?php
declare(strict_types=1);
class EditSession {
private $firstPartOfNewId;
private $secondPartOfNewId;
private $thirdPartOfNewId;
private $newSID;
public $secureKey = "";
// Create new session ID
function create_new_session_id() : void {
if (isset($_SESSION)) {
$firstPartOfNewId = str_replace(array(".", ":"), "", $_SERVER["REMOTE_ADDR"]);
$secondPartOfNewId = round(microtime(true) * 1000);
$thirdPartOfNewId = hash("sha512", random_bytes(64));
$newSID = $firstPartOfNewId.$secondPartOfNewId.$thirdPartOfNewId;
session_id($newSID);
} else {
throw new Exception("Session is not set");
}
}
// Store a value in a set session
function store_in_session(string $key,string $value) : void {
if (!isset($_SESSION)) {
throw new Exception("Session is not set.");
}
if (!isset($_SESSION[$key])) {
$_SESSION[$key] = $value;
}
}
// Store a value in a set session
function store_secure_key_in_session(string $key) : string {
if (!isset($_SESSION)) {
throw new Exception("Session is not set.");
}
if (!isset($_SESSION[$key])) {
$secureKey = hash("sha512", random_bytes(64));
$_SESSION[$key] = $secureKey;
return $secureKey;
} else {
return $secureKey;
}
}
// Unsetting variable associated with the $key
function unset_session_variable(string $key) : void {
if (isset($_SESSION)) {
$_SESSION[$key] = "";
unset($_SESSION[$key]);
} else {
throw new Exception("Session with key is not set.");
}
}
// Getting associated key from session
function get_from_session(string $key) : string {
if (isset($_SESSION[$key])) {
return $_SESSION[$key];
} elseif (isset($_SESSION)) {
throw new Exception("Session is set, but the key passed is not set in the session.");
} else {
throw new Exception("Session is not set.");
}
}
}
?>
这是ServerValidation.php
<?php
declare(strict_types=1);
class ServerValidation {
// Handles navigation to website
function navigateToWebsite(string $page, string $pageTitle, string $csrfToken) : void {
if (empty($_POST) && empty($_GET) && empty(file_get_contents("php://input"))) {
$csrfToken = $csrfToken;
$pageTitle = $pageTitle;
include_once $page;
exit;
}
}
// Checks if the website is served over https or its localhost
function checkHttps() : void {
if ($_SERVER["REQUEST_SCHEME"] !== "https" && $_SERVER["HTTP_HOST"] !== "localhost") {
throw new Exception("Not served over https");
}
}
// Checks if the content type is what it should be
function checkContentType(string $type) : void {
if ($_SERVER["CONTENT_TYPE"] !== $type) {
throw new Exception("Wrong content-type");
}
}
// Checks request method
function checkRequestType(string $type) : void {
if ($_SERVER["REQUEST_METHOD"] !== $type) {
throw new Exception("Wrong request method");
}
}
function checkToken(string $tokenFromFrontend, string $tokenFromSession) : void {
if ($tokenFromSession !== $tokenFromFrontend) {
throw new Exception("Tokens not matching up, there is a problem!!");
}
}
}
现在,这是代码发生的情况。当用户提交注册表单,并使用AJAX从表单中的隐藏值中获取csrfToken时,我在程序中抛出了异常(特别是从ServerValidation类的checkToken()
方法中抛出),该异常显示为“令牌不匹配”。我已经证实了这是为什么。如果我在调用session_start()之后立即var_dump $ _SESSION,则始终为空。它是否已经初始化(由用户首先导航到页面)无关紧要,它始终为空。因此,按照程序的逻辑,它将为CSRFTOKEN插入一个新值,因此它们当然不匹配。我绝对很沮丧,并且一直在解决这个问题一个星期。以下是一些我会问到的问题的答案:
1.我正在使用运行XAMPP和php 7.2的macbook
2.我的cookie设置为http_only,但不是secure_only。其他所有cookie参数均为默认设置。
3.我的会话数据(/ Applications / XAMPP / xamppfiles / temp /)的存储文件权限为777(我只想做我知道会做的事情)
4.如果不创建新的php session_id,会发生什么?将会发生相同的结果...即,会话仍将始终初始化为空
5.我可以在程序的不同点上var_dump会话吗?当然...这里有不同的地方及其结果:
-在session_start之后立即被调用:
-用户最初导航至页面时:一个空会话。
-用户提交注册表格后:空会话
-在$csrfToken = $sessionToEdit->store_secure_key_in_session("CSRFTOKEN");
被调用之后:
-当用户最初导航至页面时:返回带有CSRFTOKEN键的会话
-用户提交注册表后:将返回带有CSRFTOKEN密钥的会话(但该值与用户最初导航至页面时的值不同)
-就在$validatingServer->navigateToWebsite("register.php", "Register", $csrfToken);
之后
-用户最初导航至页面时:什么都没有,因为该程序由于退出而不能走得太远。
-用户提交注册表后:返回带有CSRFTOKEN密钥的会话(但该值不同于用户最初导航至页面时的值,并且该值不同于注册表中隐藏字段中的值) )
这是一个问题:为什么我的会话值总是初始化为空,即使我在同一个域中,我的所有工作都在同一个文件中,我的会话参数设置正确,并且我的文件权限设置了对吧?