Skip to content

Configuration ​

The published config file lives at config/not-today-honey.php.

Whitelist ​

IPs that are never blocked. They still trigger events with is_test = true, so you can test listeners safely.

php
'whitelist' => explode(',', env('NOT_TODAY_HONEY_WHITELIST', '127.0.0.1')),

Set NOT_TODAY_HONEY_WHITELIST=127.0.0.1,10.0.0.1 in .env for multiple IPs.

Rate Limiting ​

Two independent rate limits protect the traps from being overwhelmed.

Per-IP limiting caps how many times a single IP can hit any trap within a time window. When exceeded, the request receives a 429 response — no event is dispatched and no detection record is written.

Global limiting caps total trap hits across all IPs. When exceeded, a 429 is returned and TrapCampaignDetectedEvent is dispatched once (on the first breach). This is a signal that a coordinated campaign is underway.

TIP

Whitelisted IPs bypass both rate limits.

php
'rate_limiting' => [
    'per_ip' => [
        'enabled'       => env('NOT_TODAY_HONEY_RATE_IP_ENABLED', true),
        'max_hits'      => env('NOT_TODAY_HONEY_RATE_IP_MAX', 30),
        'decay_minutes' => env('NOT_TODAY_HONEY_RATE_IP_DECAY', 1),
    ],
    'global' => [
        'enabled'       => env('NOT_TODAY_HONEY_RATE_GLOBAL_ENABLED', true),
        'max_hits'      => env('NOT_TODAY_HONEY_RATE_GLOBAL_MAX', 200),
        'decay_minutes' => env('NOT_TODAY_HONEY_RATE_GLOBAL_DECAY', 1),
    ],
],
KeyDefaultPurpose
NOT_TODAY_HONEY_RATE_IP_ENABLEDtrueEnable per-IP rate limiting
NOT_TODAY_HONEY_RATE_IP_MAX30Max hits per IP per window
NOT_TODAY_HONEY_RATE_IP_DECAY1Window size in minutes
NOT_TODAY_HONEY_RATE_GLOBAL_ENABLEDtrueEnable global rate limiting
NOT_TODAY_HONEY_RATE_GLOBAL_MAX200Max total hits per window
NOT_TODAY_HONEY_RATE_GLOBAL_DECAY1Window size in minutes

Listen to TrapCampaignDetectedEvent to react to global limit breaches:

php
use Vinksyunit\NotTodayHoney\Events\TrapCampaignDetectedEvent;

Event::listen(TrapCampaignDetectedEvent::class, function (TrapCampaignDetectedEvent $event) {
    // Alert your security team — this is a coordinated attack signal
});

The event exposes two public readonly properties:

PropertyTypeDescription
$maxHitsintThreshold that triggered the campaign detection
$decayMinutesintTime window (minutes) in which hits were counted

Timing Normalization ​

Every trap response takes at least min_response_ms milliseconds before being sent. This prevents timing-based reconnaissance: an attacker probing many URLs cannot distinguish a honeypot from a real page by measuring how fast it responds.

php
'timing' => [
    'min_response_ms' => env('NOT_TODAY_HONEY_MIN_RESPONSE_MS', 1000),
],

Override per trap if needed:

env
NOT_TODAY_HONEY_WP_MIN_RESPONSE_MS=800
NOT_TODAY_HONEY_PMA_MIN_RESPONSE_MS=1200
NOT_TODAY_HONEY_GENERIC_MIN_RESPONSE_MS=null

Set a per-trap variable to null (or leave it unset) to fall back to the global value.

Credentials ​

Known usernames and passwords. When an attacker submits credentials that match this list, the alert level escalates immediately to Attacking.

Usernames ​

php
'usernames' => explode(',', env('NOT_TODAY_HONEY_USERNAMES', 'admin,administrator,webmaster,root,maintenance')),

Password detection ​

Passwords are compared using truncated SHA256 with a salt — not bcrypt. The submitted password is salted and hashed, and only the first 8 hex characters are compared. This prevents timing attacks (constant-time comparison on a short string) while making rainbow tables impractical.

php
'passwords' => [
    'include_defaults' => env('NOT_TODAY_HONEY_PASSWORD_SHORT_SHA_LIST') === null,
    'custom'           => array_filter(explode(',', env('NOT_TODAY_HONEY_PASSWORD_SHORT_SHA_LIST', ''))),
    'salt'             => env('NOT_TODAY_HONEY_PASSWORD_SALT', 'not-today-honey'),
],

include_defaults — enables the built-in list of common passwords (e.g. letmein, iloveyou). Automatically disabled once you set NOT_TODAY_HONEY_PASSWORD_SHORT_SHA_LIST.

custom — comma-separated truncated hashes generated with honey:hash-password.

salt — generated once with honey:generate-salt and stored in .env. Changing the salt invalidates all existing custom hashes.

See Compromised Passwords guide for the full setup walkthrough, or Artisan Commands for command reference.

Alert Levels ​

Three levels of increasing severity, each independently configurable:

Keyprobingintrusion_attemptattacking
threshold311
time_window (minutes)1440 (1 day)14401440
mark_as_insecuretruetruetrue
duration (minutes)201440 (24h)43200 (30 days)
log_levelinfowarningcritical

threshold — number of events before the alert fires and the IP is blocked.

time_window — events are counted within this rolling window (minutes).

mark_as_insecure — set to false to record without blocking (monitoring-only mode).

duration — how long the IP stays blocked. Set to null for a permanent block.

log_level — Laravel log level used when recording this alert. Accepts any standard Laravel log level: debug, info, notice, warning, error, critical, alert, emergency.

Traps ​

Each trap can be individually enabled, given a custom path, and configured with a login_success_behavior:

php
'traps' => [
    'wordpress' => [
        'enabled' => env('NOT_TODAY_HONEY_WP_ENABLED', true),
        'path' => env('NOT_TODAY_HONEY_WP_PATH', '/wp-admin'),
        'login_success_behavior' => TrapBehavior::from(env('NOT_TODAY_HONEY_WP_LOGIN_SUCCESS_BEHAVIOR', 'fake_success')),
        'specific' => [
            'version' => env('NOT_TODAY_HONEY_WP_VERSION', '6.4.2'),
        ],
    ],
    // phpmyadmin and generic_admin follow the same structure
],

See Traps for per-trap details and behavior options.

Storage ​

php
'storage' => [
    'connection' => env('NOT_TODAY_HONEY_DB_CONNECTION', null),
    'tables' => [
        'attacker_detections' => env('NOT_TODAY_HONEY_TABLE_ATTACKER_DETECTIONS', 'nt_honey_attacker_detections'),
        'trap_attempts'       => env('NOT_TODAY_HONEY_TABLE_TRAP_ATTEMPTS', 'nt_honey_trap_attempts'),
        'credential_attempts' => env('NOT_TODAY_HONEY_TABLE_CREDENTIAL_ATTEMPTS', 'nt_honey_credential_attempts'),
    ],
],

connection — database connection to use. Defaults to null, which uses the application's default connection. Set this if you want to isolate honeypot data on a separate database.

tables — table names for each of the three storage models. Override them if your project has a table prefix or naming convention:

env
NOT_TODAY_HONEY_DB_CONNECTION=security
NOT_TODAY_HONEY_TABLE_ATTACKER_DETECTIONS=nt_honey_attacker_detections
NOT_TODAY_HONEY_TABLE_TRAP_ATTEMPTS=nt_honey_trap_attempts
NOT_TODAY_HONEY_TABLE_CREDENTIAL_ATTEMPTS=nt_honey_credential_attempts