Fix security issues from code review: remove plaintext password comment, enable TLS verification, harden booking ref entropy, fix XSS/injection in signature emails, fix broken waiver link, consolidate duplicated auth/file-serving logic, add missing document view links, sync schema.sql with live DB

This commit is contained in:
2026-07-04 14:01:33 -05:00
parent 72ba4743f9
commit d12baebcbc
8 changed files with 75 additions and 51 deletions
+30 -4
View File
@@ -8,7 +8,7 @@ define('PARKER_DB_PASS', '4@rxg*8kovxCr7w6');
define('ADMIN_USER', 'admin');
define('ADMIN_PHONE', '(817) 266-2022');
define('ADMIN_PASS', '$2y$10$ynnk3RfarOD7VIJizC30kuXqu6tQ3gotNrlp5y33afh5fPOgnAMU6'); // Parker2026!
define('ADMIN_PASS', '$2y$10$ynnk3RfarOD7VIJizC30kuXqu6tQ3gotNrlp5y33afh5fPOgnAMU6');
define('ADMIN_SESSION_KEY', 'parker_admin_auth');
define('CYBERMAIL_API_KEY', 'sk_live_7f9b0f9a29f6de31a0d229d4af75d56b094ad724fc58a57d');
@@ -47,7 +47,7 @@ function squareApi(string $method, string $path, array $body = []): array {
'Content-Type: application/json',
'Square-Version: ' . SQUARE_VERSION,
];
$opts = [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false];
$opts = [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 30];
if ($method === 'POST') {
$opts[CURLOPT_POST] = true;
$opts[CURLOPT_POSTFIELDS] = $body ? json_encode($body) : '{}';
@@ -59,7 +59,34 @@ function squareApi(string $method, string $path, array $body = []): array {
}
function generateRef(): string {
return 'PSR-' . strtoupper(substr(uniqid(), -6));
// 4 random bytes -> 8 hex chars; 'PSR-' + 8 chars = 12, matches booking_ref varchar(12).
return 'PSR-' . strtoupper(bin2hex(random_bytes(4)));
}
// Shared by view-doc.php and admin/view-doc.php so the admin-token check and
// path-traversal/mime guards live in exactly one place.
function verifyAdminToken(string $token): bool {
if (!preg_match('/^[a-f0-9]{64}$/', $token)) return false;
$stmt = db()->prepare("SELECT token FROM admin_tokens WHERE token=? AND expires_at > NOW()");
$stmt->execute([$token]);
return (bool)$stmt->fetch();
}
const ALLOWED_DOC_MIMES = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'application/pdf' => 'pdf'];
function resolveUploadPath(string $root, string $relativePath): ?string {
$base = realpath($root . '/uploads');
$path = realpath($root . '/' . $relativePath);
if (!$path || !$base || !str_starts_with($path, $base . DIRECTORY_SEPARATOR)) {
return null;
}
return $path;
}
function detectAllowedMime(string $path): ?string {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($path);
return isset(ALLOWED_DOC_MIMES[$mime]) ? $mime : null;
}
function sendEmail(string $to, string $toName, string $subject, string $html): bool {
@@ -81,7 +108,6 @@ function sendEmail(string $to, string $toName, string $subject, string $html): b
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 15,
CURLOPT_SSL_VERIFYPEER => false,
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);