🛡️ Security & Anti-Spam Hardening
Because public signup forms and open user registration portals are primary targets for automated bots, scrapers, and credential-stuffing attacks, IntelliSignup implements an aggressive multi-layered cyber security architecture designed to prevent abuse while ensuring zero friction for human users.
IntelliSignup pairs silent deception (honeypots that waste bot computational resources) with deterministic defenses (curated disposable email blocklists, IP rate-limiting, and constant-time cryptographic checks) to keep server databases clean and deliverability reputations high.
1. Invisible Registration Honeypot Trap
Automated registration bots parse DOM structures to fill in all discovered input tags. IntelliSignup deploys a deceptive trap field named phone_confirm inside app/templates/register.html:
<!-- Honeypot anti-spam trap: Hidden from legitimate humans, populated by automated bots -->
<div style="position: absolute; left: -9999px; top: -9999px; opacity: 0; pointer-events: none;" aria-hidden="true">
<label for="phone_confirm">Do not fill this field</label>
<input type="text" name="phone_confirm" id="phone_confirm" tabindex="-1" autocomplete="off">
</div>
Deception Defense Mechanism
When an automated bot fills this invisible field, the server intercepts the request inside app/routes.py:
- Logs a high-priority security warning containing the attacker's IP and submitted email.
- Zero Database Writes: No user record is inserted.
- Zero Outbound Emails: No verification emails are dispatched, preserving SMTP quotas and reputation.
- Deceptive Success Flash: Redirects the bot to
/loginwith the message: "Account created. Check your email to verify your address...". The bot assumes its automated task succeeded and stops attempting form bypasses.
2. Disposable Email & Tag Filtering
Implemented inside app/security.py, registration validation enforces two strict email integrity checks:
A. Curated Disposable Email Blocklist
IntelliSignup maintains an in-memory set of over 50 temporary and disposable email providers (including mailinator.com, tempmail.com, guerrillamail.com, yopmail.com, 10minutemail.com, and sharklasers.com). Attempts to register with these domains are instantly blocked with an explanatory error:
def is_disposable_email(email):
if not email:
return False
parts = email.strip().lower().split('@')
if len(parts) != 2:
return False
return parts[1] in DISPOSABLE_DOMAINS
B. Email Sub-Addressing Block (+)
Spammers frequently bypass single-email limits using "plus-aliasing" (e.g. user+bot1@gmail.com). IntelliSignup rejects registration emails containing the + symbol, preventing attackers from multiplying accounts through a single primary mailbox.
3. Sliding-Window Rate Limiting
To defend against brute-force attacks and volumetric request flooding, IntelliSignup tracks events dynamically in the RateLimitEvent database table. The system automatically trims audit rows older than 48 hours to conserve disk space.
| Rate Limit Scope | Key Format | Threshold | Window | Security Objective |
|---|---|---|---|---|
register_ip |
Client IP | 5 attempts | 3,600s (1 hour) | Prevents single-IP botnets from mass-registering different accounts. |
register |
Email Address | 5 attempts | 3,600s (1 hour) | Blocks spam flooding targeting a specific inbox. |
login |
Client IP + Email | 8 attempts | 900s (15 min) | Thwarts brute-force password guessing and credential stuffing. |
verification_resend |
Client IP + Email | 5 attempts | 3,600s (1 hour) | Prevents abuse of verification email dispatch mechanisms. |
password_reset_request |
Client IP + Email | 5 attempts | 3,600s (1 hour) | Shields SMTP relays against password reset harassment attacks. |
public_claim |
Client IP | 20 claims | 900s (15 min) | Prevents malicious script claim hoarding on public event pages. |
def rate_limit_hit(scope, key, limit, window_seconds):
now = datetime.utcnow()
window_start = now - timedelta(seconds=window_seconds)
count = RateLimitEvent.query.filter_by(scope=scope, key=key)\
.filter(RateLimitEvent.created_at >= window_start).count()
if count >= limit:
latest = RateLimitEvent.query.filter_by(scope=scope, key=key)\
.filter(RateLimitEvent.created_at >= window_start)\
.order_by(RateLimitEvent.created_at.asc()).first()
retry_after = max(int((latest.created_at + timedelta(seconds=window_seconds) - now).total_seconds()), 1)
return True, retry_after
db.session.add(RateLimitEvent(scope=scope, key=key))
db.session.commit()
return False, None
4. Cloudflare Turnstile CAPTCHA
IntelliSignup integrates Cloudflare's privacy-focused Turnstile service to detect non-human traffic passively without irritating users with image puzzles or distorted text challenges.
- Graceful Degradation: If
TURNSTILE_SITE_KEYorTURNSTILE_SECRET_KEYare not defined in.env(such as in local development), the verification step automatically returnsTruewithout failing requests. - Managed Production Mode: When active, the client widget generates a cryptographic response token submitted with the registration payload.
- Server-Side Verification: The Flask backend validates the token directly against
https://challenges.cloudflare.com/turnstile/v0/siteverifyvia POST request with a 5-second socket timeout.
5. Passwords & Cryptographic Verification
User accounts are secured with industrial-grade cryptographic implementations:
- Password Hashing: Passwords are hashed using Werkzeug's secure hashing suite (scrypt/pbkdf2:sha256) with salted iterations. Passwords are never stored in plaintext.
- Cryptographic Email Verification: Uses
itsdangerous.URLSafeTimedSerializerwith a dedicatedemail-verifysalt. Verification links expire automatically after 24 hours (configurable viaEMAIL_VERIFICATION_MAX_AGE). - Password Reset Workflows: Time-sensitive reset tokens carry an embedded
purpose: password-resetpayload that self-invalidates after 1 hour (PASSWORD_RESET_MAX_AGE = 3600). - Complete Account Deletion: Users can trigger
/account/deletefrom their settings, executing a cascading purge that deletes all owned signups, items, claims, custom forms, and encrypted OAuth tokens.
6. AES-256 Fernet Encryption at Rest
Third-party integration credentials (such as Google OAuth refresh tokens and access tokens stored in GoogleCalendarConnection) must be safeguarded against database dumps or snapshot compromises.
# Encrypted fields in the database carry an explicit prefix
ENCRYPTED_PREFIX = 'enc::'
def encrypt_value(value):
if not value or value.startswith(ENCRYPTED_PREFIX):
return value
fernet = _fernet() # Initialized with APP_ENCRYPTION_KEY
if not fernet:
return value
encrypted = fernet.encrypt(value.encode('utf-8')).decode('utf-8')
return f'{ENCRYPTED_PREFIX}{encrypted}'
def decrypt_value(value):
if not value or not value.startswith(ENCRYPTED_PREFIX):
return value
fernet = _fernet()
return fernet.decrypt(value[len(ENCRYPTED_PREFIX):].encode('utf-8')).decode('utf-8')
7. CSRF & Open-Redirect Protections
Every mutating HTTP POST request (claim creation, event updates, form modifications, account actions) is validated against a session-stored CSRF token:
- Constant-Time Verification: Evaluated using
hmac.compare_digest(expected, provided)to prevent timing attacks. - Open-Redirect Mitigation: All post-login or post-action redirects pass through
is_safe_redirect(target), ensuring target URLs match the exact hostname and scheme of the host server and blocking external phishing redirections.