Skip to content

Commit c424af6

Browse files
authored
Fix secruity issues (#1403)
* secure file uploads * session hardening * secure path traversal in uploads * 50mb max from frontend * restrict shellCommand and sanitize post filenames * restrict and mask output on serverInfo endpoint * csrf in admin forms * escape ffmpeg inputs to prevent injection * sanitize print filename and escape command args * use shared admin_boot to enforce auth on admin pages * sanitize random image dir to allowed roots * validate theme export file before download * add rate limiting and validation to sendPic * add global frame/referrer/nosniff headers * set uploads to 0644 with chmod warning * log errors * add ip-based throttling for password and pin login * fixes * add pin hashing * fix * harden shell command auth * secure live chroma config with admin auth * sanitize video effect filename * tighten sendpic limits and validation * sanitize download image param * csrf token flow and rate limit for print * sanitize file input in applyeffects * protect rebuild imagedb with admin bootstrap * protect checkversion with admin bootstrap * sanitize delete photo input * add csrf token to client calls, add csrf and rate limiting to printdb * sanitize qrcode filename input * add more csrf * add more csrf * fix csrf * show msg on too many attempts * Added keypad attempt reset FAQ * fix saving password and pin * fix saving password and pin
1 parent a722c26 commit c424af6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+888
-245
lines changed

admin/admin_boot.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/** @var array $config */
4+
5+
require_once __DIR__ . '/../lib/boot.php';
6+
7+
use Photobooth\Service\ConfigurationService;
8+
use Photobooth\Utility\PathUtility;
9+
10+
$config = ConfigurationService::getInstance()->getConfiguration();
11+
12+
// Make sure CSRF helper is available even if only admin_boot is required
13+
if (!function_exists('checkCsrfOrFail')) {
14+
/**
15+
* @param array<string,mixed> $source
16+
*/
17+
function checkCsrfOrFail(array $source, string $key = 'csrf'): void
18+
{
19+
$sessionToken = $_SESSION[$key] ?? '';
20+
$incomingToken = $source[$key] ?? '';
21+
if (!hash_equals((string)$sessionToken, (string)$incomingToken)) {
22+
$logger = Photobooth\Service\LoggerService::getInstance()->getLogger('main');
23+
$logger->debug('CSRF validation failed', [
24+
'expected' => $sessionToken,
25+
'provided' => $incomingToken,
26+
'path' => $_SERVER['REQUEST_URI'] ?? '',
27+
'method' => $_SERVER['REQUEST_METHOD'] ?? '',
28+
]);
29+
http_response_code(403);
30+
echo json_encode(['error' => 'Invalid CSRF token']);
31+
exit();
32+
}
33+
}
34+
}
35+
36+
// Enforce admin session only when login is enabled
37+
if ($config['login']['enabled']) {
38+
if (!isset($_SESSION['auth']) || $_SESSION['auth'] !== true) {
39+
header('Location: ' . PathUtility::getPublicPath('login'));
40+
exit();
41+
}
42+
}

admin/captureconfig.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
<?php
22

33
/** @var array $config */
4-
require_once '../lib/boot.php';
4+
require_once __DIR__ . '/admin_boot.php';
55

66
use Photobooth\Environment;
77
use Photobooth\Service\ConfigurationService;
88
use Photobooth\Service\LoggerService;
99
use Photobooth\Service\ProcessService;
1010
use Photobooth\Utility\PathUtility;
1111

12-
// Login / Authentication check
13-
if (!(
14-
!$config['login']['enabled'] ||
15-
(!$config['protect']['localhost_admin'] && isset($_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR']) ||
16-
(isset($_SESSION['auth']) && $_SESSION['auth'] === true) || !$config['protect']['admin']
17-
)) {
18-
header('location: ' . PathUtility::getPublicPath('login'));
19-
exit();
20-
}
21-
2212
header('Content-Type: application/json');
2313

2414
$loggerService = LoggerService::getInstance();

admin/debug/index.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
<?php
22

3-
require_once '../../lib/boot.php';
3+
require_once __DIR__ . '/../admin_boot.php';
44

55
use Photobooth\Environment;
66
use Photobooth\Service\ApplicationService;
77
use Photobooth\Service\AssetService;
88
use Photobooth\Service\LanguageService;
99
use Photobooth\Utility\PathUtility;
1010

11-
// Login / Authentication check
12-
if (!(
13-
!$config['login']['enabled'] ||
14-
(!$config['protect']['localhost_admin'] && isset($_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR']) ||
15-
(isset($_SESSION['auth']) && $_SESSION['auth'] === true) || !$config['protect']['admin']
16-
)) {
17-
header('location: ' . PathUtility::getPublicPath('login'));
18-
exit();
19-
}
20-
2111
$languageService = LanguageService::getInstance();
2212
$assetService = AssetService::getInstance();
2313
$pageTitle = 'Debugpanel - ' . ApplicationService::getInstance()->getTitle();

admin/diskusage/index.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
<?php
22

3-
require_once '../../lib/boot.php';
3+
require_once __DIR__ . '/../admin_boot.php';
44

55
use Photobooth\Enum\FolderEnum;
66
use Photobooth\Helper;
77
use Photobooth\Service\ApplicationService;
88
use Photobooth\Service\LanguageService;
99
use Photobooth\Utility\PathUtility;
1010

11-
// Login / Authentication check
12-
if (!(
13-
!$config['login']['enabled'] ||
14-
(!$config['protect']['localhost_admin'] && isset($_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR']) ||
15-
(isset($_SESSION['auth']) && $_SESSION['auth'] === true) || !$config['protect']['admin']
16-
)) {
17-
header('location: ' . PathUtility::getPublicPath('login'));
18-
exit();
19-
}
20-
2111
$languageService = LanguageService::getInstance();
2212
$pageTitle = 'Diskusage - ' . ApplicationService::getInstance()->getTitle();
2313
include PathUtility::getAbsolutePath('admin/components/head.admin.php');

admin/generator/index.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
require_once '../../lib/boot.php';
2+
require_once __DIR__ . '/../admin_boot.php';
33

44
use Photobooth\Service\ConfigurationService;
55
use Photobooth\Service\ApplicationService;
@@ -10,16 +10,6 @@
1010
use Photobooth\Utility\PathUtility;
1111
use Photobooth\Service\AssetService;
1212

13-
// Login / Authentication check
14-
if (!(
15-
!$config['login']['enabled'] ||
16-
(!$config['protect']['localhost_admin'] && isset($_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR']) ||
17-
(isset($_SESSION['auth']) && $_SESSION['auth'] === true) || !$config['protect']['admin']
18-
)) {
19-
header('location: ' . PathUtility::getPublicPath('login'));
20-
exit();
21-
}
22-
2313
$configurationService = ConfigurationService::getInstance();
2414

2515
$error = false;

admin/index.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
11
<?php
22

3-
require_once '../lib/boot.php';
3+
require_once __DIR__ . '/admin_boot.php';
44

55
use Photobooth\Service\ApplicationService;
66
use Photobooth\Utility\PathUtility;
77

8-
// Login / Authentication check
9-
if (!(
10-
!$config['login']['enabled'] ||
11-
(!$config['protect']['localhost_admin'] && isset($_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR']) ||
12-
(isset($_SESSION['auth']) && $_SESSION['auth'] === true) || !$config['protect']['admin']
13-
)) {
14-
header('location: ' . PathUtility::getPublicPath('login'));
15-
exit();
16-
}
17-
188
$configsetup = require PathUtility::getAbsolutePath('lib/configsetup.inc.php');
199

2010
$appName = ApplicationService::getInstance()->getTitle();

admin/upload/index.php

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
<?php
22

3-
require_once '../../lib/boot.php';
3+
require_once __DIR__ . '/../admin_boot.php';
44

55
use Photobooth\FileUploader;
66
use Photobooth\Service\ApplicationService;
77
use Photobooth\Service\LanguageService;
88
use Photobooth\Utility\PathUtility;
99
use Photobooth\Service\LoggerService;
1010

11-
// Login / Authentication check
12-
if (!(
13-
!$config['login']['enabled'] ||
14-
(!$config['protect']['localhost_admin'] && isset($_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR']) ||
15-
(isset($_SESSION['auth']) && $_SESSION['auth'] === true) || !$config['protect']['admin']
16-
)) {
17-
header('location: ' . PathUtility::getPublicPath('login'));
18-
exit();
11+
// CSRF token helper
12+
if (!isset($_SESSION['csrf_token'])) {
13+
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
1914
}
15+
$csrfToken = $_SESSION['csrf_token'];
2016

2117
$logger = LoggerService::getInstance()->getLogger('main');
2218
$logger->debug(basename($_SERVER['PHP_SELF']));
@@ -36,26 +32,33 @@
3632
$max_file_size = ini_get('upload_max_filesize');
3733

3834
if (isset($_POST['submit'])) {
35+
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
36+
$errors['csrf'] = 'csrf';
37+
}
38+
3939
$folderName = $_POST['folder_name'];
4040
$uploadedFiles = $_FILES['files'];
4141

42-
$uploader = new FileUploader($folderName, $uploadedFiles, $logger);
43-
$response = $uploader->uploadFiles();
44-
45-
list($success, $message, $errors, $uploadedFiles, $failedFiles) = [
46-
$response['success'],
47-
$response['message'],
48-
$response['errors'],
49-
$response['uploadedFiles'],
50-
$response['failedFiles']
51-
];
42+
if (!isset($errors['csrf'])) {
43+
$uploader = new FileUploader($folderName, $uploadedFiles, $logger);
44+
$response = $uploader->uploadFiles();
45+
46+
list($success, $message, $errors, $uploadedFiles, $failedFiles) = [
47+
$response['success'],
48+
$response['message'],
49+
$response['errors'],
50+
$response['uploadedFiles'],
51+
$response['failedFiles']
52+
];
53+
}
5254
}
5355
?>
5456

5557
<div class="w-full h-screen grid place-items-center absolute bg-brand-2 px-6 py-12 overflow-x-hidden overflow-y-auto">
5658
<div class="w-full flex items-center justify-center flex-col">
5759
<div class="w-full max-w-xl rounded-lg p-8 bg-white flex flex-col shadow-xl">
5860
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST" enctype="multipart/form-data">
61+
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
5962
<div class="w-full flex flex-col items-center justify-center text-2xl font-bold text-brand-1 mb-2">
6063
File uploader
6164
</div>
@@ -76,16 +79,21 @@
7679
<label class="<?= $labelClass ?>" for="files"><?=$languageService->translate('upload_selection')?></label>
7780
<input class="<?= $labelClass ?>" type="file" name="files[]" id="files" multiple accept="image/*, video/*, .ttf" required>
7881
<div class="my-2"><?= $languageService->translate('file_upload_max_size') ?> <?= $max_file_size ?></div>
82+
<?php
83+
if (isset($errors['csrf'])) {
84+
echo '<div class="flex flex-col justify-between p-2 rounded-sm bg-red-300 text-red-800 border-2 border-red-800">' . $languageService->translate('invalid_session') . '</div>';
85+
}
86+
?>
7987
</div>
8088

8189
<?php
82-
if (count($failedFiles) > 0) {
83-
echo '<div class="flex flex-col gap-2">';
84-
foreach ($failedFiles as $fileName => $reason) {
85-
echo '<div class="flex flex-col justify-between p-2 rounded-sm bg-red-300 text-red-800 border-2 border-red-800"><div class="col-span-1">' . $fileName . '</div><div class="col-span-1">' . $languageService->translate($reason) . '</div></div>';
86-
}
87-
echo '</div>';
88-
}
90+
if (count($failedFiles) > 0) {
91+
echo '<div class="flex flex-col gap-2">';
92+
foreach ($failedFiles as $fileName => $reason) {
93+
echo '<div class="flex flex-col justify-between p-2 rounded-sm bg-red-300 text-red-800 border-2 border-red-800"><div class="col-span-1">' . $fileName . '</div><div class="col-span-1">' . $languageService->translate($reason) . '</div></div>';
94+
}
95+
echo '</div>';
96+
}
8997
?>
9098

9199
<div class="mt-6">

admin/wgetcaptureconfig.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
<?php
22

33
/** @var array $config */
4-
require_once '../lib/boot.php';
4+
require_once __DIR__ . '/admin_boot.php';
55

66
use Photobooth\Environment;
77
use Photobooth\Service\ConfigurationService;
88
use Photobooth\Service\LoggerService;
99
use Photobooth\Service\ProcessService;
1010
use Photobooth\Utility\PathUtility;
1111

12-
// Login / Authentication check
13-
if (!(
14-
!$config['login']['enabled'] ||
15-
(!$config['protect']['localhost_admin'] && isset($_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR']) ||
16-
(isset($_SESSION['auth']) && $_SESSION['auth'] === true) || !$config['protect']['admin']
17-
)) {
18-
header('location: ' . PathUtility::getPublicPath('login'));
19-
exit();
20-
}
21-
2212
header('Content-Type: application/json');
2313

2414
$loggerService = LoggerService::getInstance();

0 commit comments

Comments
 (0)