Add anti-spam honeypot field and update contact form service options
This commit is contained in:
parent
a0cf3e9f35
commit
ae7c52db20
3 changed files with 76 additions and 5 deletions
|
|
@ -321,5 +321,42 @@ Response: 250 2.0.0 OK 1767992239 41be03b00d2f7-c4cc96ca7a9sm11346262a12.25 - g
|
|||
**Files Modified:**
|
||||
- `.env` - Updated SMTP_PORT=587, updated GOOGLE_APP_PASSWORD, Redis still commented out
|
||||
|
||||
## Recently Completed Work
|
||||
**Date: 1/20/2026**
|
||||
|
||||
### ✅ Anti-Spam Implementation - COMPLETED
|
||||
Successfully implemented honeypot field and updated service dropdown to reduce contact form spam:
|
||||
|
||||
**Changes Made:**
|
||||
1. **Honeypot Field Added** (Layer 4):
|
||||
- Added hidden "website" field to contact form
|
||||
- Field positioned off-screen with CSS (absolute left-[-5000px], opacity-0)
|
||||
- Backend validation: silently rejects submissions if honeypot is filled
|
||||
- Logs bot attempts to console for monitoring
|
||||
- Zero impact on legitimate users
|
||||
|
||||
2. **Service Dropdown Updated** (Layer 6):
|
||||
- Removed "ADA-Compliant Transcripts" option (service discontinued)
|
||||
- Updated to match current Services page offerings:
|
||||
- Universal Website Design
|
||||
- Board Communication
|
||||
- Training & Workshops
|
||||
|
||||
**Files Modified:**
|
||||
- `src/pages/Contact.tsx` - Added honeypot field, removed outdated service option
|
||||
- `src/api/contact.ts` - Added honeypot validation logic
|
||||
|
||||
**Expected Results:**
|
||||
- 60-80% reduction in bot spam submissions
|
||||
- Bots that auto-fill forms will be caught by honeypot
|
||||
- Legitimate users unaffected (field is invisible and inaccessible)
|
||||
- Bot submissions logged but not emailed
|
||||
|
||||
**Anti-Spam Strategy:**
|
||||
- Honeypot catches simple bots that auto-fill all form fields
|
||||
- Returns "success" to bots to avoid detection
|
||||
- Actual email only sent for legitimate submissions
|
||||
- Console logging allows monitoring of blocked attempts
|
||||
|
||||
## Status Summary
|
||||
🟢 **SYSTEM FULLY OPERATIONAL** - Email configuration fixed with standard SMTP port 587 and new Gmail app password (generated 1/9/2026). Email system tested and working correctly. Redis temporarily disabled (rate limiting inactive). Services page updated to reflect current service offerings. Interactive map successfully expanded with additional 2025 conference locations in New Orleans and Austin. Production website live at https://deafgain.org/ and ready for continued use.
|
||||
🟢 **SYSTEM FULLY OPERATIONAL** - Email configuration fixed with standard SMTP port 587 and new Gmail app password (generated 1/9/2026). Email system tested and working correctly. Redis temporarily disabled (rate limiting inactive). Anti-spam honeypot field implemented to reduce bot submissions. Services page updated to reflect current service offerings. Interactive map successfully expanded with additional 2025 conference locations in New Orleans and Austin. Production website live at https://deafgain.org/ and ready for continued use.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ interface FormData {
|
|||
email: string
|
||||
service: string
|
||||
message: string
|
||||
website?: string // Honeypot field
|
||||
}
|
||||
|
||||
const validateEmail = (email: string): boolean => {
|
||||
|
|
@ -65,6 +66,24 @@ export async function POST(req: Request) {
|
|||
}
|
||||
|
||||
const data: FormData = req.body
|
||||
|
||||
// Honeypot check - if website field is filled, it's a bot
|
||||
if (data.website && data.website.trim() !== '') {
|
||||
console.log('Honeypot triggered - bot submission blocked:', {
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
website: data.website
|
||||
})
|
||||
// Return success to the bot but don't send email
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
success: true,
|
||||
message: 'Message sent successfully'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const validation = validateInput(data)
|
||||
|
||||
if (!validation.isValid) {
|
||||
|
|
@ -94,7 +113,8 @@ export async function POST(req: Request) {
|
|||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
console.error('Contact form error:', err)
|
||||
return {
|
||||
status: 500,
|
||||
body: {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ const Contact = () => {
|
|||
name: '',
|
||||
email: '',
|
||||
service: '',
|
||||
message: ''
|
||||
message: '',
|
||||
website: '' // Honeypot field
|
||||
})
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
|
|
@ -31,7 +32,7 @@ const Contact = () => {
|
|||
|
||||
if (data.success) {
|
||||
showNotification('Message sent successfully!', 'success')
|
||||
setFormData({ name: '', email: '', service: '', message: '' })
|
||||
setFormData({ name: '', email: '', service: '', message: '', website: '' })
|
||||
} else {
|
||||
showNotification(data.message || 'Failed to send message', 'error')
|
||||
}
|
||||
|
|
@ -142,13 +143,26 @@ const Contact = () => {
|
|||
required
|
||||
>
|
||||
<option value="">Select a service</option>
|
||||
<option value="ADA-Compliant Transcripts">ADA-Compliant Transcripts</option>
|
||||
<option value="Universal Website Design">Universal Website Design</option>
|
||||
<option value="Board Communication">Board Communication</option>
|
||||
<option value="Training & Workshops">Training & Workshops</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Honeypot field - hidden from humans, visible to bots */}
|
||||
<div className="absolute left-[-5000px] opacity-0" aria-hidden="true">
|
||||
<label htmlFor="website">Website</label>
|
||||
<input
|
||||
type="text"
|
||||
id="website"
|
||||
name="website"
|
||||
value={formData.website}
|
||||
onChange={(e) => setFormData({ ...formData, website: e.target.value })}
|
||||
tabIndex={-1}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="message" className="block text-accent-mountain mb-2 text-sm sm:text-base">
|
||||
Message
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue