mirror of
https://github.com/ellite/Wallos.git
synced 2026-05-12 23:08:32 -05:00
csrf on profile
This commit is contained in:
@@ -1,13 +1,7 @@
|
||||
<?php
|
||||
|
||||
require_once '../../includes/connect_endpoint.php';
|
||||
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => translate('session_expired', $i18n)
|
||||
]));
|
||||
}
|
||||
require_once '../../includes/validate_endpoint.php';
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
if (isset($input['avatar'])) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
require_once '../../includes/connect_endpoint.php';
|
||||
require_once '../../includes/inputvalidation.php';
|
||||
require_once '../../includes/validate_endpoint.php';
|
||||
|
||||
if (!function_exists('trigger_deprecation')) {
|
||||
function trigger_deprecation($package, $version, $message, ...$args)
|
||||
@@ -12,15 +13,6 @@ if (!function_exists('trigger_deprecation')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => translate('session_expired', $i18n),
|
||||
"reload" => false
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
$statement = $db->prepare('SELECT totp_enabled FROM user WHERE id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$result = $statement->execute();
|
||||
@@ -34,43 +26,69 @@ if ($row['totp_enabled'] == 0) {
|
||||
]));
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$postData = file_get_contents("php://input");
|
||||
$data = json_decode($postData, true);
|
||||
$postData = file_get_contents("php://input");
|
||||
$data = json_decode($postData, true);
|
||||
|
||||
if (isset($data['totpCode']) && $data['totpCode'] != "") {
|
||||
require_once __DIR__ . '/../../libs/OTPHP/FactoryInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/Factory.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/ParameterTrait.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/OTPInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/OTP.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/TOTPInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/TOTP.php';
|
||||
require_once __DIR__ . '/../../libs/Psr/Clock/ClockInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/InternalClock.php';
|
||||
require_once __DIR__ . '/../../libs/constant_time_encoding/Binary.php';
|
||||
require_once __DIR__ . '/../../libs/constant_time_encoding/EncoderInterface.php';
|
||||
require_once __DIR__ . '/../../libs/constant_time_encoding/Base32.php';
|
||||
if (isset($data['totpCode']) && $data['totpCode'] != "") {
|
||||
require_once __DIR__ . '/../../libs/OTPHP/FactoryInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/Factory.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/ParameterTrait.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/OTPInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/OTP.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/TOTPInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/TOTP.php';
|
||||
require_once __DIR__ . '/../../libs/Psr/Clock/ClockInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/InternalClock.php';
|
||||
require_once __DIR__ . '/../../libs/constant_time_encoding/Binary.php';
|
||||
require_once __DIR__ . '/../../libs/constant_time_encoding/EncoderInterface.php';
|
||||
require_once __DIR__ . '/../../libs/constant_time_encoding/Base32.php';
|
||||
|
||||
$totp_code = $data['totpCode'];
|
||||
$totp_code = $data['totpCode'];
|
||||
|
||||
$statement = $db->prepare('SELECT totp_secret FROM totp WHERE user_id = :id');
|
||||
$statement = $db->prepare('SELECT totp_secret FROM totp WHERE user_id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$result = $statement->execute();
|
||||
$row = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$secret = $row['totp_secret'];
|
||||
|
||||
$statement = $db->prepare('SELECT backup_codes FROM totp WHERE user_id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$result = $statement->execute();
|
||||
$row = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$backupCodes = $row['backup_codes'];
|
||||
|
||||
$clock = new OTPHP\InternalClock();
|
||||
$totp = OTPHP\TOTP::createFromSecret($secret, $clock);
|
||||
$totp->setPeriod(30);
|
||||
|
||||
if ($totp->verify($totp_code, null, 15)) {
|
||||
$statement = $db->prepare('UPDATE user SET totp_enabled = 0 WHERE id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$result = $statement->execute();
|
||||
$row = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$secret = $row['totp_secret'];
|
||||
$statement->execute();
|
||||
|
||||
$statement = $db->prepare('SELECT backup_codes FROM totp WHERE user_id = :id');
|
||||
$statement = $db->prepare('DELETE FROM totp WHERE user_id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$result = $statement->execute();
|
||||
$row = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$backupCodes = $row['backup_codes'];
|
||||
$statement->execute();
|
||||
|
||||
$clock = new OTPHP\InternalClock();
|
||||
$totp = OTPHP\TOTP::createFromSecret($secret, $clock);
|
||||
$totp->setPeriod(30);
|
||||
die(json_encode([
|
||||
"success" => true,
|
||||
"message" => translate('success', $i18n),
|
||||
"reload" => true
|
||||
]));
|
||||
} else {
|
||||
// Compare the TOTP code agains the backup codes
|
||||
// Normalize TOTP input
|
||||
$totp_code = strtolower(trim((string) $totp_code));
|
||||
|
||||
if ($totp->verify($totp_code, null, 15)) {
|
||||
// Decode and normalize backup codes
|
||||
$backupCodes = json_decode($backupCodes, true);
|
||||
$normalizedBackupCodes = array_map(function ($code) {
|
||||
return strtolower(trim((string) $code));
|
||||
}, $backupCodes);
|
||||
|
||||
// Search for the normalized code
|
||||
if (($key = array_search($totp_code, $normalizedBackupCodes)) !== false) {
|
||||
// Match found, disable TOTP
|
||||
$statement = $db->prepare('UPDATE user SET totp_enabled = 0 WHERE id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$statement->execute();
|
||||
@@ -85,53 +103,19 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
"reload" => true
|
||||
]));
|
||||
} else {
|
||||
// Compare the TOTP code agains the backup codes
|
||||
// Normalize TOTP input
|
||||
$totp_code = strtolower(trim((string) $totp_code));
|
||||
|
||||
// Decode and normalize backup codes
|
||||
$backupCodes = json_decode($backupCodes, true);
|
||||
$normalizedBackupCodes = array_map(function ($code) {
|
||||
return strtolower(trim((string) $code));
|
||||
}, $backupCodes);
|
||||
|
||||
// Search for the normalized code
|
||||
if (($key = array_search($totp_code, $normalizedBackupCodes)) !== false) {
|
||||
// Match found, disable TOTP
|
||||
$statement = $db->prepare('UPDATE user SET totp_enabled = 0 WHERE id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$statement->execute();
|
||||
|
||||
$statement = $db->prepare('DELETE FROM totp WHERE user_id = :id');
|
||||
$statement->bindValue(':id', $userId, SQLITE3_INTEGER);
|
||||
$statement->execute();
|
||||
|
||||
die(json_encode([
|
||||
"success" => true,
|
||||
"message" => translate('success', $i18n),
|
||||
"reload" => true
|
||||
]));
|
||||
} else {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => translate('totp_code_incorrect', $i18n),
|
||||
"reload" => false
|
||||
]));
|
||||
}
|
||||
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => translate('totp_code_incorrect', $i18n),
|
||||
"reload" => false
|
||||
]));
|
||||
}
|
||||
|
||||
} else {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => translate('fields_missing', $i18n),
|
||||
"reload" => false
|
||||
]));
|
||||
}
|
||||
|
||||
} else {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => translate('invalid_request_method', $i18n),
|
||||
"message" => translate('fields_missing', $i18n),
|
||||
"reload" => false
|
||||
]));
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
require_once '../../includes/connect_endpoint.php';
|
||||
require_once '../../includes/inputvalidation.php';
|
||||
require_once '../../includes/validate_endpoint.php';
|
||||
|
||||
if (!function_exists('trigger_deprecation')) {
|
||||
function trigger_deprecation($package, $version, $message, ...$args)
|
||||
@@ -12,14 +13,13 @@ if (!function_exists('trigger_deprecation')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => translate('session_expired', $i18n)
|
||||
]));
|
||||
}
|
||||
$postData = file_get_contents("php://input");
|
||||
$data = json_decode($postData, true) ?? [];
|
||||
|
||||
$action = $data['action'] ?? '';
|
||||
|
||||
if ($action === 'generate') {
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "GET") {
|
||||
function base32_encode($hex)
|
||||
{
|
||||
$alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||
@@ -39,23 +39,19 @@ if ($_SERVER["REQUEST_METHOD"] === "GET") {
|
||||
return $base32;
|
||||
}
|
||||
|
||||
$data = $_GET;
|
||||
if (isset($data['generate']) && $data['generate'] == true) {
|
||||
$secret = base32_encode(bin2hex(random_bytes(20)));
|
||||
$qrCodeUrl = "otpauth://totp/Wallos:" . $_SESSION['username'] . "?secret=" . $secret . "&issuer=Wallos";
|
||||
$response = [
|
||||
"success" => true,
|
||||
"secret" => $secret,
|
||||
"qrCodeUrl" => $qrCodeUrl
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
$secret = base32_encode(bin2hex(random_bytes(20)));
|
||||
$qrCodeUrl = "otpauth://totp/Wallos:" . $_SESSION['username'] . "?secret=" . $secret . "&issuer=Wallos";
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"secret" => $secret,
|
||||
"qrCodeUrl" => $qrCodeUrl,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$postData = file_get_contents("php://input");
|
||||
$data = json_decode($postData, true);
|
||||
|
||||
if ($action === 'verify') {
|
||||
if (isset($data['totpSecret']) && $data['totpSecret'] != "" && isset($data['totpCode']) && $data['totpCode'] != "") {
|
||||
require_once __DIR__ . '/../../libs/OTPHP/FactoryInterface.php';
|
||||
require_once __DIR__ . '/../../libs/OTPHP/Factory.php';
|
||||
@@ -134,8 +130,4 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
"message" => translate('totp_code_incorrect', $i18n)
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,40 +1,29 @@
|
||||
<?php
|
||||
require_once '../../includes/connect_endpoint.php';
|
||||
require_once '../../includes/validate_endpoint.php';
|
||||
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
die(json_encode([
|
||||
$postData = file_get_contents("php://input");
|
||||
$data = json_decode($postData, true);
|
||||
|
||||
$apiKey = bin2hex(random_bytes(32));
|
||||
|
||||
$sql = "UPDATE user SET api_key = :apiKey WHERE id = :userId";
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->bindValue(':apiKey', $apiKey, SQLITE3_TEXT);
|
||||
$stmt->bindValue(':userId', $userId, SQLITE3_TEXT);
|
||||
$result = $stmt->execute();
|
||||
|
||||
if ($result) {
|
||||
$response = [
|
||||
"success" => true,
|
||||
"message" => translate('user_details_saved', $i18n),
|
||||
"apiKey" => $apiKey
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => translate('session_expired', $i18n)
|
||||
]));
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$postData = file_get_contents("php://input");
|
||||
$data = json_decode($postData, true);
|
||||
|
||||
$apiKey = bin2hex(random_bytes(32));
|
||||
|
||||
$sql = "UPDATE user SET api_key = :apiKey WHERE id = :userId";
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->bindValue(':apiKey', $apiKey, SQLITE3_TEXT);
|
||||
$stmt->bindValue(':userId', $userId, SQLITE3_TEXT);
|
||||
$result = $stmt->execute();
|
||||
|
||||
if ($result) {
|
||||
$response = [
|
||||
"success" => true,
|
||||
"message" => translate('user_details_saved', $i18n),
|
||||
"apiKey" => $apiKey
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => translate('error_updating_user_data', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
"message" => translate('error_updating_user_data', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
require_once '../../includes/connect_endpoint.php';
|
||||
require_once '../../includes/inputvalidation.php';
|
||||
require_once '../../includes/validate_endpoint.php';
|
||||
|
||||
if (!file_exists('../../images/uploads/logos')) {
|
||||
mkdir('../../images/uploads/logos', 0777, true);
|
||||
@@ -343,4 +344,3 @@ if (
|
||||
echo json_encode($response);
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
|
||||
+70
-45
@@ -2,29 +2,40 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
document.getElementById("userForm").addEventListener("submit", function (event) {
|
||||
event.preventDefault();
|
||||
document.getElementById("userSubmit").disabled = true;
|
||||
const submitButton = document.getElementById("userSubmit");
|
||||
submitButton.disabled = true;
|
||||
|
||||
const formData = new FormData(event.target);
|
||||
formData.append("action", "save");
|
||||
|
||||
fetch("endpoints/user/save_user.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
headers: {
|
||||
"X-CSRF-Token": window.csrfToken,
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById("avatar").src = document.getElementById("avatarImg").src;
|
||||
var newUsername = document.getElementById("username").value;
|
||||
const newUsername = document.getElementById("username").value;
|
||||
document.getElementById("user").textContent = newUsername;
|
||||
showSuccessMessage(data.message);
|
||||
|
||||
if (data.reload) {
|
||||
location.reload();
|
||||
}
|
||||
} else {
|
||||
showErrorMessage(data.errorMessage);
|
||||
showErrorMessage(data.errorMessage || translate("failed_save_user"));
|
||||
}
|
||||
document.getElementById("userSubmit").disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage(translate('unknown_error'));
|
||||
console.error(error);
|
||||
showErrorMessage(translate("unknown_error"));
|
||||
})
|
||||
.finally(() => {
|
||||
submitButton.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,6 +92,7 @@ function deleteAvatar(path) {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"X-CSRF-Token": window.csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ avatar: path }),
|
||||
})
|
||||
@@ -102,31 +114,36 @@ function deleteAvatar(path) {
|
||||
}
|
||||
|
||||
function enableTotp() {
|
||||
const totpSecret = document.querySelector('#totp-secret');
|
||||
const totpSecretCode = document.querySelector('#totp-secret-code');
|
||||
const qrCode = document.getElementById('totp-qr-code');
|
||||
totpSecret.value = '';
|
||||
totpSecretCode.textContent = '';
|
||||
qrCode.innerHTML = '';
|
||||
const totpSecret = document.querySelector("#totp-secret");
|
||||
const totpSecretCode = document.querySelector("#totp-secret-code");
|
||||
const qrCode = document.getElementById("totp-qr-code");
|
||||
totpSecret.value = "";
|
||||
totpSecretCode.textContent = "";
|
||||
qrCode.innerHTML = "";
|
||||
|
||||
fetch('endpoints/user/enable_totp.php?generate=true', {
|
||||
method: 'GET'
|
||||
fetch("endpoints/user/enable_totp.php", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": window.csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ action: "generate" }),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
totpSecret.value = data.secret;
|
||||
totpSecretCode.textContent = data.secret;
|
||||
new QRCode(qrCode, data.qrCodeUrl);
|
||||
openTotpPopup();
|
||||
} else {
|
||||
showErrorMessage(data.message);
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
totpSecret.value = data.secret;
|
||||
totpSecretCode.textContent = data.secret;
|
||||
new QRCode(qrCode, data.qrCodeUrl);
|
||||
|
||||
openTotpPopup();
|
||||
} else {
|
||||
showErrorMessage(data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage(error);
|
||||
});
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
showErrorMessage(translate("unknown_error"));
|
||||
});
|
||||
}
|
||||
|
||||
function openTotpPopup() {
|
||||
@@ -157,8 +174,9 @@ function submitTotp() {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"X-CSRF-Token": window.csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ totpCode: totpCode, totpSecret: totpSecret }),
|
||||
body: JSON.stringify({ totpCode: totpCode, totpSecret: totpSecret, action: 'verify' }),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
@@ -233,6 +251,7 @@ function submitDisableTotp() {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"X-CSRF-Token": window.csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ totpCode: totpCode }),
|
||||
})
|
||||
@@ -253,29 +272,34 @@ function submitDisableTotp() {
|
||||
}
|
||||
|
||||
function regenerateApiKey() {
|
||||
const regenerateButton = document.getElementById('regenerateApiKey');
|
||||
regenerateButton.disabled = true;
|
||||
const regenerateButton = document.getElementById("regenerateApiKey");
|
||||
regenerateButton.disabled = true;
|
||||
|
||||
fetch('endpoints/user/regenerateapikey.php', {
|
||||
method: 'POST',
|
||||
})
|
||||
fetch("endpoints/user/regenerateapikey.php", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-Token": window.csrfToken,
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
regenerateButton.disabled = false;
|
||||
if (data.success) {
|
||||
const newApiKey = data.apiKey;
|
||||
document.getElementById('apikey').value = newApiKey;
|
||||
showSuccessMessage(data.message);
|
||||
} else {
|
||||
showErrorMessage(data.message);
|
||||
}
|
||||
regenerateButton.disabled = false;
|
||||
if (data.success) {
|
||||
const newApiKey = data.apiKey;
|
||||
document.getElementById("apikey").value = newApiKey;
|
||||
showSuccessMessage(data.message);
|
||||
} else {
|
||||
showErrorMessage(data.message || translate("failed_regenerate_api_key"));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
regenerateButton.disabled = false;
|
||||
showErrorMessage(error);
|
||||
console.error(error);
|
||||
regenerateButton.disabled = false;
|
||||
showErrorMessage(translate("unknown_error"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function exportAsJson() {
|
||||
fetch("endpoints/subscriptions/export.php")
|
||||
.then(response => response.json())
|
||||
@@ -337,6 +361,7 @@ function deleteAccount(userId) {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"X-CSRF-Token": window.csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ userId: userId }),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user