check()) { abort(404); } $ip = (string) $request->ip(); $meta = [ 'ua' => Str::limit((string) $request->userAgent(), 190), 'method' => $request->method(), 'query' => Str::limit((string) $request->getQueryString(), 190), ]; // Capture the credentials a prober typed into the fake login — their guesses, which are // threat intel (a legitimate user never reaches a decoy). The field names span the // impersonated apps: WordPress (log/pwd), phpMyAdmin (pma_username/pma_password), and the // generic form (username/password). This is the attacker's OWN submission, so logging + // banning stays keyed to their IP — no reflected-victim risk. $user = $request->input('log') ?? $request->input('pma_username') ?? $request->input('username'); $pass = $request->input('pwd') ?? $request->input('pma_password') ?? $request->input('password'); if (is_string($user) && $user !== '') { $meta['tried_user'] = Str::limit($user, 120); } if (is_string($pass) && $pass !== '') { $meta['tried_pass'] = Str::limit($pass, 120); } AuditEvent::create([ 'user_id' => null, 'actor' => 'system', 'action' => 'security.honeypot_hit', 'target' => Str::limit($request->path(), 190), 'ip' => $ip, 'meta' => $meta, ]); app(BruteforceGuard::class)->banNow($ip, 'honeypot'); return $this->deceive($request); } /** Pick a convincing fake response for the probed path. */ private function deceive(Request $request): Response { $path = strtolower($request->path()); if ($path === '.env') { return response($this->fakeDotenv(), 200, ['Content-Type' => 'text/plain; charset=UTF-8']); } if ($path === '.git/config') { return response($this->fakeGitConfig(), 200, ['Content-Type' => 'text/plain; charset=UTF-8']); } if (str_starts_with($path, 'wp-login.php') || str_starts_with($path, 'wp-admin')) { return response($this->fakeWordPress(), 200, ['Content-Type' => 'text/html; charset=UTF-8']); } if (str_starts_with($path, 'phpmyadmin')) { return response($this->fakePhpMyAdmin(), 200, ['Content-Type' => 'text/html; charset=UTF-8']); } return response($this->fakeGenericLogin(), 200, ['Content-Type' => 'text/html; charset=UTF-8']); } /** * Plausible-looking dotenv seeded with the per-install canaries (the bait) when configured. When a * canary is empty (unconfigured/dev), a plausible static value is served instead so the decoy still * looks authentic — it is simply not registered as a detectable honeytoken. */ private function fakeDotenv(): string { $c = (array) config('clusev.honeypot.canaries'); $appKey = ($c['app_key'] ?? '') !== '' ? $c['app_key'] : 'base64:H0n3yP0tC4n4ryK3yD0N0tUs3AAAAAAAAAAAAAAAAA0='; $dbPassword = ($c['db_password'] ?? '') !== '' ? $c['db_password'] : 'Sup3rS3cr3t-Pr0d-DB-9f2a7c'; $apiKey = ($c['api_key'] ?? '') !== '' ? $c['api_key'] : 'a7f3c9e1d5b04826bf1a3c7e9d2f6b84'; return implode("\n", [ 'APP_NAME=Application', 'APP_ENV=production', 'APP_KEY='.$appKey, 'APP_DEBUG=false', 'APP_URL=https://app.internal.example.com', '', 'DB_CONNECTION=mysql', 'DB_HOST=10.20.0.14', 'DB_PORT=3306', 'DB_DATABASE=app_production', 'DB_USERNAME=app_prod', 'DB_PASSWORD='.$dbPassword, '', 'API_KEY='.$apiKey, 'AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE', 'AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', 'AWS_DEFAULT_REGION=eu-central-1', '', ]); } /** Plausible fake git config (an [core]/[remote "origin"] INI) so the /.git/config decoy looks real. */ private function fakeGitConfig(): string { return implode("\n", [ '[core]', "\trepositoryformatversion = 0", "\tfilemode = true", "\tbare = false", "\tlogallrefupdates = true", '[remote "origin"]', "\turl = https://git.internal.example.com/app/backend.git", "\tfetch = +refs/heads/*:refs/remotes/origin/*", '[branch "main"]', "\tremote = origin", "\tmerge = refs/heads/main", '', ]); } private function fakeWordPress(): string { return <<<'HTML' Log In ‹ Blog — WordPress

Powered by WordPress

← Go to Blog

HTML; } private function fakePhpMyAdmin(): string { return <<<'HTML' phpMyAdmin

Welcome to phpMyAdmin

Log in
HTML; } private function fakeGenericLogin(): string { return <<<'HTML' Sign in
Admin Console

Sign in to continue

Authorized personnel only

HTML; } }