Fix totp_setup.php to use header-based CSRF token verification

This commit is contained in:
Ryan
2025-04-11 23:40:27 -04:00
committed by GitHub
parent b06c49f213
commit 28ac23c2f6
3 changed files with 82 additions and 29 deletions

View File

@@ -14,6 +14,7 @@
- Updated TOTP integration (namespace, enum, QR provider) accordingly
- Updated docker image from 22.04 to 24.04 <https://github.com/error311/filerise-docker>
- Ensure consistent session behavior
- Fix totp_setup.php to use header-based CSRF token verification
---

View File

@@ -329,7 +329,8 @@ export function openTOTPModal() {
<span id="closeTOTPModal" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px;">&times;</span>
<h3>TOTP Setup</h3>
<p>Scan this QR code with your authenticator app:</p>
<img src="totp_setup.php?csrf=${encodeURIComponent(window.csrfToken)}" alt="TOTP QR Code" style="max-width: 100%; height: auto; display: block; margin: 0 auto;">
<!-- Create an image placeholder without the CSRF token in the src -->
<img id="totpQRCodeImage" src="" alt="TOTP QR Code" style="max-width: 100%; height: auto; display: block; margin: 0 auto;">
<br/>
<p>Enter the 6-digit code from your app to confirm setup:</p>
<input type="text" id="totpConfirmInput" maxlength="6" style="font-size:24px; text-align:center; width:100%; padding:10px;" placeholder="6-digit code" />
@@ -338,6 +339,7 @@ export function openTOTPModal() {
</div>
`;
document.body.appendChild(totpModal);
loadTOTPQRCode();
document.getElementById("closeTOTPModal").addEventListener("click", () => {
closeTOTPModal(true);
@@ -406,6 +408,13 @@ export function openTOTPModal() {
modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff";
modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000";
// Clear any previous QR code src if needed and then load it:
const qrImg = document.getElementById("totpQRCodeImage");
if (qrImg) {
qrImg.src = "";
}
loadTOTPQRCode();
// Focus the input and attach enter key listener
const totpConfirmInput = document.getElementById("totpConfirmInput");
if (totpConfirmInput) {
@@ -419,6 +428,33 @@ export function openTOTPModal() {
}
}
function loadTOTPQRCode() {
fetch("totp_setup.php", {
method: "GET",
credentials: "include",
headers: {
"X-CSRF-Token": window.csrfToken // Send your CSRF token here
}
})
.then(response => {
if (!response.ok) {
throw new Error("Failed to fetch QR code. Status: " + response.status);
}
return response.blob();
})
.then(blob => {
const imageURL = URL.createObjectURL(blob);
const qrImg = document.getElementById("totpQRCodeImage");
if (qrImg) {
qrImg.src = imageURL;
}
})
.catch(error => {
console.error("Error loading TOTP QR code:", error);
showToast("Error loading QR code.");
});
}
// Updated closeTOTPModal function with a disable parameter
export function closeTOTPModal(disable = true) {
const totpModal = document.getElementById("totpModal");

View File

@@ -9,16 +9,34 @@ use Endroid\QrCode\Writer\PngWriter;
use RobThree\Auth\Algorithm;
use RobThree\Auth\Providers\Qr\GoogleChartsQrCodeProvider;
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
// Define the respond() helper if not already defined.
if (!function_exists('respond')) {
function respond($status, $code, $message, $data = []) {
http_response_code($code);
echo json_encode([
'status' => $status,
'code' => $code,
'message' => $message,
'data' => $data
]);
exit;
}
}
// Allow access if the user is authenticated or pending TOTP.
if (!((isset($_SESSION['authenticated']) && $_SESSION['authenticated'] === true) || isset($_SESSION['pending_login_user']))) {
http_response_code(403);
exit;
}
// Retrieve CSRF token from GET parameter or request headers.
$headers = array_change_key_case(getallheaders(), CASE_LOWER);
$csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : '';
$receivedToken = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : '';
if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) {
respond('error', 403, 'Invalid CSRF token');
if ($receivedToken !== $_SESSION['csrf_token']) {
echo json_encode(["error" => "Invalid CSRF token"]);
http_response_code(403);
exit;
}
$username = $_SESSION['username'] ?? '';
@@ -111,7 +129,7 @@ $tfa = new \RobThree\Auth\TwoFactorAuth(
'FileRise', // issuer
6, // number of digits
30, // period in seconds
Algorithm::Sha1 // Correct enum case name from your enum
Algorithm::Sha1 // enum case from your Algorithm enum
);
// Retrieve the current TOTP secret for the user.
@@ -124,8 +142,6 @@ if (!$totpSecret) {
}
// Determine the otpauth URL to use.
// If a global OTPAuth URL template is defined, replace placeholders {label} and {secret}.
// Otherwise, use the default method.
$globalOtpauthUrl = getGlobalOtpauthUrl();
if (!empty($globalOtpauthUrl)) {
$label = "FileRise:" . $username;