IPASIS - IP Reputation and Risk Intelligence API
Blog/Guide

How to Prevent Fake Signups: The Complete Guide for SaaS and E-commerce

March 28, 202618 min read

Fake signups are quietly destroying your business metrics — inflating acquisition costs, polluting analytics, consuming infrastructure resources, and degrading the experience for legitimate users. For SaaS companies offering free trials and e-commerce platforms with promotional offers, fake accounts aren't just a nuisance. They're a serious operational and financial drain.

This guide provides a comprehensive, actionable approach to detecting and preventing fake signups at scale. You'll learn the attack vectors, detection techniques, implementation strategies, and the API integration patterns that engineering teams use to stop fraudulent registrations before they pollute your database.

Why Fake Signups Are a Growing Problem

Fake signups have become increasingly sophisticated and prevalent. Understanding the scale of the problem is the first step to building effective defenses.

The Real Cost of Fake Accounts

According to recent research, 20-30% of new account registrations on SaaS platforms with free trials are fraudulent or bot-generated. For e-commerce sites running promotional campaigns ("10% off your first order"), that number can spike to 40-60% during peak periods.

The financial impact compounds quickly:

  • Infrastructure costs: Each fake account consumes database storage, server resources, email sending credits, and customer support time
  • Analytics pollution: Fake signups destroy funnel metrics, making it impossible to calculate true conversion rates, CAC, or retention cohorts
  • Free trial abuse: Sophisticated users create dozens of accounts to extend free trials indefinitely, never converting to paid
  • Promo code theft: E-commerce platforms lose 5-15% of promotional budgets to serial abusers creating fake accounts for discount codes
  • Deliverability damage: Sending transactional emails to disposable addresses hurts sender reputation, impacting deliverability for real users

A mid-sized SaaS company with 10,000 signups per month and a 25% fake account rate is wasting approximately $5,000-$15,000 monthly on infrastructure, email services, and support resources dedicated to fraudulent users. That's $60K-$180K annually — enough to fund a full-time engineer dedicated to improving the product for legitimate customers.

Why Traditional Defenses Aren't Enough

Many teams implement basic signup validation and assume it's sufficient. But attackers have evolved:

  • Email verification links can be clicked programmatically by bots
  • Simple CAPTCHAs (especially reCAPTCHA v2) are easily solved by CAPTCHA-solving services for $1-$3 per 1,000 challenges
  • Rate limiting by IP is bypassed using residential proxy networks with millions of rotating IPs
  • Browser fingerprinting alone misses server-side API abuse and headless browser automation

The only reliable approach is multi-layered detection that combines server-side intelligence (IP reputation, email validation) with behavioral analysis and risk scoring.

Common Sources of Fake Signups

Understanding who is creating fake accounts and why helps you tailor your detection strategy.

1. Automated Bots

Script-based bots create accounts at scale for various purposes: data harvesting, testing stolen credentials, inflating metrics for competitors, or simply because your signup form is exposed and unprotected.

Detection signals: Datacenter IPs, rapid-fire submissions (milliseconds between form fields), missing browser headers, identical user-agent strings across accounts.

2. Competitor Scrapers

Competitors create accounts to access gated content, reverse-engineer your product, scrape pricing information, or monitor feature rollouts. Often more sophisticated than simple bots — they use residential proxies and headless browsers to appear legitimate.

Detection signals: VPN/proxy usage, email domains that don't match claimed company affiliation, rapid navigation through specific features immediately after signup, no engagement with core product functionality.

3. Free Trial Abusers

Users who create multiple accounts to extend free trials indefinitely. They often use disposable email services (Mailinator, Guerrilla Mail, TempMail) and may rotate through VPNs to avoid IP-based detection.

Detection signals: Disposable email addresses, similar usage patterns across accounts (same features accessed, same time of day), device fingerprint matches despite different email/IP combinations.

4. Promo Code Hunters

E-commerce shoppers who create dozens of accounts to claim "first purchase" discounts or referral bonuses. They often use Gmail/Outlook aliases (user+variation@gmail.com) or temporary email services.

Detection signals: Email pattern matching (sequential numbered emails, plus-addressing), same billing address or payment method across accounts, immediate checkout after signup with no browsing.

5. API Abusers

For platforms with free-tier APIs (like IPASIS itself), some users create hundreds of accounts to aggregate free quotas rather than paying for a legitimate plan.

Detection signals: Accounts created in rapid succession from the same IP or IP range, API keys activated immediately without product exploration, usage patterns that hit free-tier limits exactly.

Detection Methods: Building Your Defense Stack

Effective fake signup prevention requires multiple detection layers. No single technique catches everything, but combining these approaches creates a robust defense.

Layer 1: IP Intelligence

IP reputation analysis is the first line of defense. Before a user even submits the signup form, you can assess whether their IP address exhibits characteristics associated with fraudulent activity.

Key signals to check:

  • Datacenter hosting: Is the IP from AWS, DigitalOcean, or another cloud provider? Legitimate users rarely sign up from EC2 instances
  • VPN detection: Is the traffic routed through a commercial VPN service? While VPNs have legitimate uses, they're also commonly used for trial abuse
  • Proxy detection: Is this a known proxy server, especially a residential or mobile proxy often used by sophisticated attackers?
  • Tor exit nodes: Tor traffic is nearly always suspicious for signup flows
  • Abuse history: Has this IP been associated with spam, fraud, or malicious activity in the past?
  • Geographic anomalies: Does the IP location match the claimed country/language in the signup form?

The advantage of IP intelligence is that it's server-side — impossible for attackers to bypass. They can't fake their IP origin once the request reaches your server.

// Example: IP intelligence check during signup

// Node.js signup handler
app.post('/api/signup', async (req, res) => {
  const clientIP = req.headers['x-forwarded-for']?.split(',')[0] || req.socket.remoteAddress;
  
  // Check IP reputation
  const ipCheck = await fetch(
    `https://api.ipasis.com/v1/lookup?ip=${clientIP}`,
    { headers: { 'Authorization': `Bearer ${process.env.IPASIS_API_KEY}` } }
  );
  
  const ipData = await ipCheck.json();
  
  // Calculate risk score
  let riskScore = ipData.risk_score;
  
  if (ipData.is_datacenter) riskScore += 20;
  if (ipData.is_vpn) riskScore += 15;
  if (ipData.is_proxy) riskScore += 25;
  if (ipData.is_tor) riskScore += 40;
  
  // Decision logic
  if (riskScore > 85) {
    return res.status(403).json({
      error: 'Signup blocked',
      message: 'Unable to create account from this location'
    });
  }
  
  if (riskScore > 60) {
    // High risk: require additional verification
    await sendEmailVerification(req.body.email);
    await flagForManualReview(req.body.email, ipData);
  }
  
  // Proceed with signup...
});

For a deep dive into IP-based detection techniques, see our guide on how to detect bots on your website.

Layer 2: Email Validation

Email addresses provide rich signals for fraud detection. Attackers often use disposable email addresses (DEAs) from services like Mailinator, 10MinuteMail, or Guerrilla Mail because they're free, require no setup, and expire automatically.

Comprehensive email validation includes:

  • Disposable email detection: Check against a database of 900+ known temporary email providers
  • Domain age: Newly registered domains (<30 days old) are often created specifically for fraud campaigns
  • MX record validation: Does the domain actually have valid mail servers configured?
  • Free vs. business email: Gmail/Yahoo are free; company domains suggest legitimate business users
  • Email pattern matching: Detect suspicious patterns like sequential numbers (user1@, user2@, user3@) or plus-addressing abuse
  • Domain reputation: Has this domain been associated with spam or abuse?

// Example: Combined IP + email validation

// Node.js: Validate both IP and email in a single call
const validateSignup = async (email, ip) => {
  const response = await fetch(
    `https://api.ipasis.com/v1/lookup?ip=${ip}&email=${email}`,
    { headers: { 'Authorization': `Bearer ${API_KEY}` } }
  );
  
  const data = await response.json();
  
  return {
    ipRisk: data.risk_score,
    isDisposableEmail: data.email?.is_disposable,
    isFreeEmail: data.email?.is_free,
    emailDomainAge: data.email?.domain_age_days,
    isVPN: data.is_vpn,
    isDatacenter: data.is_datacenter,
    
    // Combined risk assessment
    shouldBlock: (
      data.risk_score > 85 ||
      (data.email?.is_disposable && data.is_datacenter) ||
      (data.is_vpn && data.email?.domain_age_days < 30)
    ),
    
    shouldChallenge: (
      data.risk_score > 60 ||
      data.email?.is_disposable ||
      data.is_vpn
    )
  };
};

// Usage in signup flow
const validation = await validateSignup(email, clientIP);

if (validation.shouldBlock) {
  return res.status(403).json({ error: 'Signup not allowed' });
}

if (validation.shouldChallenge) {
  // Require SMS verification or CAPTCHA
  await requireAdditionalVerification(email);
}

Read our detailed guide on detecting disposable email addresses for more email validation techniques.

Layer 3: Behavioral Signals

Legitimate users behave differently from bots. By tracking how users interact with your signup form, you can identify automation patterns.

Client-side behavioral signals to collect:

  • Form interaction timing: How long between focusing on the email field and submitting? Bots often fill forms in <100ms
  • Mouse movement: Do mouse movements appear natural? Bots often move in perfectly straight lines or not at all
  • Keystroke dynamics: Real users have variable typing speeds; bots paste entire strings instantly
  • JavaScript execution: Can the client execute JavaScript? Many simple scrapers can't
  • Browser properties: Screen resolution, timezone, language settings — do they match claimed location?
  • Navigation history: Did the user arrive directly at /signup or browse the site first?

// Example: Client-side behavioral tracking

// Collect behavioral signals client-side
class SignupBehaviorTracker {
  constructor() {
    this.signals = {
      formStartTime: Date.now(),
      mouseMovements: 0,
      keystrokes: 0,
      fieldInteractions: {},
      pastedFields: []
    };
    this.attachListeners();
  }
  
  attachListeners() {
    document.addEventListener('mousemove', () => {
      this.signals.mouseMovements++;
    });
    
    document.querySelectorAll('input').forEach(input => {
      input.addEventListener('focus', (e) => {
        this.signals.fieldInteractions[e.target.name] = Date.now();
      });
      
      input.addEventListener('keydown', () => {
        this.signals.keystrokes++;
      });
      
      input.addEventListener('paste', (e) => {
        this.signals.pastedFields.push(e.target.name);
      });
    });
  }
  
  getSignals() {
    return {
      ...this.signals,
      formDuration: Date.now() - this.signals.formStartTime,
      hasMouseActivity: this.signals.mouseMovements > 10,
      hasTypingActivity: this.signals.keystrokes > 5,
      suspiciousPaste: this.signals.pastedFields.length > 2
    };
  }
}

// Initialize on page load
const tracker = new SignupBehaviorTracker();

// Send with signup request
document.getElementById('signup-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  
  const formData = new FormData(e.target);
  const behaviorData = tracker.getSignals();
  
  await fetch('/api/signup', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email: formData.get('email'),
      password: formData.get('password'),
      behaviorSignals: behaviorData
    })
  });
});

Server-side, you can score these signals to identify bot-like behavior:

// Server-side behavioral scoring

// Score behavioral signals
function scoreBehavior(signals) {
  let botScore = 0;
  
  // Too fast = bot
  if (signals.formDuration < 2000) botScore += 30;
  
  // No mouse movement = bot
  if (!signals.hasMouseActivity) botScore += 25;
  
  // No typing (only paste) = bot
  if (!signals.hasTypingActivity && signals.suspiciousPaste) botScore += 20;
  
  // Multiple pasted fields = suspicious
  if (signals.suspiciousPaste) botScore += 15;
  
  return {
    score: botScore,
    isLikelyBot: botScore > 40
  };
}

// Combine with IP/email validation
app.post('/api/signup', async (req, res) => {
  const { email, behaviorSignals } = req.body;
  const clientIP = getClientIP(req);
  
  // Parallel validation
  const [ipData, behaviorScore] = await Promise.all([
    checkIPReputation(clientIP, email),
    Promise.resolve(scoreBehavior(behaviorSignals))
  ]);
  
  // Combined decision
  const totalRisk = ipData.risk_score + behaviorScore.score;
  
  if (totalRisk > 100 || behaviorScore.isLikelyBot) {
    return res.status(403).json({ error: 'Automated signup detected' });
  }
  
  // Continue signup...
});

Layer 4: CAPTCHA (Use Sparingly)

CAPTCHAs are overused and increasingly ineffective. Modern CAPTCHA-solving services defeat reCAPTCHA v2 for $1-$3 per 1,000 solves. reCAPTCHA v3 (which is invisible) generates scores but requires you to implement your own decision logic — essentially shifting the detection burden back to you.

When to use CAPTCHAs:

  • As a challenge mechanism for high-risk signups rather than showing them to everyone
  • When IP + email + behavioral signals indicate moderate risk (score 60-80) but not high enough to block outright
  • During attack surges when you detect abnormal signup velocity

CAPTCHA limitations:

  • Degrades user experience, especially on mobile
  • Accessibility concerns for users with visual impairments
  • Easily bypassed by CAPTCHA-solving services integrated into bot frameworks
  • False sense of security — teams often assume "we have reCAPTCHA" means the problem is solved

The best approach: Use IP/email/behavior validation as primary defense, and only show CAPTCHAs to ambiguous cases that score in the moderate risk range.

How IPASIS Helps Prevent Fake Signups

IPASIS combines IP intelligence and email validation in a single API call, giving you the signals needed to assess signup risk in real-time. The API is designed for inline evaluation — responses in <50ms (p95) so you can make decisions before the signup completes.

What IPASIS Detects

  • Datacenter, VPN, proxy, and Tor traffic with confidence scores
  • Disposable email addresses from 900+ temporary email providers
  • Email domain age and reputation to catch newly created fraud domains
  • Residential proxy detection using advanced heuristics (Bright Data, Oxylabs, etc.)
  • IP abuse history aggregated from spam, fraud, and malicious activity databases
  • Geographic consistency between IP location and claimed user location

Integration Example: Full Signup Protection

// Complete signup protection with IPASIS

// Node.js + Express
const express = require('express');
const app = express();

app.post('/api/signup', async (req, res) => {
  const { email, password, behaviorSignals } = req.body;
  const clientIP = req.headers['x-forwarded-for']?.split(',')[0] || 
                   req.socket.remoteAddress;
  
  try {
    // Step 1: Validate with IPASIS
    const ipasisResponse = await fetch(
      `https://api.ipasis.com/v1/lookup?ip=${encodeURIComponent(clientIP)}&email=${encodeURIComponent(email)}`,
      {
        headers: { 
          'Authorization': `Bearer ${process.env.IPASIS_API_KEY}` 
        }
      }
    );
    
    if (!ipasisResponse.ok) {
      throw new Error('Validation service unavailable');
    }
    
    const validation = await ipasisResponse.json();
    
    // Step 2: Calculate combined risk score
    let riskScore = validation.risk_score;
    
    // IP-based risk factors
    if (validation.is_datacenter) riskScore += 25;
    if (validation.is_vpn) riskScore += 15;
    if (validation.is_proxy) riskScore += 20;
    if (validation.is_tor) riskScore += 40;
    
    // Email-based risk factors
    if (validation.email?.is_disposable) riskScore += 30;
    if (validation.email?.domain_age_days < 30) riskScore += 20;
    if (validation.email?.is_free && validation.is_vpn) riskScore += 10;
    
    // Behavioral factors
    if (behaviorSignals?.formDuration < 2000) riskScore += 20;
    if (!behaviorSignals?.hasMouseActivity) riskScore += 15;
    
    // Step 3: Decision logic
    if (riskScore > 100) {
      // High risk: block
      await logBlockedSignup(email, clientIP, validation, riskScore);
      return res.status(403).json({
        error: 'SIGNUP_BLOCKED',
        message: 'Unable to create account. Please contact support if you believe this is an error.'
      });
    }
    
    if (riskScore > 70) {
      // Moderate risk: require email verification + manual review
      const user = await createPendingUser({ email, password });
      await sendVerificationEmail(email, user.verificationToken);
      await flagForManualReview(user.id, {
        ip: clientIP,
        riskScore,
        ipasisData: validation
      });
      
      return res.json({
        status: 'verification_required',
        message: 'Please verify your email address to continue.'
      });
    }
    
    if (riskScore > 50) {
      // Low-moderate risk: require CAPTCHA
      return res.json({
        status: 'captcha_required',
        challenge: await generateCaptchaChallenge()
      });
    }
    
    // Low risk: proceed with signup
    const user = await createUser({ email, password });
    await sendWelcomeEmail(email);
    
    return res.json({
      status: 'success',
      userId: user.id,
      token: generateAuthToken(user.id)
    });
    
  } catch (error) {
    console.error('Signup validation error:', error);
    // Fail open with monitoring alert
    await alertSecurityTeam('signup_validation_failure', { email, error });
    // Continue signup but flag for review
    const user = await createUser({ email, password });
    await flagForManualReview(user.id, { error: error.message });
    return res.json({ status: 'success', userId: user.id });
  }
});

// Helper functions
async function logBlockedSignup(email, ip, validation, riskScore) {
  await db.blockedSignups.insert({
    email,
    ip,
    riskScore,
    reason: {
      isDatacenter: validation.is_datacenter,
      isVPN: validation.is_vpn,
      isProxy: validation.is_proxy,
      isTor: validation.is_tor,
      isDisposableEmail: validation.email?.is_disposable,
      domainAge: validation.email?.domain_age_days
    },
    timestamp: new Date()
  });
}

async function flagForManualReview(userId, metadata) {
  await db.reviewQueue.insert({
    userId,
    metadata,
    status: 'pending',
    createdAt: new Date()
  });
}

Implementation Guide: Node.js and Python Examples

Node.js / TypeScript Implementation

Here's a production-ready TypeScript implementation with proper error handling, retry logic, and monitoring:

// TypeScript: Production-ready signup validator

// signup-validator.ts
import fetch from 'node-fetch';

interface IPASISResponse {
  ip: string;
  risk_score: number;
  is_datacenter: boolean;
  is_vpn: boolean;
  is_proxy: boolean;
  is_tor: boolean;
  provider?: string;
  country: string;
  email?: {
    is_disposable: boolean;
    is_free: boolean;
    domain_age_days: number;
    mx_valid: boolean;
  };
  signals: {
    datacenter_confidence: number;
    vpn_confidence: number;
    proxy_confidence: number;
    abuse_history: number;
  };
}

interface SignupValidationResult {
  allowed: boolean;
  requiresChallenge: boolean;
  riskScore: number;
  reasons: string[];
  metadata: IPASISResponse;
}

export class SignupValidator {
  private apiKey: string;
  private baseURL = 'https://api.ipasis.com/v1';
  
  constructor(apiKey: string) {
    this.apiKey = apiKey;
  }
  
  async validateSignup(
    email: string,
    ip: string,
    behaviorSignals?: any
  ): Promise<SignupValidationResult> {
    try {
      // Call IPASIS API
      const url = `${this.baseURL}/lookup?ip=${encodeURIComponent(ip)}&email=${encodeURIComponent(email)}`;
      
      const response = await fetch(url, {
        headers: {
          'Authorization': `Bearer ${this.apiKey}`,
          'User-Agent': 'YourApp/1.0'
        },
        timeout: 3000 // 3s timeout
      });
      
      if (!response.ok) {
        throw new Error(`IPASIS API error: ${response.status}`);
      }
      
      const data: IPASISResponse = await response.json();
      
      // Calculate risk score
      const result = this.calculateRisk(data, behaviorSignals);
      
      return result;
      
    } catch (error) {
      console.error('Signup validation error:', error);
      // Fail open: allow signup but flag for review
      return {
        allowed: true,
        requiresChallenge: true,
        riskScore: 50,
        reasons: ['validation_error'],
        metadata: {} as IPASISResponse
      };
    }
  }
  
  private calculateRisk(
    data: IPASISResponse,
    behaviorSignals?: any
  ): SignupValidationResult {
    let riskScore = data.risk_score;
    const reasons: string[] = [];
    
    // IP-based scoring
    if (data.is_datacenter) {
      riskScore += 25;
      reasons.push('datacenter_ip');
    }
    
    if (data.is_vpn) {
      riskScore += 15;
      reasons.push('vpn_detected');
    }
    
    if (data.is_proxy) {
      riskScore += 20;
      reasons.push('proxy_detected');
    }
    
    if (data.is_tor) {
      riskScore += 40;
      reasons.push('tor_exit_node');
    }
    
    // Email-based scoring
    if (data.email?.is_disposable) {
      riskScore += 30;
      reasons.push('disposable_email');
    }
    
    if (data.email?.domain_age_days < 30) {
      riskScore += 20;
      reasons.push('new_email_domain');
    }
    
    if (!data.email?.mx_valid) {
      riskScore += 15;
      reasons.push('invalid_mx_records');
    }
    
    // Combined signals
    if (data.email?.is_free && (data.is_vpn || data.is_proxy)) {
      riskScore += 10;
      reasons.push('free_email_with_proxy');
    }
    
    // Behavioral scoring
    if (behaviorSignals) {
      if (behaviorSignals.formDuration < 2000) {
        riskScore += 20;
        reasons.push('suspicious_form_speed');
      }
      
      if (!behaviorSignals.hasMouseActivity) {
        riskScore += 15;
        reasons.push('no_mouse_movement');
      }
    }
    
    // Decision thresholds
    const allowed = riskScore <= 100;
    const requiresChallenge = riskScore > 50 && riskScore <= 100;
    
    return {
      allowed,
      requiresChallenge,
      riskScore,
      reasons,
      metadata: data
    };
  }
}

// Usage example
const validator = new SignupValidator(process.env.IPASIS_API_KEY!);

app.post('/api/signup', async (req, res) => {
  const { email, password } = req.body;
  const clientIP = getClientIP(req);
  
  const validation = await validator.validateSignup(email, clientIP);
  
  if (!validation.allowed) {
    return res.status(403).json({
      error: 'Signup blocked',
      reasons: validation.reasons
    });
  }
  
  if (validation.requiresChallenge) {
    // Send to challenge flow (CAPTCHA, email verification, etc.)
    return res.json({ 
      status: 'challenge_required',
      challengeType: 'email_verification'
    });
  }
  
  // Low risk: proceed
  const user = await createUser({ email, password });
  return res.json({ status: 'success', userId: user.id });
});

Python / Django Implementation

Python implementation with Django integration and async support:

// Python: Django signup validator

# signup_validator.py
import httpx
from typing import Optional, Dict, Any
from dataclasses import dataclass
import os

@dataclass
class ValidationResult:
    allowed: bool
    requires_challenge: bool
    risk_score: int
    reasons: list[str]
    metadata: Dict[str, Any]

class IPASISSignupValidator:
    BASE_URL = "https://api.ipasis.com/v1"
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.client = httpx.Client(timeout=3.0)
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.client.close()
    
    def validate_signup(
        self,
        email: str,
        ip_address: str,
        behavior_signals: Optional[Dict[str, Any]] = None
    ) -> ValidationResult:
        """
        Validate a signup attempt using IP and email intelligence.
        
        Args:
            email: User's email address
            ip_address: Client IP address
            behavior_signals: Optional dict with client-side behavioral data
            
        Returns:
            ValidationResult with decision and risk details
        """
        try:
            # Call IPASIS API
            response = self.client.get(
                f"{self.BASE_URL}/lookup",
                params={"ip": ip_address, "email": email},
                headers={"Authorization": f"Bearer {self.api_key}"}
            )
            response.raise_for_status()
            data = response.json()
            
            # Calculate risk
            return self._calculate_risk(data, behavior_signals)
            
        except httpx.HTTPError as e:
            print(f"IPASIS API error: {e}")
            # Fail open: allow but require challenge
            return ValidationResult(
                allowed=True,
                requires_challenge=True,
                risk_score=50,
                reasons=["validation_error"],
                metadata={}
            )
    
    def _calculate_risk(
        self,
        data: Dict[str, Any],
        behavior_signals: Optional[Dict[str, Any]]
    ) -> ValidationResult:
        risk_score = data.get("risk_score", 0)
        reasons = []
        
        # IP-based risk factors
        if data.get("is_datacenter"):
            risk_score += 25
            reasons.append("datacenter_ip")
        
        if data.get("is_vpn"):
            risk_score += 15
            reasons.append("vpn_detected")
        
        if data.get("is_proxy"):
            risk_score += 20
            reasons.append("proxy_detected")
        
        if data.get("is_tor"):
            risk_score += 40
            reasons.append("tor_exit_node")
        
        # Email-based risk factors
        email_data = data.get("email", {})
        
        if email_data.get("is_disposable"):
            risk_score += 30
            reasons.append("disposable_email")
        
        if email_data.get("domain_age_days", 999) < 30:
            risk_score += 20
            reasons.append("new_domain")
        
        if not email_data.get("mx_valid"):
            risk_score += 15
            reasons.append("invalid_mx")
        
        # Combined signals
        if email_data.get("is_free") and (data.get("is_vpn") or data.get("is_proxy")):
            risk_score += 10
            reasons.append("free_email_with_proxy")
        
        # Behavioral signals
        if behavior_signals:
            if behavior_signals.get("formDuration", 9999) < 2000:
                risk_score += 20
                reasons.append("suspicious_speed")
            
            if not behavior_signals.get("hasMouseActivity"):
                risk_score += 15
                reasons.append("no_mouse_activity")
        
        # Decision logic
        allowed = risk_score <= 100
        requires_challenge = 50 < risk_score <= 100
        
        return ValidationResult(
            allowed=allowed,
            requires_challenge=requires_challenge,
            risk_score=risk_score,
            reasons=reasons,
            metadata=data
        )

# Django view integration
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json

@csrf_exempt
def signup_view(request):
    if request.method != "POST":
        return JsonResponse({"error": "Method not allowed"}, status=405)
    
    try:
        data = json.loads(request.body)
        email = data.get("email")
        password = data.get("password")
        behavior_signals = data.get("behaviorSignals")
        
        # Get client IP
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            client_ip = x_forwarded_for.split(',')[0]
        else:
            client_ip = request.META.get('REMOTE_ADDR')
        
        # Validate signup
        with IPASISSignupValidator(os.environ["IPASIS_API_KEY"]) as validator:
            result = validator.validate_signup(email, client_ip, behavior_signals)
        
        if not result.allowed:
            # Log blocked attempt
            BlockedSignup.objects.create(
                email=email,
                ip_address=client_ip,
                risk_score=result.risk_score,
                reasons=result.reasons
            )
            
            return JsonResponse({
                "error": "Signup blocked",
                "message": "Unable to create account from this location."
            }, status=403)
        
        if result.requires_challenge:
            # Send email verification
            user = User.objects.create_user(
                email=email,
                password=password,
                is_active=False
            )
            send_verification_email(user)
            
            # Flag for review
            SignupReview.objects.create(
                user=user,
                risk_score=result.risk_score,
                reasons=result.reasons,
                metadata=result.metadata
            )
            
            return JsonResponse({
                "status": "verification_required",
                "message": "Please verify your email to continue."
            })
        
        # Low risk: create active account
        user = User.objects.create_user(
            email=email,
            password=password,
            is_active=True
        )
        
        return JsonResponse({
            "status": "success",
            "userId": user.id
        })
        
    except Exception as e:
        print(f"Signup error: {e}")
        return JsonResponse({
            "error": "Internal server error"
        }, status=500)

Best Practices Checklist

Use this checklist to ensure your fake signup prevention is comprehensive and maintainable:

✅ Detection Layer

  • [ ] Implement server-side IP intelligence for every signup
  • [ ] Validate email addresses for disposable providers and domain reputation
  • [ ] Collect client-side behavioral signals (timing, mouse, keystrokes)
  • [ ] Combine multiple signals into a unified risk score
  • [ ] Use CAPTCHAs only as a challenge for moderate-risk users, not for everyone

✅ Decision Logic

  • [ ] Define clear risk thresholds: block (100+), challenge (70-100), allow (<70)
  • [ ] Implement different challenge types based on risk level (email verification, SMS, CAPTCHA)
  • [ ] Fail open gracefully when validation services are unavailable
  • [ ] Log all blocked and challenged signups for analysis
  • [ ] Create a manual review queue for borderline cases

✅ Performance & Reliability

  • [ ] Ensure validation API calls complete in <100ms (use services with sub-50ms p95 latency)
  • [ ] Implement request timeouts (3s max) to avoid hanging signups
  • [ ] Cache IP reputation results for repeated signups from the same IP (5-10 minute TTL)
  • [ ] Monitor validation API error rates and latency
  • [ ] Have a fallback decision path when APIs are down

✅ Monitoring & Iteration

  • [ ] Track daily signup volume, block rate, and challenge rate
  • [ ] Measure false positive rate (legitimate users blocked)
  • [ ] Review blocked signups weekly to identify patterns and adjust thresholds
  • [ ] A/B test risk thresholds to optimize for your user base
  • [ ] Set up alerts for abnormal signup velocity or block rate spikes

✅ User Experience

  • [ ] Provide clear, non-accusatory error messages ("Unable to complete signup" vs. "You are a bot")
  • [ ] Offer a manual review option for users who believe they were incorrectly blocked
  • [ ] Don't show CAPTCHAs to low-risk users
  • [ ] Test signup flow from VPNs to ensure legitimate VPN users can still sign up (with challenge)
  • [ ] Support account appeals/unblocks via customer support

✅ Privacy & Compliance

  • [ ] Disclose in your privacy policy that you perform fraud detection on signups
  • [ ] Don't store raw IP addresses longer than necessary (30-90 days)
  • [ ] Anonymize or hash IP addresses in long-term analytics
  • [ ] Ensure your validation provider is GDPR/CCPA compliant
  • [ ] Implement data retention policies for blocked signup logs

Measuring Success

Track these metrics to understand the effectiveness of your fake signup prevention:

  • Signup block rate: Percentage of signups blocked automatically. Target: 5-15% (depends on your industry and attack profile)
  • Challenge rate: Percentage requiring additional verification. Target: 10-20%
  • False positive rate: Legitimate users blocked. Target: <1% (measure via support tickets and manual reviews)
  • Trial-to-paid conversion rate: Should increase as fake trial accounts decrease
  • Email deliverability rate: Should improve as disposable emails are filtered out
  • Support ticket volume: "Account locked" or "Can't sign up" tickets should remain stable or decrease

A successful implementation typically sees a 60-80% reduction in fake signups within the first month, with a <1% false positive rate on legitimate users.

Advanced: Pattern Detection Across Accounts

For sophisticated trial abusers who create multiple accounts, cross-account pattern detection adds another layer:

// Example: Detect email pattern abuse

// Detect sequential or pattern-based emails
function detectEmailPattern(email: string): boolean {
  const recentSignups = await db.users
    .find({
      createdAt: { $gt: Date.now() - 7 * 24 * 60 * 60 * 1000 } // Last 7 days
    })
    .select('email');
  
  const baseEmail = email.split('@')[0];
  const domain = email.split('@')[1];
  
  // Check for plus-addressing abuse: user+1@, user+2@, user+3@
  const plusAddressCount = recentSignups.filter(u => {
    return u.email.includes('+') && 
           u.email.startsWith(baseEmail.split('+')[0]) &&
           u.email.endsWith('@' + domain);
  }).length;
  
  if (plusAddressCount > 3) return true;
  
  // Check for sequential numbering: user1@, user2@, user3@
  const baseWithoutNumber = baseEmail.replace(/\d+$/, '');
  const sequentialCount = recentSignups.filter(u => {
    const otherBase = u.email.split('@')[0].replace(/\d+$/, '');
    return otherBase === baseWithoutNumber && u.email.endsWith('@' + domain);
  }).length;
  
  if (sequentialCount > 3) return true;
  
  return false;
}

// Usage in signup validation
if (await detectEmailPattern(email)) {
  riskScore += 30;
  reasons.push('email_pattern_abuse');
}

Next Steps: Start Preventing Fake Signups Today

Fake signups are costing you money, degrading analytics, and consuming engineering resources. Implementing comprehensive signup validation doesn't require months of development — with the right API and decision logic, you can deploy a robust solution in a few hours.

Action plan:

  1. Audit current state: What percentage of your signups are likely fake? Look for disposable emails, datacenter IPs, and accounts that never convert.
  2. Implement IP + email validation: Start with IPASIS for server-side intelligence. Free tier available — no credit card required.
  3. Add behavioral tracking: Collect client-side signals (form timing, mouse movement) to identify bot behavior.
  4. Define risk thresholds: Block high-risk (>100), challenge moderate-risk (70-100), allow low-risk (<70).
  5. Monitor and iterate: Track block rates, false positives, and user feedback. Adjust thresholds monthly.

Related Resources