From c8322d09c49a3d2ed9270fb93c0cf1c9fea2548c Mon Sep 17 00:00:00 2001
From: "Florine W. Dekker"
Date: Fri, 12 Aug 2022 17:06:21 +0200
Subject: [PATCH] Improve CSRF handling and session management
---
src/main/api.php | 171 +++++++++++++++++++++++++++++++-------------
src/main/index.php | 11 ---
src/main/js/Main.ts | 44 +++++++-----
tsconfig.json | 3 +-
4 files changed, 149 insertions(+), 80 deletions(-)
diff --git a/src/main/api.php b/src/main/api.php
index 1e6a1ee..e8f512c 100644
--- a/src/main/api.php
+++ b/src/main/api.php
@@ -34,59 +34,77 @@ if (!file_exists($config["database"]["filename"])) {
// Start session
session_start();
+if (!isset($_SESSION["token"])) {
+ $_SESSION["token"] = bin2hex(random_bytes(32));
+}
// Read JSON from POST, if it's there
if (empty($_POST)) {
$_POST = json_decode(file_get_contents("php://input"), associative: true);
}
+$response = array();
+$response["satisfied"] = false;
+$response["token"] = $_SESSION["token"];
+
if (isset($_POST["action"])) {
// Process POST
switch ($_POST["action"]) {
case "register":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
if (isset($_SESSION["uuid"])) {
- exit("\"already logged in\"");
+ $response["message"] = "already logged in";
+ break;
}
if (!isset($_POST["email"], $_POST["password"], $_POST["password_confirm"])) {
- exit("\"missing inputs\"");
+ $response["message"] = "missing inputs";
+ break;
}
if (!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
- exit("\"invalid email\"");
+ $response["message"] = "invalid email";
+ break;
}
if ($_POST["password"] !== $_POST["password_confirm"]) {
- exit("\"differing passwords\"");
+ $response["message"] = "differing passwords";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READONLY);
$email_is_in_use = $db->get_user_by_email($_POST["email"]) !== null;
$db->close();
if ($email_is_in_use) {
- exit("\"email already in use\"");
+ $response["message"] = "email already in use";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READWRITE);
try {
$uuid = bin2hex(random_bytes(16));
- } catch (\Exception) {
- exit("false");
+ } catch (\Exception $exception) {
+ $response["message"] = "unknown database error";
+ break;
}
$db->add_user($uuid, $_POST["email"], $_POST["password"]);
$db->close();
- exit("true");
+
+ $response["satisfied"] = true;
+ break;
case "login":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
if (!isset($_POST["email"], $_POST["password"])) {
- exit("\"missing inputs\"");
+ $response["message"] = "missing inputs";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READWRITE);
@@ -94,58 +112,77 @@ if (isset($_POST["action"])) {
$db->close();
if ($user === null || !password_verify($_POST["password"], $user["password"])) {
- exit("\"wrong password\"");
+ $response["message"] = "wrong password";
+ break;
}
$_SESSION["uuid"] = $user["uuid"];
- exit("true");
+
+ $response["satisfied"] = true;
+ break;
case "logout":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
session_destroy();
- exit("true");
+ session_start();
+ $_SESSION["token"] = bin2hex(random_bytes(32));
+
+ $response["satisfied"] = true;
+ $response["token"] = $_SESSION["token"];
+ break;
case "user-update-email":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
if (!isset($_SESSION["uuid"])) {
- exit("\"not logged in\"");
+ $response["message"] = "not logged in";
+ break;
}
if (!isset($_POST["email"])) {
- exit("\"missing inputs\"");
+ $response["message"] = "missing inputs";
+ break;
}
if (!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
- exit("\"invalid email\"");
+ $response["message"] = "invalid email";
+ break;
}
// TODO: Check if user exists
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READONLY);
if ($db->get_user_by_email($_POST["email"]) !== null) {
- exit("\"email already in use\"");
+ $response["message"] = "email already in use";
+ break;
}
$db->close();
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READWRITE);
$db->set_user_email($_SESSION["uuid"], $_POST["email"]);
$db->close();
- exit("\"true\"");
+
+ $response["satisfied"] = "true";
+ break;
case "user-update-password":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
if (!isset($_SESSION["uuid"])) {
- exit("\"not logged in\"");
+ $response["message"] = "not logged in";
+ break;
}
if (!isset($_POST["password_old"], $_POST["password_new"], $_POST["password_confirm"])) {
- exit("\"missing inputs\"");
+ $response["message"] = "missing inputs";
+ break;
}
// TODO: Check if user exists
@@ -155,24 +192,30 @@ if (isset($_POST["action"])) {
$db->close();
if ($user === null || !password_verify($_POST["password_old"], $user["password"])) {
- exit("\"wrong password\"");
+ $response["message"] = "wrong password";
+ break;
}
if ($_POST["password_new"] !== $_POST["password_confirm"]) {
- exit("\"differing passwords\"");
+ $response["message"] = "differing passwords";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READWRITE);
$db->set_user_password($_SESSION["uuid"], $_POST["password"]);
$db->close();
- exit("true");
+
+ $response["satisfied"] = true;
+ break;
case "user-delete":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
if (!isset($_SESSION["uuid"])) {
- exit("\"not logged in\"");
+ $response["message"] = "not logged in";
+ break;
}
// TODO: Check if user exists
@@ -182,43 +225,52 @@ if (isset($_POST["action"])) {
$db->close();
session_destroy();
- exit("true");
+ $response["satisfied"] = true;
+ break;
case "add-tracking":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
if (!isset($_SESSION["uuid"])) {
- exit("\"not logged in\"");
+ $response["message"] = "not logged in";
+ break;
}
if (!isset($_POST["person_name"])) {
- exit("\"missing inputs\"");
+ $response["message"] = "missing inputs";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READONLY);
$tracking_already_exists = $db->has_tracking($_SESSION["uuid"], $_POST["person_name"]);
$db->close();
if ($tracking_already_exists) {
- exit("\"tracking already exists\"");
+ $response["message"] = "tracking already exists";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READWRITE);
$db->add_tracking($_SESSION["uuid"], $_POST["person_name"]);
$db->close();
- exit("true");
+ $response["satisfied"] = true;
+ break;
case "delete-tracking":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
if (!isset($_SESSION["uuid"])) {
- exit("\"not logged in\"");
+ $response["message"] = "not logged in";
+ break;
}
if (!isset($_POST["person_name"])) {
- exit("\"missing inputs\"");
+ $response["message"] = "missing inputs";
+ break;
}
// TODO: Check if tracking exists
@@ -227,10 +279,12 @@ if (isset($_POST["action"])) {
$db->remove_tracking($_SESSION["uuid"], $_POST["person_name"]);
$db->close();
- exit("true");
+ $response["satisfied"] = true;
+ break;
case "send-test-email":
if (!isset($_POST["token"]) || !isset($_SESSION["token"]) || $_POST["token"] !== $_SESSION["token"]) {
- exit("\"no token, or invalid token\"");
+ $response["message"] = "no token, or invalid token";
+ break;
}
// TODO: Send this to logged-in user
@@ -250,7 +304,8 @@ if (isset($_POST["action"])) {
$mail->setFrom($config["mail"]["username"], $config["mail"]["from_name"]);
$mail->addAddress($config["mail"]["to_address_test"]);
} catch (Exception) {
- exit("false");
+ $response["message"] = "unknown mail error occurred";
+ break;
}
$mail->Subject = "Test mail";
@@ -259,37 +314,55 @@ if (isset($_POST["action"])) {
try {
$mail->send();
} catch (Exception) {
- exit("false");
+ $response["message"] = "unknown mail error occurred";
+ break;
}
- exit("true");
+ $response["satisfied"] = true;
+ break;
+ default:
+ $response["message"] = "unknown POST action '" . $_POST["action"] . "'";
+ break;
}
} else if (isset($_GET["action"])) {
// Process GET
switch ($_GET["action"]) {
case "get-user-data":
if (!isset($_SESSION["uuid"])) {
- exit("\"not logged in\"");
+ $response["message"] = "not logged in";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READONLY);
$user_data = $db->get_user_by_uuid($_SESSION["uuid"]);
$db->close();
- exit(json_encode($user_data));
+ $response["message"] = $user_data;
+ $response["satisfied"] = true;
+ break;
case "list-trackings":
if (!isset($_SESSION["uuid"])) {
- exit("\"not logged in\"");
+ $response["message"] = "not logged in";
+ break;
}
$db = new Database($config["database"]["filename"], SQLITE3_OPEN_READONLY);
$trackings = $db->list_trackings($_SESSION["uuid"]);
$db->close();
- exit(json_encode($trackings));
+ $response["message"] = $trackings;
+ $response["satisfied"] = true;
+ break;
case "is-alive":
- exit(json_encode((new MyMediawiki())->people_are_alive(array("Janelle Monáe", "John Malkovich", "Adolf Hitler"))));
+ $response["message"] = ((new MyMediawiki())->people_are_alive(array("Janelle Monáe", "John Malkovich", "Adolf Hitler")));
+ $response["satisfied"] = true;
+ break;
+ default:
+ $response["message"] = "unknown GET action '" . $_GET["action"] . "'";
+ break;
}
+} else {
+ $response["message"] = "unknown method";
}
-exit("\"unknown action\"");
+exit(json_encode($response));
diff --git a/src/main/index.php b/src/main/index.php
index 0f5ad21..fe0947a 100644
--- a/src/main/index.php
+++ b/src/main/index.php
@@ -1,9 +1,3 @@
-
-
-
@@ -45,7 +39,6 @@ $_SESSION["token"] = bin2hex(random_bytes(32));
Already have an account? Welcome back!