Last year, I began building a website for an organisation. It contains a contact form implemented in PHP, with a Securimage captcha.
Today I checked the database table into which contact messages are archived, and discovered it to be full of spam! The spams started in August 2022, about a month after the contact form was put live, and the rate of spams peaked in August-September at 11 per day. Now they're one every 1-2 days, which is still a far cry from the level that having a captcha is meant to get it down to. Going by the recent logs, it doesn't seem to be a brute-force attack or anything like that.
The code to render the contact form is like this:
<form method="post" action="contact">
<div class="tabForm">
<?php
renderInlineField('name', 'Your name:');
renderInlineField('email', 'Email address:');
renderInlineField('subject', 'Subject:');
?>
<p><?php renderLabel('message', 'Message:'); ?> <textarea style="width: 100%; height: 15em;" name="message" id="message"><?php echo htmlspecialchars($message); ?></textarea></p>
<p><img id="captcha" src="securimage/securimage_show.php" alt="[CAPTCHA Image]" /></p>
<p><audio id="captcha_one_audio" preload="none" controls="controls">
<source id="captcha_one_source_wav" src="securimage/securimage_play.php?id=<?php echo uniqid(); ?>" type="audio/wav" />
</audio></p>
<p><?php renderLabel('captcha', 'Please enter the characters you see/hear:'); ?> <input type="text" name="captcha" id="captcha" size="10" maxlength="6" /></p>
<p><input type="submit" value="Submit" /></p>
</div>
</form>
The two functions (besides uniqid
) are defined as follows:
function renderInlineField($fieldName, $htmlLabel) {
global $errors;
echo "<p class='inline'>";
renderLabel($fieldName, $htmlLabel);
echo "<input type='text' name='$fieldName' id='$fieldName' maxlength='255' value='", htmlspecialchars(@$_POST[$fieldName]), "' /></p>";
}
function renderLabel($fieldName, $htmlLabel) {
global $errors;
echo "<label for='$fieldName'>";
if (!empty($errors[$fieldName])) echo "<span class='error'>($errors[$fieldName])</span> ";
echo "$htmlLabel</label>";
}
The code that verifies the captcha:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$name = sanitise($_POST['name']);
$email = trim($_POST['email']);
$subject = sanitise($_POST['subject']);
$message = trim($_POST['message']);
$captcha = $_POST['captcha'];
$errors = [];
session_start();
require_once 'securimage/securimage.php';
$securimage = new Securimage();
if (empty($name)) $errors['name'] = 'missing';
if (empty($email)) {
$errors['email'] = 'missing';
} else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'not valid';
}
if (empty($message)) $errors['message'] = 'missing';
if (empty($captcha)) {
$errors['captcha'] = 'missing';
} else if (!$securimage->check($captcha)) {
$errors['captcha'] = 'not entered correctly';
}
if (empty($errors)) {
// code to store the message in the database and email it to the intended recipient
}
}
My questions are:
- Can anyone see any holes in this code which a spambot would be able to exploit?
- Has anyone found a solution that works?