turnley.dev

PHP and MySQL code for faucet operators

Tools & Calculators

FRD-006

A cookieless visitor key you can count on

One rotating hash that identifies a visitor for limits and counting, without a cookie to clear and without storing anything that identifies a person.

Per-visitor limits need a key, and a cookie is the worst possible one: clearing it is a menu item. Anyone farming your site will clear it between page loads.

A hash of the address, the user agent and a salt that rotates daily gives you a key that survives a cookie wipe, cannot be reversed into an address, and expires on its own. It is not an identity — two people behind one router share it — which is exactly the right trade for a fraud limit.

The daily rotation is deliberate. It stops the key from becoming a long-term identifier, which is both a privacy improvement and one less thing to explain in your policy page.

PHP
function visitor_key(?string $ip = null, ?string $ua = null): string
{
    $ip = $ip ?? client_ip();
    $ua = $ua ?? (string) ($_SERVER['HTTP_USER_AGENT'] ?? '');

    // Salt rotates daily: the key cannot be correlated across days.
    $salt = date('Y-m-d') . '|' . setting('visitor_salt');

    // IPv6 addresses rotate freely within a /64, so group them.
    if (str_contains($ip, ':')) {
        $parts = explode(':', $ip);
        $ip = implode(':', array_slice($parts, 0, 4)) . '::/64';
    }

    return hash('sha256', $salt . '|' . $ip . '|' . $ua, false);
}

// Generate the salt once, on first use, and keep it out of version control.
function ensure_visitor_salt(): void
{
    if (trim(setting('visitor_salt')) === '') {
        setting_set('visitor_salt', bin2hex(random_bytes(16)), 'security', 'hidden');
    }
}

Using it

Store the key, never the inputs. A table of hashes is not a table of addresses, and that difference matters if the database ever leaks.

Truncate to 32 characters if you are indexing it heavily. Collisions at that length are irrelevant for a daily fraud key.

What bites people

Everyone behind one office router or one mobile carrier NAT shares a key. Do not use this to ban, only to limit.

Rotating the salt daily means your limits reset at midnight UTC for everyone at once. If that produces a spike, rotate on a per-visitor offset instead.

Also in Anti-Bot and Fraud