Forms - v1.0.0-beta1
Spam Protection
Protect your forms from spam submissions and abuse.
Built-in Protection
The package includes two spam protection mechanisms:
- Honeypot Field - A hidden field that bots typically fill out
- Rate Limiting - Limits submissions per IP address
Honeypot Protection
How It Works
A hidden field is added to forms. Legitimate users don't see it, but bots automatically fill it out. Submissions with this field filled are rejected.
Configuration
// config/artisanpack/forms.php
'spam_protection' => [
'honeypot' => [
'enabled' => true,
'field_name' => 'website_url', // Appealing name for bots
],
],
Implementation
The honeypot field is automatically added to forms:
{{-- Hidden from users with CSS --}}
<div style="position: absolute; left: -9999px;" aria-hidden="true">
<label for="website_url">Website (leave blank)</label>
<input
type="text"
id="website_url"
name="website_url"
tabindex="-1"
autocomplete="off"
>
</div>
Validation
// In FormRenderer component
protected function validateHoneypot(): bool
{
if (!config('artisanpack.forms.spam_protection.honeypot.enabled')) {
return true;
}
$fieldName = config('artisanpack.forms.spam_protection.honeypot.field_name');
// If honeypot is filled, it's likely a bot
if (!empty($this->formData[$fieldName] ?? null)) {
Log::warning('Honeypot triggered', [
'form_id' => $this->form->id,
'ip' => request()->ip(),
]);
return false;
}
return true;
}
Rate Limiting
How It Works
Limits the number of submissions from a single IP address within a time window.
Configuration
// config/artisanpack/forms.php
'spam_protection' => [
'rate_limit' => [
'enabled' => true,
'attempts' => 5, // Max submissions
'decay' => 60, // Time window in seconds
],
],
Implementation
use Illuminate\Support\Facades\RateLimiter;
protected function checkRateLimit(): bool
{
if (!config('artisanpack.forms.spam_protection.rate_limit.enabled')) {
return true;
}
$key = 'form-submit:' . $this->form->id . ':' . request()->ip();
$attempts = config('artisanpack.forms.spam_protection.rate_limit.attempts');
$decay = config('artisanpack.forms.spam_protection.rate_limit.decay');
if (RateLimiter::tooManyAttempts($key, $attempts)) {
Log::warning('Rate limit exceeded', [
'form_id' => $this->form->id,
'ip' => request()->ip(),
]);
return false;
}
RateLimiter::hit($key, $decay);
return true;
}
User Feedback
When rate limited:
@if ($rateLimited)
<div class="error">
Too many submissions. Please try again later.
</div>
@endif
reCAPTCHA Integration
For additional protection, integrate Google reCAPTCHA:
Installation
composer require google/recaptcha
Configuration
Add to .env:
RECAPTCHA_SITE_KEY=your-site-key
RECAPTCHA_SECRET_KEY=your-secret-key
Form Integration
{{-- In form view --}}
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="{{ config('services.recaptcha.site_key') }}"></div>
Validation
use function addFilter;
addFilter('forms.validation_rules', function ($rules, $form) {
if ($form->settings['recaptcha_enabled'] ?? false) {
$rules['g-recaptcha-response'] = 'required|recaptcha';
}
return $rules;
});
// Custom validation rule
Validator::extend('recaptcha', function ($attribute, $value) {
$recaptcha = new \ReCaptcha\ReCaptcha(config('services.recaptcha.secret_key'));
$response = $recaptcha->verify($value, request()->ip());
return $response->isSuccess();
});
hCaptcha Integration
Alternative to reCAPTCHA:
Configuration
HCAPTCHA_SITE_KEY=your-site-key
HCAPTCHA_SECRET_KEY=your-secret-key
Form Integration
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
<div class="h-captcha" data-sitekey="{{ config('services.hcaptcha.site_key') }}"></div>
Custom Spam Checks
Add custom spam detection:
use function addFilter;
// Check for spam keywords
addFilter('forms.spam_check', function ($isSpam, $form, $data) {
$spamKeywords = ['casino', 'viagra', 'crypto'];
foreach ($data as $value) {
if (is_string($value)) {
foreach ($spamKeywords as $keyword) {
if (stripos($value, $keyword) !== false) {
return true; // Is spam
}
}
}
}
return $isSpam;
});
// Check against spam database
addFilter('forms.spam_check', function ($isSpam, $form, $data) {
$email = $data['email'] ?? null;
if ($email && SpamDatabase::isSpammer($email)) {
return true;
}
return $isSpam;
});
Security Logging
Enable logging for security events:
// config/artisanpack/forms.php
'security' => [
'logging_enabled' => true,
],
Logged events:
- Honeypot triggered
- Rate limit exceeded
- Invalid file uploads
- Suspicious submissions
Log Format
[warning] Honeypot triggered {
"form_id": 1,
"ip": "192.168.1.100",
"user_agent": "Mozilla/5.0..."
}
Best Practices
- Layer Protection: Use multiple methods together
- Monitor Logs: Review security logs regularly
- Adjust Limits: Tune rate limits based on legitimate traffic
- Update Keywords: Keep spam keyword lists current
- Use HTTPS: Always serve forms over HTTPS
Disabling Protection
For testing or trusted environments:
// Disable all spam protection
'spam_protection' => [
'honeypot' => ['enabled' => false],
'rate_limit' => ['enabled' => false],
],
Next Steps
- Webhooks - External integrations
- Customization - Extend protection
- Configuration - All settings