mirror of
https://github.com/myronblair/novacpx
synced 2026-06-30 17:50:41 -05:00
fix: all code review security findings
- CORS: replace open regex with explicit hostname allowlist + port whitelist - Exception handler: only expose RuntimeException/InvalidArgumentException messages; PDOException and others return generic 'internal error' - Auth::portalUrl(): allowlist-validate HTTP_HOST before using it in redirect URL — prevents open redirect via Host header injection - _branding.php custom_css: strip HTML tags, js: URLs, @import, expression() instead of just </style> which was trivially bypassable - accounts create: check accounts table as well as users for username uniqueness (TOCTOU fix); wrap user INSERT + provisioning in single transaction so rollback is atomic on failure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LP9Q4kfCAYAjJnsbHBrViZ
This commit is contained in:
+24
-7
@@ -150,18 +150,35 @@ class Auth {
|
||||
* Used by login redirect so each role lands on the right port
|
||||
*/
|
||||
public static function portalUrl(string $role, string $path = '/'): string {
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||
// No port in HTTP_HOST means the request came through a reverse proxy on 443 — stay on same host
|
||||
if (!preg_match('/:\d+$/', $host)) {
|
||||
return "https://{$host}{$path}";
|
||||
}
|
||||
// Direct access — redirect to the correct panel port
|
||||
$host = $_SERVER['HTTP_HOST'] ?? '';
|
||||
|
||||
// Allowlist of trusted hostnames — prevents open redirect via Host header injection
|
||||
$allowed = [
|
||||
'novacpx.orbishosting.com',
|
||||
'admin.novacpx.orbishosting.com',
|
||||
'reseller.novacpx.orbishosting.com',
|
||||
'panel.novacpx.orbishosting.com',
|
||||
'web.orbishosting.com',
|
||||
];
|
||||
$hostname = preg_replace('/:\d+$/', '', $host);
|
||||
|
||||
// Trusted proxy (no port) — stay on same host
|
||||
if ($host && !preg_match('/:\d+$/', $host) && in_array($hostname, $allowed, true)) {
|
||||
return "https://{$hostname}{$path}";
|
||||
}
|
||||
|
||||
// Direct port access — validate hostname is a known server IP or allowed host
|
||||
$port = match($role) {
|
||||
'admin' => PORT_ADMIN,
|
||||
'reseller' => PORT_RESELLER,
|
||||
default => PORT_USER,
|
||||
};
|
||||
return "https://{$hostname}:{$port}{$path}";
|
||||
// Only redirect to localhost/LAN IPs on direct panel access
|
||||
if ($hostname && (filter_var($hostname, FILTER_VALIDATE_IP) || in_array($hostname, $allowed, true))) {
|
||||
return "https://{$hostname}:{$port}{$path}";
|
||||
}
|
||||
|
||||
// Fallback — safe relative path with no host (works for same-origin redirects)
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user