Security hardening: CSRF, rate limiting, session/password policy, audit log
Fixes critical pre-existing issues found during review: bulk_action.php had no auth check at all (unauthenticated download/delete of any qrcode) and built a table name from unwhitelisted user input (SQL injection); the QR generator classes wrote files from unvalidated filename/format, allowing path traversal and arbitrary file writes. Also pins chillerlan/php-qrcode to 5.0.5 since master now requires PHP 8.4, breaking the PHP 8.3 build. - CSRF tokens on all POST forms and the bulk_action.php JSON endpoint - Login rate limiting (5 attempts / 15 min) via new login_attempts table - Hardened sessions: httponly/samesite cookies, 30 min idle timeout, session regeneration on login - Forced password change for the default superadmin/superadmin account - Server-side validation in Users/DynamicQrcode/Qrcode classes - Audit log table for auth, user, and qrcode actions - Checked-in db schema (db/init.sql, migrations/) instead of relying on an opaque prebuilt db image - Production docker-compose with Nginx + php-fpm instead of the PHP dev server Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+57
-34
@@ -1,66 +1,89 @@
|
||||
<?php
|
||||
|
||||
|
||||
require_once 'config/config.php';
|
||||
session_start();
|
||||
require_once 'includes/bootstrap.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST')
|
||||
{
|
||||
csrf_verify_or_die();
|
||||
|
||||
$username = filter_input(INPUT_POST, 'username');
|
||||
$password = filter_input(INPUT_POST, 'password');
|
||||
$remember = filter_input(INPUT_POST, 'remember');
|
||||
|
||||
if (!$username || !$password) {
|
||||
$_SESSION['login_failure'] = 'Invalid username or password';
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (qr_is_login_locked_out($username)) {
|
||||
$_SESSION['login_failure'] = 'Too many failed login attempts. Try again in 15 minutes.';
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get DB instance.
|
||||
$db = getDbInstance();
|
||||
|
||||
$db->where('username', $username);
|
||||
$row = $db->getOne('users');
|
||||
|
||||
if ($db->count >= 1)
|
||||
if ($db->count >= 1 && password_verify($password, $row['password']))
|
||||
{
|
||||
$db_password = $row['password'];
|
||||
qr_record_login_attempt($username, true);
|
||||
|
||||
// Voorkom session fixation: nieuwe sessie-id na een geslaagde login.
|
||||
session_regenerate_id(true);
|
||||
|
||||
$_SESSION['user_logged_in'] = TRUE;
|
||||
$_SESSION['type'] = $row['type'];
|
||||
$_SESSION['user_id'] = $row['id'];
|
||||
$_SESSION['username'] = $row['username'];
|
||||
$_SESSION['must_change_password'] = !empty($row['must_change_password']);
|
||||
$_SESSION['last_activity'] = time();
|
||||
|
||||
audit_log('login_success');
|
||||
|
||||
$user_id = $row['id'];
|
||||
|
||||
if (password_verify($password, $db_password))
|
||||
if ($remember)
|
||||
{
|
||||
$_SESSION['user_logged_in'] = TRUE;
|
||||
$_SESSION['type'] = $row['type'];
|
||||
$_SESSION['user_id'] = $row['id'];
|
||||
$series_id = randomString(16);
|
||||
$remember_token = getSecureRandomToken(20);
|
||||
$encryted_remember_token = password_hash($remember_token,PASSWORD_DEFAULT);
|
||||
|
||||
if ($remember)
|
||||
{
|
||||
$series_id = randomString(16);
|
||||
$remember_token = getSecureRandomToken(20);
|
||||
$encryted_remember_token = password_hash($remember_token,PASSWORD_DEFAULT);
|
||||
$expiry_time = date('Y-m-d H:i:s', strtotime(' + 30 days'));
|
||||
$expires = strtotime($expiry_time);
|
||||
$is_https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
|
||||
$expiry_time = date('Y-m-d H:i:s', strtotime(' + 30 days'));
|
||||
$expires = strtotime($expiry_time);
|
||||
$cookie_options = [
|
||||
'expires' => $expires,
|
||||
'path' => '/',
|
||||
'secure' => $is_https,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
];
|
||||
|
||||
setcookie('series_id', $series_id, $expires, '/');
|
||||
setcookie('remember_token', $remember_token, $expires, '/');
|
||||
setcookie('series_id', $series_id, $cookie_options);
|
||||
setcookie('remember_token', $remember_token, $cookie_options);
|
||||
|
||||
$db = getDbInstance();
|
||||
$db->where ('id',$user_id);
|
||||
$db = getDbInstance();
|
||||
$db->where ('id',$user_id);
|
||||
|
||||
$update_remember = array(
|
||||
'series_id'=> $series_id,
|
||||
'remember_token' => $encryted_remember_token,
|
||||
'expires' =>$expiry_time
|
||||
);
|
||||
$db->update('users', $update_remember);
|
||||
}
|
||||
// Authentication successfull redirect user
|
||||
header('Location: index.php');
|
||||
}
|
||||
else
|
||||
{
|
||||
$_SESSION['login_failure'] = 'Invalid username or password';
|
||||
header('Location: login.php');
|
||||
$update_remember = array(
|
||||
'series_id'=> $series_id,
|
||||
'remember_token' => $encryted_remember_token,
|
||||
'expires' =>$expiry_time
|
||||
);
|
||||
$db->update('users', $update_remember);
|
||||
}
|
||||
// Authentication successfull redirect user
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
qr_record_login_attempt($username, false);
|
||||
$_SESSION['login_failure'] = 'Invalid username or password';
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
|
||||
+31
-17
@@ -1,9 +1,14 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once BASE_PATH . '/includes/auth_validate.php';
|
||||
require_once BASE_PATH . '/lib/DynamicQrcode/DynamicQrcode.php';
|
||||
require_once BASE_PATH . '/lib/StaticQrcode/StaticQrcode.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
csrf_verify_header_or_die();
|
||||
|
||||
$allowed_types = ['dynamic', 'static'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$db = getDbInstance();
|
||||
$json = json_decode(file_get_contents('php://input'), true);
|
||||
@@ -12,8 +17,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$params = $json['params'];
|
||||
$files = [];
|
||||
|
||||
if (isset($json['type'])) {
|
||||
$type = filter_var($json['type'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||
if (isset($json['type']) && in_array($json['type'], $allowed_types, true)) {
|
||||
$type = $json['type'];
|
||||
} else {
|
||||
echo json_encode([
|
||||
'data' => 'Type action field in the request.',
|
||||
@@ -31,9 +36,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
foreach ($params as $param) {
|
||||
$row = $db->where('id', $param);
|
||||
$db->where('id', $param);
|
||||
if ($_SESSION['type'] !== 'super') {
|
||||
$db->where('id_owner', $_SESSION['user_id']);
|
||||
$db->orWhere('id_owner', NULL, 'IS');
|
||||
}
|
||||
$row = $db->getOne("{$type}_qrcodes");
|
||||
@$files[] = SAVED_QRCODE_FOLDER . $row['qrcode'];
|
||||
if ($row !== NULL) {
|
||||
$files[] = SAVED_QRCODE_FOLDER . $row['qrcode'];
|
||||
}
|
||||
}
|
||||
|
||||
$zip = new ZipArchive();
|
||||
@@ -50,6 +61,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
$zip->close();
|
||||
|
||||
audit_log('bulk_download', $type, implode(',', $params));
|
||||
|
||||
echo json_encode([
|
||||
'data' => $url_path,
|
||||
'status' => 200
|
||||
@@ -57,10 +70,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
exit();
|
||||
} else if($json["action"] == "delete") {
|
||||
$params = $json['params'];
|
||||
$files = [];
|
||||
|
||||
if (isset($json['type'])) {
|
||||
$type = filter_var($json['type'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||
if (isset($json['type']) && in_array($json['type'], $allowed_types, true)) {
|
||||
$type = $json['type'];
|
||||
} else {
|
||||
echo json_encode([
|
||||
'data' => 'Type action field in the request.',
|
||||
@@ -79,16 +91,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if($type == "dynamic")
|
||||
$instance = new DynamicQrcode();
|
||||
else if($type == "static")
|
||||
$instance = new StaticQrcode();
|
||||
else
|
||||
die("Type not allowed");
|
||||
$instance = new StaticQrcode();
|
||||
|
||||
foreach ($params as $param) {
|
||||
$a = 0;
|
||||
$instance->deleteQrcode($param, true);
|
||||
}
|
||||
|
||||
audit_log('bulk_delete', $type, implode(',', $params));
|
||||
|
||||
echo json_encode([
|
||||
'action' => "delete",
|
||||
'data' => "Qrcode deleted",
|
||||
@@ -96,9 +107,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
]);
|
||||
exit();
|
||||
|
||||
} else
|
||||
exit("Action not allowed");
|
||||
} else {
|
||||
echo json_encode(['data' => 'Action not allowed', 'status' => 400]);
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
exit('Direct access to this script not allowed.');
|
||||
http_response_code(405);
|
||||
echo json_encode(['data' => 'Direct access to this script not allowed.', 'status' => 405]);
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
require_once 'includes/bootstrap.php';
|
||||
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$forced = !empty($_SESSION['must_change_password']);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
csrf_verify_or_die();
|
||||
|
||||
$current_password = $_POST['current_password'] ?? '';
|
||||
$new_password = $_POST['new_password'] ?? '';
|
||||
$confirm_password = $_POST['confirm_password'] ?? '';
|
||||
|
||||
$db = getDbInstance();
|
||||
$db->where('id', $_SESSION['user_id']);
|
||||
$user = $db->getOne('users');
|
||||
|
||||
if ($user === NULL || !password_verify($current_password, $user['password'])) {
|
||||
$_SESSION['failure'] = 'Current password is incorrect.';
|
||||
} elseif (strlen($new_password) < 10) {
|
||||
$_SESSION['failure'] = 'New password must be at least 10 characters long.';
|
||||
} elseif ($new_password !== $confirm_password) {
|
||||
$_SESSION['failure'] = 'New password and confirmation do not match.';
|
||||
} elseif ($new_password === $current_password) {
|
||||
$_SESSION['failure'] = 'New password must be different from the current password.';
|
||||
} else {
|
||||
$db = getDbInstance();
|
||||
$db->where('id', $_SESSION['user_id']);
|
||||
$db->update('users', [
|
||||
'password' => password_hash($new_password, PASSWORD_DEFAULT),
|
||||
'must_change_password' => 0,
|
||||
'password_changed_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
$_SESSION['must_change_password'] = false;
|
||||
audit_log('password_changed');
|
||||
|
||||
$_SESSION['success'] = 'Password updated successfully.';
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<title>Change password - Qrcode Generator</title>
|
||||
<?php include './includes/head.php'; ?>
|
||||
|
||||
<body class="login-page" style="min-height: 512.391px;">
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<img src="dist/img/DynamicQRCode_Original.png" style="width: 95%; height: 95%">
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body login-card-body">
|
||||
<p class="login-box-msg">
|
||||
<?php echo $forced
|
||||
? 'You must change your password before continuing.'
|
||||
: 'Change your password'; ?>
|
||||
</p>
|
||||
|
||||
<?php include './includes/flash_messages.php'; ?>
|
||||
|
||||
<form method="POST" action="change_password.php">
|
||||
<?php echo csrf_field(); ?>
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" name="current_password" class="form-control" placeholder="Current password" required="required" autocomplete="current-password">
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" name="new_password" class="form-control" placeholder="New password (min. 10 characters)" required="required" minlength="10" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" name="confirm_password" class="form-control" placeholder="Confirm new password" required="required" minlength="10" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-primary btn-block">Update password</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php if (!$forced): ?>
|
||||
<p class="mt-3 text-center"><a href="index.php">Back to dashboard</a></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../plugins/jquery/jquery.min.js"></script>
|
||||
<script src="../../plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="../../dist/js/adminlte.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+3
@@ -470,6 +470,9 @@
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
headers: {
|
||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.status == 200) {
|
||||
if(data["action"] === "download") {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once BASE_PATH.'/includes/auth_validate.php';
|
||||
require_once BASE_PATH . '/lib/DynamicQrcode/DynamicQrcode.php';
|
||||
|
||||
$dynamic_qrcode_instance = new DynamicQrcode();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
csrf_verify_or_die();
|
||||
}
|
||||
|
||||
$edit = false;
|
||||
if($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["edit"]) && $_GET["edit"] == "true" && isset($_GET["id"])) {
|
||||
$edit = true;
|
||||
@@ -83,6 +86,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"])) {
|
||||
<h3 class="card-title">Enter the requested data</h3>
|
||||
</div>
|
||||
<form class="form" action="" method="post" id="dynamic_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<div class="card-body">
|
||||
<?php
|
||||
if($edit)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once BASE_PATH . '/includes/auth_validate.php';
|
||||
require_once BASE_PATH . '/lib/DynamicQrcode/DynamicQrcode.php';
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<span class="input-group-text"><i class="fa fa-lock"></i></span>
|
||||
</div>
|
||||
|
||||
<input type="password" name="password" placeholder="Password" class="form-control" required="required" autocomplete="off">
|
||||
<input type="password" name="password" placeholder="<?php echo ($edit) ? 'Leave blank to keep current password' : 'Password'; ?>" class="form-control" <?php echo ($edit) ? '' : 'required="required"'; ?> minlength="10" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=2fa" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=bitcoin" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=bookmark" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=email" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=event" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-4">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=location" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-4">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=paypal" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=phone" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=skype" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-4">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=sms" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=text" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-6">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=vcard" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<!-- First row -->
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=whatsapp" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form class="form" action="static_qrcode.php?type=wifi" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
|
||||
<!-- Input forms -->
|
||||
<div class="col-sm-12 mb-2">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<div id="err-msg"></div>
|
||||
<div class="bulk-action-wrapper">
|
||||
<form id="bulk-action" action="bulk_action.php" method="POST">
|
||||
<?php echo csrf_field(); ?>
|
||||
<div class="col-sm-12 mb-2" style="margin-left: 10px">
|
||||
<div class="row">
|
||||
<div class="col-5 col-md-2">
|
||||
@@ -100,6 +101,7 @@
|
||||
<div class="modal fade" id="delete-modal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<form action="dynamic_qrcode.php" method="POST">
|
||||
<?php echo csrf_field(); ?>
|
||||
<!-- Modal content -->
|
||||
|
||||
<div class="modal-content">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<div id="err-msg"></div>
|
||||
<div class="bulk-action-wrapper">
|
||||
<form id="bulk-action" action="bulk_action.php" method="POST">
|
||||
<?php echo csrf_field(); ?>
|
||||
<div class="col-sm-12 mb-2" style="margin-left: 10px">
|
||||
<div class="row">
|
||||
<div class="col-5 col-md-2">
|
||||
@@ -96,6 +97,7 @@
|
||||
<div class="modal fade" id="delete-modal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<form action="static_qrcode.php" method="POST">
|
||||
<?php echo csrf_field(); ?>
|
||||
<!-- Modal content -->
|
||||
|
||||
<div class="modal-content">
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<div class="modal fade" id="delete-modal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<form action="user.php" method="POST">
|
||||
<?php echo csrf_field(); ?>
|
||||
<!-- Modal content -->
|
||||
|
||||
<div class="modal-content">
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* Centrale bootstrap voor elke entrypoint: config laden, sessie starten met
|
||||
* verharde instellingen, sessie-timeout en verplichte wachtwoordwijziging afdwingen.
|
||||
*
|
||||
* Vervangt de losse "session_start(); require_once 'config/config.php';" aanroepen.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
require_once __DIR__ . '/security.php';
|
||||
|
||||
qr_session_start();
|
||||
qr_enforce_session_timeout();
|
||||
qr_enforce_password_change();
|
||||
@@ -1,6 +1,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="csrf-token" content="<?php echo csrf_token(); ?>">
|
||||
|
||||
<!-- Font Awesome Icons -->
|
||||
<link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
|
||||
|
||||
@@ -16,14 +16,10 @@
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
|
||||
<div class="dropdown-divider"></div>
|
||||
<!--<a href="#" class="dropdown-item">
|
||||
<i class="fas fa-user"></i> Profile
|
||||
<a href="./change_password.php" class="dropdown-item">
|
||||
<i class="fas fa-key"></i> Change password
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="dropdown-item">
|
||||
<i class="fa fa-cog"></i> Settings
|
||||
</a>-->
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="./logout.php" class="dropdown-item">
|
||||
<i class="fas fa-sign-out-alt"></i> Logout
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/**
|
||||
* Fase 1 security hardening: sessiebeheer, CSRF, rate limiting, audit log.
|
||||
* Wordt geladen via includes/bootstrap.php, dat als eerste in elke entrypoint hoort te staan.
|
||||
*/
|
||||
|
||||
define('SESSION_IDLE_TIMEOUT', 30 * 60); // 30 minuten inactiviteit -> uitloggen
|
||||
define('LOGIN_MAX_ATTEMPTS', 5);
|
||||
define('LOGIN_LOCKOUT_WINDOW', 15 * 60); // 15 minuten
|
||||
|
||||
/**
|
||||
* Start de sessie met verharde cookie-instellingen. Moet vóór elke output aangeroepen worden.
|
||||
*/
|
||||
function qr_session_start() {
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$is_https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
|
||||
ini_set('session.gc_maxlifetime', (string) SESSION_IDLE_TIMEOUT);
|
||||
ini_set('session.use_strict_mode', '1');
|
||||
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => $is_https,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
|
||||
session_start();
|
||||
}
|
||||
|
||||
function qr_client_ip() {
|
||||
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Logt de gebruiker uit als de sessie te lang inactief is geweest.
|
||||
*/
|
||||
function qr_enforce_session_timeout() {
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
|
||||
if (isset($_SESSION['last_activity']) && ($now - $_SESSION['last_activity']) > SESSION_IDLE_TIMEOUT) {
|
||||
$_SESSION = [];
|
||||
$_SESSION['login_failure'] = 'Je sessie is verlopen wegens inactiviteit. Log opnieuw in.';
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$_SESSION['last_activity'] = $now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stuurt ingelogde gebruikers met een verplichte wachtwoordwijziging naar change_password.php,
|
||||
* behalve op de wijzigingspagina en logout zelf.
|
||||
*/
|
||||
function qr_enforce_password_change() {
|
||||
if (empty($_SESSION['user_logged_in']) || empty($_SESSION['must_change_password'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$current_script = basename(parse_url($_SERVER['SCRIPT_NAME'], PHP_URL_PATH));
|
||||
$exempt = ['change_password.php', 'logout.php'];
|
||||
|
||||
if (in_array($current_script, $exempt, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
header('Location: change_password.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSRF-bescherming
|
||||
*/
|
||||
function csrf_token() {
|
||||
if (empty($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $_SESSION['csrf_token'];
|
||||
}
|
||||
|
||||
function csrf_field() {
|
||||
return '<input type="hidden" name="csrf_token" value="' . htmlspecialchars(csrf_token(), ENT_QUOTES, 'UTF-8') . '">';
|
||||
}
|
||||
|
||||
function csrf_is_valid($token) {
|
||||
return isset($_SESSION['csrf_token']) && is_string($token) && hash_equals($_SESSION['csrf_token'], $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Voor klassieke form-POSTs: verwacht een verborgen veld "csrf_token".
|
||||
*/
|
||||
function csrf_verify_or_die() {
|
||||
if (!csrf_is_valid($_POST['csrf_token'] ?? '')) {
|
||||
http_response_code(403);
|
||||
exit('403 Forbidden: invalid or missing CSRF token.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Voor JSON/AJAX-endpoints (bv. bulk_action.php): verwacht header X-CSRF-Token.
|
||||
*/
|
||||
function csrf_verify_header_or_die() {
|
||||
$token = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||
if (!csrf_is_valid($token)) {
|
||||
http_response_code(403);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['data' => 'Invalid or missing CSRF token', 'status' => 403]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limiting op login
|
||||
*/
|
||||
function qr_record_login_attempt($username, $success) {
|
||||
$db = getDbInstance();
|
||||
$db->insert('login_attempts', [
|
||||
'username' => $username,
|
||||
'ip_address' => qr_client_ip(),
|
||||
'success' => $success ? 1 : 0,
|
||||
'attempted_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
function qr_is_login_locked_out($username) {
|
||||
$db = getDbInstance();
|
||||
$window_start = date('Y-m-d H:i:s', time() - LOGIN_LOCKOUT_WINDOW);
|
||||
|
||||
$db->where('username', $username);
|
||||
$db->where('success', 0);
|
||||
$db->where('attempted_at', $window_start, '>=');
|
||||
$count = $db->getValue('login_attempts', 'count(*)');
|
||||
|
||||
return $count !== null && $count >= LOGIN_MAX_ATTEMPTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit log
|
||||
*/
|
||||
function audit_log($action, $target_type = null, $target_id = null) {
|
||||
$db = getDbInstance();
|
||||
$db->insert('audit_log', [
|
||||
'user_id' => $_SESSION['user_id'] ?? null,
|
||||
'username' => $_SESSION['username'] ?? null,
|
||||
'action' => $action,
|
||||
'target_type' => $target_type,
|
||||
'target_id' => $target_id !== null ? (string) $target_id : null,
|
||||
'ip_address' => qr_client_ip(),
|
||||
'user_agent' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255),
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
+1
-11
@@ -1,15 +1,5 @@
|
||||
<?php
|
||||
//Use httponly flag
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
|
||||
//Use only cookies
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
|
||||
//Use secure flag
|
||||
ini_set('session.cookie_secure', 1);
|
||||
|
||||
session_start();
|
||||
require_once './config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once 'includes/auth_validate.php';
|
||||
|
||||
$db = getDbInstance();
|
||||
|
||||
@@ -56,6 +56,8 @@ class DynamicQrcode {
|
||||
* We save into db the url of qrcode image
|
||||
*/
|
||||
public function addQrcode($input_data) {
|
||||
$this->validateLink($input_data['link'] ?? '');
|
||||
|
||||
if($input_data['id_owner'] != "")
|
||||
$data_to_db['id_owner'] = $input_data['id_owner'];
|
||||
else
|
||||
@@ -79,6 +81,8 @@ class DynamicQrcode {
|
||||
*
|
||||
*/
|
||||
public function editQrcode($input_data) {
|
||||
$this->validateLink($input_data['link'] ?? '');
|
||||
|
||||
if($input_data['id_owner'] != "")
|
||||
$data_to_db['id_owner'] = $input_data['id_owner'];
|
||||
else
|
||||
@@ -117,6 +121,17 @@ class DynamicQrcode {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Server-side validatie van de redirect-link (verplicht, max. 500 tekens per kolomdefinitie).
|
||||
*/
|
||||
private function validateLink($link) {
|
||||
$link = trim((string) $link);
|
||||
|
||||
if ($link === '' || strlen($link) > 500) {
|
||||
$this->failure('Link is required and must be at most 500 characters.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash message Failure process
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,8 @@ class Qrcode {
|
||||
private string $table;
|
||||
private string $redirect_url;
|
||||
|
||||
const ALLOWED_FORMATS = ['png', 'gif', 'jpeg', 'jpg', 'svg', 'svgbw', 'eps'];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -43,6 +45,33 @@ class Qrcode {
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Voorkomt path traversal / arbitrary file write via een gemanipuleerde bestandsnaam.
|
||||
*/
|
||||
private function sanitizeFilename($filename) {
|
||||
$filename = trim((string) $filename);
|
||||
|
||||
if ($filename === '' || strlen($filename) > 45) {
|
||||
$this->failure('Filename must be between 1 and 45 characters.');
|
||||
}
|
||||
|
||||
if (preg_match('#[\\/\\\\]#', $filename) || strpos($filename, '..') !== false || strpos($filename, "\0") !== false) {
|
||||
$this->failure('Filename cannot contain path separators.');
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
private function validateFormat($format) {
|
||||
$format = strtolower((string) $format);
|
||||
|
||||
if (!in_array($format, self::ALLOWED_FORMATS, true)) {
|
||||
$this->failure('Invalid qr code format.');
|
||||
}
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
public function getQrcode($id) {
|
||||
$db = getDbInstance();
|
||||
|
||||
@@ -94,6 +123,9 @@ class Qrcode {
|
||||
public function addQrcode($input_data, $data_to_db, $data_to_qrcode) {
|
||||
$options = $this->setOptions($input_data);
|
||||
|
||||
$data_to_db['filename'] = $this->sanitizeFilename($data_to_db['filename']);
|
||||
$data_to_db['format'] = $this->validateFormat($data_to_db['format']);
|
||||
|
||||
$outputInterface = QRGdImagePNG::class;
|
||||
$imageFormat = strtolower($data_to_db['format']);
|
||||
$fileExt = $imageFormat;
|
||||
@@ -305,21 +337,23 @@ class Qrcode {
|
||||
$this->failure('You cannot create a new qr code with an existing name on the server!');
|
||||
|
||||
if ($last_id){
|
||||
audit_log('qrcode_created', $this->table, $last_id);
|
||||
$this->success('Qr code added successfully!');
|
||||
}
|
||||
else {
|
||||
$this->failure('Insert failed: ' . $db->getLastError());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Edit qr code
|
||||
*
|
||||
*
|
||||
*/
|
||||
public function editQrcode($input_data, $data_to_db) {
|
||||
$db = getDbInstance();
|
||||
$old_qrcode = $this->getQrcode($input_data["id"]);
|
||||
|
||||
$data_to_db['filename'] = $this->sanitizeFilename($data_to_db['filename']);
|
||||
$data_to_db['qrcode'] = $data_to_db['filename'].'.'.$old_qrcode["format"];
|
||||
|
||||
if(!file_exists(SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.'.$old_qrcode["format"]) || $data_to_db['filename'] == $input_data["old_filename"]){
|
||||
@@ -337,6 +371,7 @@ class Qrcode {
|
||||
$this->failure('You cannot edit a qr code with an existing name on the server!');
|
||||
|
||||
if ($stat){
|
||||
audit_log('qrcode_updated', $this->table, $input_data['id']);
|
||||
$this->success('Qr code updated successfully!');
|
||||
}
|
||||
else {
|
||||
@@ -344,10 +379,10 @@ class Qrcode {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Delete qr code
|
||||
*
|
||||
*
|
||||
*/
|
||||
public function deleteQrcode($id, $async = false) {
|
||||
$db = getDbInstance();
|
||||
@@ -356,7 +391,11 @@ class Qrcode {
|
||||
|
||||
$db->where('id', $id);
|
||||
$status = $db->delete($this->table);
|
||||
|
||||
|
||||
if ($status) {
|
||||
audit_log('qrcode_deleted', $this->table, $id);
|
||||
}
|
||||
|
||||
try{
|
||||
unlink(SAVED_QRCODE_DIRECTORY.$qrcode["filename"].'.'.$qrcode["format"]);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ class Qrcode {
|
||||
private string $table;
|
||||
private string $redirect_url;
|
||||
|
||||
const ALLOWED_FORMATS = ['png', 'gif', 'jpeg', 'jpg', 'svg', 'eps'];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -28,6 +30,33 @@ class Qrcode {
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Voorkomt path traversal / arbitrary file write via een gemanipuleerde bestandsnaam.
|
||||
*/
|
||||
private function sanitizeFilename($filename) {
|
||||
$filename = trim((string) $filename);
|
||||
|
||||
if ($filename === '' || strlen($filename) > 45) {
|
||||
$this->failure('Filename must be between 1 and 45 characters.');
|
||||
}
|
||||
|
||||
if (preg_match('#[\\/\\\\]#', $filename) || strpos($filename, '..') !== false || strpos($filename, "\0") !== false) {
|
||||
$this->failure('Filename cannot contain path separators.');
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
private function validateFormat($format) {
|
||||
$format = strtolower((string) $format);
|
||||
|
||||
if (!in_array($format, self::ALLOWED_FORMATS, true)) {
|
||||
$this->failure('Invalid qr code format.');
|
||||
}
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
public function getQrcode($id) {
|
||||
$db = getDbInstance();
|
||||
|
||||
@@ -79,6 +108,9 @@ class Qrcode {
|
||||
public function addQrcode($input_data, $data_to_db, $data_to_qrcode) {
|
||||
$options = $this->setOptions($input_data);
|
||||
|
||||
$data_to_db['filename'] = $this->sanitizeFilename($data_to_db['filename']);
|
||||
$data_to_db['format'] = $this->validateFormat($data_to_db['format']);
|
||||
|
||||
if(!file_exists(SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.'.$data_to_db['format'])){
|
||||
$url =
|
||||
'https://api.qrserver.com/v1/create-qr-code/?data='.
|
||||
@@ -111,6 +143,7 @@ class Qrcode {
|
||||
$this->failure('You cannot create a new qr code with an existing name on the server!');
|
||||
|
||||
if ($last_id){
|
||||
audit_log('qrcode_created', $this->table, $last_id);
|
||||
$this->success('Qr code added successfully!');
|
||||
}
|
||||
else {
|
||||
@@ -126,6 +159,7 @@ class Qrcode {
|
||||
$db = getDbInstance();
|
||||
$old_qrcode = $this->getQrcode($input_data["id"]);
|
||||
|
||||
$data_to_db['filename'] = $this->sanitizeFilename($data_to_db['filename']);
|
||||
$data_to_db['qrcode'] = $data_to_db['filename'].'.'.$old_qrcode["format"];
|
||||
|
||||
if(!file_exists(SAVED_QRCODE_DIRECTORY.$data_to_db['filename'].'.'.$old_qrcode["format"]) || $data_to_db['filename'] == $input_data["old_filename"]){
|
||||
@@ -143,6 +177,7 @@ class Qrcode {
|
||||
$this->failure('You cannot edit a qr code with an existing name on the server!');
|
||||
|
||||
if ($stat){
|
||||
audit_log('qrcode_updated', $this->table, $input_data['id']);
|
||||
$this->success('Qr code updated successfully!');
|
||||
}
|
||||
else {
|
||||
@@ -162,7 +197,11 @@ class Qrcode {
|
||||
|
||||
$db->where('id', $id);
|
||||
$status = $db->delete($this->table);
|
||||
|
||||
|
||||
if ($status) {
|
||||
audit_log('qrcode_deleted', $this->table, $id);
|
||||
}
|
||||
|
||||
try{
|
||||
unlink(SAVED_QRCODE_DIRECTORY.$qrcode["filename"].'.'.$qrcode["format"]);
|
||||
}
|
||||
|
||||
+59
-11
@@ -3,6 +3,8 @@ require_once 'config/config.php';
|
||||
|
||||
class Users
|
||||
{
|
||||
const ALLOWED_TYPES = ['super', 'admin'];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -10,6 +12,25 @@ class Users
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side validatie van username/type. Geeft een foutmelding terug (string) of null als geldig.
|
||||
*/
|
||||
private function validateUsernameAndType($username, $type) {
|
||||
if (!is_string($username) || strlen($username) < 3 || strlen($username) > 50) {
|
||||
return 'Username must be between 3 and 50 characters.';
|
||||
}
|
||||
|
||||
if (!preg_match('/^[a-zA-Z0-9._-]+$/', $username)) {
|
||||
return 'Username may only contain letters, numbers, dots, underscores and hyphens.';
|
||||
}
|
||||
|
||||
if (!in_array($type, self::ALLOWED_TYPES, true)) {
|
||||
return 'Invalid user type.';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -54,6 +75,15 @@ class Users
|
||||
public function addUser($input_data) {
|
||||
$db = getDbInstance();
|
||||
|
||||
$validation_error = $this->validateUsernameAndType($input_data['username'] ?? '', $input_data['type'] ?? '');
|
||||
if ($validation_error !== null) {
|
||||
$this->failure($validation_error, 'Location: user.php');
|
||||
}
|
||||
|
||||
if (!isset($input_data['password']) || strlen($input_data['password']) < 10) {
|
||||
$this->failure('Password must be at least 10 characters long.', 'Location: user.php');
|
||||
}
|
||||
|
||||
$data_to_db["username"] = $input_data["username"];
|
||||
$data_to_db['password'] = password_hash($input_data['password'], PASSWORD_DEFAULT);
|
||||
$data_to_db["type"] = $input_data["type"];
|
||||
@@ -66,8 +96,10 @@ class Users
|
||||
|
||||
$last_id = $db->insert('users', $data_to_db);
|
||||
|
||||
if ($last_id)
|
||||
if ($last_id) {
|
||||
audit_log('user_created', 'user', $last_id);
|
||||
$this->success('User added successfully');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,28 +109,43 @@ class Users
|
||||
public function editUser($input_data) {
|
||||
$db = getDbInstance();
|
||||
|
||||
$query_string = http_build_query(array(
|
||||
'id' => $input_data["id"],
|
||||
'edit' => "true",
|
||||
));
|
||||
|
||||
$validation_error = $this->validateUsernameAndType($input_data['username'] ?? '', $input_data['type'] ?? '');
|
||||
if ($validation_error !== null) {
|
||||
$this->failure($validation_error, 'Location: user.php?'.$query_string);
|
||||
}
|
||||
|
||||
if (isset($input_data['password']) && strlen($input_data['password']) > 0 && strlen($input_data['password']) < 10) {
|
||||
$this->failure('Password must be at least 10 characters long.', 'Location: user.php?'.$query_string);
|
||||
}
|
||||
|
||||
$db->where('username', $input_data['username']);
|
||||
$db->where('id', $input_data["id"], '!=');
|
||||
$row = $db->getOne('users');
|
||||
|
||||
if (!empty($row['username'])) {
|
||||
$query_string = http_build_query(array(
|
||||
'id' => $input_data["id"],
|
||||
'edit' => "true",
|
||||
));
|
||||
$this->failure('Username already exists', 'Location: user.php?'.$query_string);
|
||||
}
|
||||
|
||||
$data_to_db["username"] = $input_data["username"];
|
||||
$data_to_db['password'] = password_hash($input_data['password'], PASSWORD_DEFAULT);
|
||||
$data_to_db["type"] = $input_data["type"];
|
||||
|
||||
// Alleen wachtwoord overschrijven als er een nieuwe waarde is opgegeven.
|
||||
if (!empty($input_data['password'])) {
|
||||
$data_to_db['password'] = password_hash($input_data['password'], PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
$db->where('id', $input_data["id"]);
|
||||
$stat = $db->update('users', $data_to_db);
|
||||
|
||||
if ($stat)
|
||||
|
||||
if ($stat) {
|
||||
audit_log('user_updated', 'user', $input_data['id']);
|
||||
$this->success('User updated successfully!');
|
||||
else
|
||||
} else
|
||||
$this->failure('Failed to update User: ' . $db->getLastError());
|
||||
}
|
||||
|
||||
@@ -116,9 +163,10 @@ class Users
|
||||
$db->where('id', $id);
|
||||
$stat = $db->delete('users');
|
||||
|
||||
if ($stat)
|
||||
if ($stat) {
|
||||
audit_log('user_deleted', 'user', $id);
|
||||
$this->info('User deleted successfully!');
|
||||
else
|
||||
} else
|
||||
$this->failure('Unable to delete user');
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
$token = bin2hex(openssl_random_pseudo_bytes(16));
|
||||
|
||||
// If User has already logged in, redirect to dashboard page.
|
||||
if (isset($_SESSION['user_logged_in']) && $_SESSION['user_logged_in'] === TRUE)
|
||||
{
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// If user has previously selected "remember me option":
|
||||
@@ -33,9 +33,17 @@ if (isset($_COOKIE['series_id']) && isset($_COOKIE['remember_token']))
|
||||
exit;
|
||||
}
|
||||
|
||||
session_regenerate_id(true);
|
||||
|
||||
$_SESSION['user_logged_in'] = TRUE;
|
||||
$_SESSION['user_id'] = $row['id'];
|
||||
$_SESSION['type'] = $row['type'];
|
||||
$_SESSION['username'] = $row['username'];
|
||||
$_SESSION['must_change_password'] = !empty($row['must_change_password']);
|
||||
$_SESSION['last_activity'] = time();
|
||||
|
||||
audit_log('login_success_remember');
|
||||
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
@@ -71,6 +79,7 @@ if (isset($_COOKIE['series_id']) && isset($_COOKIE['remember_token']))
|
||||
<p class="login-box-msg">Sign in to start your session</p>
|
||||
|
||||
<form method="POST" action="authenticate.php">
|
||||
<?php echo csrf_field(); ?>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" name="username" class="form-control" placeholder="Username" required="required">
|
||||
<div class="input-group-append">
|
||||
|
||||
+14
-4
@@ -1,10 +1,20 @@
|
||||
<?php
|
||||
require_once './config/config.php';
|
||||
session_start();
|
||||
require_once 'includes/bootstrap.php';
|
||||
|
||||
if (!empty($_SESSION['user_logged_in'])) {
|
||||
audit_log('logout');
|
||||
}
|
||||
|
||||
$_SESSION = [];
|
||||
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
|
||||
}
|
||||
|
||||
session_destroy();
|
||||
|
||||
|
||||
if(isset($_COOKIE['series_id']) && isset($_COOKIE['remember_token'])){
|
||||
if (isset($_COOKIE['series_id']) && isset($_COOKIE['remember_token'])) {
|
||||
clearAuthCookie();
|
||||
}
|
||||
header('Location:index.php');
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once BASE_PATH.'/includes/auth_validate.php';
|
||||
require_once BASE_PATH . '/lib/StaticQrcode/StaticQrcode.php';
|
||||
|
||||
$static_qrcode_instance = new StaticQrcode();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
csrf_verify_or_die();
|
||||
}
|
||||
|
||||
$edit = false;
|
||||
if($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["edit"]) && $_GET["edit"] == "true" && isset($_GET["id"])) {
|
||||
$edit = true;
|
||||
@@ -115,6 +118,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"])) {
|
||||
</div>
|
||||
<?php if($edit) {?>
|
||||
<form class="form" action="" method="post" id="static_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<div class="card-body">
|
||||
<?php include BASE_PATH . '/forms/form_static_edit.php';?>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once BASE_PATH . '/includes/auth_validate.php';
|
||||
require_once BASE_PATH . '/lib/StaticQrcode/StaticQrcode.php';
|
||||
|
||||
|
||||
+5
-2
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once BASE_PATH . '/includes/auth_validate.php';
|
||||
require_once BASE_PATH . '/lib/Users/Users.php';
|
||||
|
||||
@@ -9,6 +8,9 @@ $user_instance = new Users();
|
||||
if ($_SESSION['type'] !== 'super')
|
||||
$user_instance->failure('Only a "super admin" account can access the admin listing page', 'Location: index.php');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
csrf_verify_or_die();
|
||||
}
|
||||
|
||||
$edit = false;
|
||||
if($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["edit"]) && $_GET["edit"] == "true" && isset($_GET["id"])) {
|
||||
@@ -83,6 +85,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"])) {
|
||||
<h3 class="card-title">Enter the requested data</h3>
|
||||
</div>
|
||||
<form class="well form-horizontal" action="" method="post" id="contact_form" enctype="multipart/form-data">
|
||||
<?php echo csrf_field(); ?>
|
||||
<div class="card-body">
|
||||
<?php include BASE_PATH . '/forms/form_users.php'; ?>
|
||||
</div>
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'config/config.php';
|
||||
require_once 'includes/bootstrap.php';
|
||||
require_once BASE_PATH . '/includes/auth_validate.php';
|
||||
require_once BASE_PATH . '/lib/Users/Users.php';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user