security: address second round of code-review comments

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/f1016d36-0771-46e0-992c-95ce22594414

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-05-04 10:01:09 +00:00
committed by GitHub
parent 8356d0dadc
commit 6a9e55a8de
3 changed files with 15 additions and 3 deletions

View File

@@ -6,6 +6,10 @@
// The target URL is passed via the script tag's data-target attribute.
// document.currentScript is only available during synchronous script execution
// (not with defer/async), so this script is loaded without those attributes.
//
// We replace with location.pathname only (no query string, no hash), which
// intentionally strips the ?token=… parameter and any hash fragment from the
// recorded history entry.
const target = document.currentScript.dataset.target;
history.replaceState(null, '', location.pathname);
window.location.replace(target);

View File

@@ -167,6 +167,8 @@ readonly class DockerController {
// The password has been passed to the borgbackup container's environment.
// Clear it from the persistent config so it is not kept at rest longer than needed.
// ConfigurationManager.set() overwrites the stored value with an empty string and
// immediately writes the updated config to disk, so the plaintext password is removed.
$this->configurationManager->borgRestorePassword = '';
// End streaming response

View File

@@ -40,12 +40,18 @@ readonly class LoginController {
$response->getBody()->write("Unable to determine client IP. Login refused.");
return $response->withStatus(403);
}
$rateLimitKey = 'login_attempts_' . hash('sha256', $ip);
// Use HMAC to avoid leaking which IPs are being tracked via predictable cache-key names.
$hmacKey = (string)(apcu_fetch('login_rate_limit_hmac_key') ?: '');
if ($hmacKey === '') {
$hmacKey = bin2hex(random_bytes(16));
apcu_add('login_rate_limit_hmac_key', $hmacKey);
}
$rateLimitKey = 'login_attempts_' . hash_hmac('sha256', $ip, $hmacKey);
$attempts = (int)(apcu_fetch($rateLimitKey) ?: 0);
if ($attempts >= self::MAX_FAILED_ATTEMPTS) {
// Keep a delay even when blocked so the 429 itself isn't a timing oracle.
sleep(5);
// Return 429 immediately; the rate limit itself is sufficient protection.
$response->getBody()->write("Too many failed login attempts. Please try again later.");
return $response->withStatus(429);
}