SPAM protection methods N° 3: Timing is once again everything
Page navigation, too, lets us exploit the speed difference between a bot and a real user quite well. While a user normally stays on a page for a few seconds until they've taken in and processed all the information (and possibly even filled out a form), a bot does the whole thing in a fraction of a second. We can exploit that in several ways.
1. When is it a bot, when a human
Basically it makes sense to work with average times here. A 404 error page is probably only looked at for 3 seconds at most, while a long text (such as company news, About Us or even the imprint) is probably read for considerably longer. If a page navigation happens several times after less than 3 seconds, you can assume something is off! Alternatively, we can also measure the time the user spends on the page. If a page navigation always happens after about the same time (even if it's longer than 3 seconds), regardless of how long the document is, something is probably off too. Who navigates after the same amount of time every time, regardless of the page content? Top-level developers would also consider measuring the dwell time of all users individually for each page and forming an average. Any user who deviates by more than X% here can be regarded as a bot. But be careful: if someone misclicks and leaves the page right away, or notices on the page that the content isn't what they expected, they may navigate on faster than average and would then wrongly be regarded as a bot. You might have to think the concept through a bit further :-)
2. PHP: variant number uno
<?php
session_start();
if (time() - $_SESSION['last_navigation'] < 5) if (!isset($_SESSION['quicknavigation']) ? $_SESSION['quicknavigation'] = 1 : $_SESSION['quicknavigation']++);
if (time() - $_SESSION['last_navigation'] > 10) $_SESSION['quicknavigation'] = 0;
if (isset($_SESSION['quicknavigation']) && $_SESSION['quicknavigation'] == 8) die("You are most likely a bot!");
$_SESSION['last_navigation'] = time();
?>
So, what's happening here? The above script is called on every page. If the difference between the current Unix timestamp and the Unix timestamp of the last page view is less than 5 seconds (so the user navigated to the next page in under 5 seconds), the value of the session variable "quicknavigation" (it is, after all, a ... well? Quick navigation!) is increased by one (or set to 1 if not yet defined). If it's more than 5 seconds but less than 10 seconds, nothing happens at first - since in this window I assume it's either a bot or a human (maybe the bot is computing something, needs time to store the found email address or the like...). If the next navigation only happens after 10 or more seconds, however, we reset the "quicknavigation value" to 0, because we can assume that, even if there were a few quick navigations and quicknavigation has a higher value, no bot takes 10 seconds to analyze a page. Ergo it's probably a human despite the previous quick navigation, and we set the quicknavigation value back to 0 (I know, bots can also have timeouts set to overcome exactly such security measures. But from experience, bots are mainly about getting information quickly or finding the comment function quickly to spread spam, where the principle is quantity over quality. So I decided on a complete reset of the data after 10 seconds). If the user/bot has now navigated 8 times in a row within less than 5 seconds each, we output the bot message (and maybe ask for a captcha?).
3. PHP: variant number due
<?php
session_start();
if ($_SESSION['block'] === true) die('BOT');
if (count($_SESSION['navigation']) == 5) array_shift($_SESSION['navigation']);
if ($_SERVER['SCRIPT_FILENAME'] != $_SESSION['last_called_script']){
$_SESSION['navigation'][] = time();
$diff[] = $_SESSION['navigation'][1] - $_SESSION['navigation'][0];
$diff[] = $_SESSION['navigation'][2] - $_SESSION['navigation'][1];
$diff[] = $_SESSION['navigation'][3] - $_SESSION['navigation'][2];
$diff[] = $_SESSION['navigation'][4] - $_SESSION['navigation'][3];
$diff[] = $_SESSION['navigation'][5] - $_SESSION['navigation'][4];
if (abs($diff[0] - $diff[1]) < 5 && abs($diff[1] - $diff[2]) < 5 && abs($diff[2] - $diff[3]) < 5 && abs($diff[3] - $diff[4]) < 5 && abs($diff[4] - $diff[5]) < 5) $_SESSION['block'] = true;
}
?>
Oh what the ... what's happening here? We measure the average time of the last navigations. If the page was switched 5 times within less than 5 seconds each, we set the session variable 'block' to "true" and lock the user out of all further pages. Here too it would be appropriate to start with a captcha rather than excluding the user right away.
4. Conclusion
With measures like this we should always keep in mind that while we can detect potential bots, false positives can of course never be ruled out. So a user should basically never be locked out of the site completely just because they behave like a bot in terms of navigation timing. But based on the navigation timing we can raise a first suspicion and take suitable measures, e.g. via a captcha prompt, to effectively protect our site from bots. Do you have further ideas or suggestions on this approach? Write them in the comments, which will be approved after I've reviewed them.