DEV Community

Alexis
Alexis

Posted on

5 Email Attack Vectors Every SaaS Should Block

TL;DR: A simple email input field is an attack surface. Users exploit aliases, relay services, and disposable domains to abuse free tiers, evade bans, and game referral systems. Here's how to defend against each vector.


Your registration form has one text field: email. It looks innocent. It's not.

That field is an attack surface for multi-accounting, ban evasion, referral fraud, and trial abuse. A determined user can create hundreds of accounts using a single Gmail address. Privacy-focused services give users unlimited anonymous aliases. Disposable email providers exist specifically for throwaway accounts.

This article covers five attack vectors and how to defend against each. See open-source libraries at the end for ready-to-use implementations.

1. Alias Abuse: One Inbox, Infinite Accounts

Most email providers support aliases that all deliver to the same inbox. Users exploit this to create multiple accounts that appear unique but aren't.

Gmail: Dots and Plus-Addressing

Gmail ignores dots in the local part and supports plus-addressing:

john.doe@gmail.com
johndoe@gmail.com
j.o.h.n.d.o.e@gmail.com
johndoe+signup1@gmail.com
johndoe+signup2@gmail.com
johndoe+freetrialforever@gmail.com
Enter fullscreen mode Exit fullscreen mode

All six addresses deliver to the same inbox. A naive uniqueness check treats them as different users.

ProtonMail: Dots, Hyphens, and Underscores

ProtonMail goes further. As a security measure against impersonation, they ignore dots, hyphens, and underscores:

journalist.name@protonmail.com
journalistname@protonmail.com
journalist-name@protonmail.com
journalist_name@protonmail.com
Enter fullscreen mode Exit fullscreen mode

All equivalent. ProtonMail implemented this to prevent attackers from registering lookalike addresses.

Yahoo: Hyphen-Based Aliases

Yahoo uses hyphens instead of plus signs:

john@yahoo.com
john-shopping@yahoo.com
john-newsletters@yahoo.com
Enter fullscreen mode Exit fullscreen mode

Fastmail: Subdomain Addressing

Fastmail supports both plus-addressing and subdomain aliases:

user+tag@fastmail.com
anything@user.fastmail.com
randomalias@user.fastmail.com
Enter fullscreen mode Exit fullscreen mode

The subdomain format is particularly dangerous because the local part can be anything.

Defense: Provider-Specific Normalization

Normalize emails before storing or checking uniqueness:

public static class EmailNormalizer
{
    private static readonly HashSet<string> GmailDomains =
        new(StringComparer.OrdinalIgnoreCase) { "gmail.com", "googlemail.com" };

    private static readonly HashSet<string> ProtonMailDomains =
        new(StringComparer.OrdinalIgnoreCase) { "protonmail.com", "proton.me", "pm.me" };

    public static string Normalize(string email)
    {
        var atIndex = email.LastIndexOf('@');
        var localPart = email[..atIndex];
        var domain = email[(atIndex + 1)..].ToLowerInvariant();

        if (GmailDomains.Contains(domain))
        {
            localPart = StripPlusSuffix(localPart);
            localPart = localPart.Replace(".", "");
        }
        else if (ProtonMailDomains.Contains(domain))
        {
            localPart = StripPlusSuffix(localPart);
            localPart = localPart.Replace(".", "")
                                 .Replace("-", "")
                                 .Replace("_", "");
        }
        // Yahoo: strip hyphen suffix
        // Fastmail: handle subdomain addressing
        // Outlook/iCloud: strip plus suffix only

        return $"{localPart.ToLowerInvariant()}@{domain}";
    }

    private static string StripPlusSuffix(string localPart)
    {
        var plusIndex = localPart.IndexOf('+');
        return plusIndex > 0 ? localPart[..plusIndex] : localPart;
    }
}
Enter fullscreen mode Exit fullscreen mode

Store both the original email (for sending) and the normalized form (for uniqueness checks).

2. Relay Services: Unlimited Anonymous Aliases

Email relay services let users generate unlimited unique addresses that forward to their real inbox. Unlike provider aliases, each relay address is genuinely unique at the DNS level.

Major Relay Services

Service Domain(s) Notes
Apple Hide My Email privaterelay.appleid.com Built into iCloud+
Firefox Relay mozmail.com Free tier available
DuckDuckGo duck.com Email Protection feature
SimpleLogin simplelogin.com, slmails.com, etc. Proton-owned
Proton Pass passmail.net Password manager integration
addy.io (AnonAddy) addy.io, anonaddy.com Subdomain aliases
Fastmail Masked Email fastmail.com Premium feature
GitHub users.noreply.github.com Commit email privacy

Subdomain-Based Services

Some services use username-based subdomains:

randomalias@johndoe.anonaddy.com
anotheralias@johndoe.anonaddy.com
Enter fullscreen mode Exit fullscreen mode

This requires wildcard matching, not just exact domain checks.

Defense: Relay Service Blocklist

public static class RelayServiceBlocklist
{
    private static readonly HashSet<string> BlockedDomains =
        new(StringComparer.OrdinalIgnoreCase)
    {
        "privaterelay.appleid.com",
        "mozmail.com",
        "duck.com",
        "simplelogin.com", "slmails.com",
        "passmail.net",
        "addy.io", "anonaddy.com",
        "users.noreply.github.com"
    };

    private static readonly string[] WildcardParents =
    {
        "anonaddy.com",  // *.anonaddy.com
        "aleeas.com"     // *.aleeas.com (SimpleLogin)
    };

    public static bool IsRelayService(string domain)
    {
        if (BlockedDomains.Contains(domain))
            return true;

        foreach (var parent in WildcardParents)
        {
            if (domain.EndsWith($".{parent}", StringComparison.OrdinalIgnoreCase))
                return true;
        }

        return false;
    }
}
Enter fullscreen mode Exit fullscreen mode

Tradeoff: Blocking relay services frustrates privacy-conscious users. Consider your product's threat model. A free tier with expensive compute? Block them. A paid product with identity verification? Maybe allow them.

3. Disposable Emails: Throwaway Accounts

Disposable email providers create temporary inboxes that expire after minutes or hours. Users create an account, verify the email, and abandon the address.

Scale of the Problem

The disposable-email-domains community maintains a blocklist of 38,000+ domains. New services appear constantly.

Common Disposable Providers

  • Mailinator, Guerrilla Mail, 10 Minute Mail
  • Temp Mail, ThrowAwayMail, Fake Inbox
  • Thousands of lesser-known variants

4. Fake Domains: Non-Existent Email Addresses

Users provide email addresses with domains that can't receive mail:

  • Typos: user@gmial.com, user@hotnail.com
  • Invented domains: user@notreal.fake
  • Expired domains: Previously valid, now unregistered

Defense: MX Record Validation

Verify the domain has mail exchanger (MX) records in DNS:

public class MxRecordValidator
{
    private readonly ILookupClient _dnsClient;

    public async Task<bool> HasValidMxRecordsAsync(string domain, CancellationToken ct)
    {
        try
        {
            var result = await _dnsClient.QueryAsync(domain, QueryType.MX, ct);

            // Check for MX records
            if (result.Answers.MxRecords().Any())
                return true;

            // Fallback: Check for A/AAAA records (some domains accept mail without MX)
            var aResult = await _dnsClient.QueryAsync(domain, QueryType.A, ct);
            return aResult.Answers.ARecords().Any();
        }
        catch (DnsResponseException)
        {
            return false;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Performance note: DNS lookups add latency. Cache results and consider making MX validation async (verify after initial signup, not blocking).

5. Format Bypass: Invalid Email Strings

Weak regex patterns allow malformed addresses or miss edge cases.

Common Validation Mistakes

// Too permissive - allows invalid characters
var weak = @".+@.+";

// Too strict - rejects valid addresses
var strict = @"^[a-z]+@[a-z]+\.[a-z]{2,3}$";
// Rejects: user+tag@domain.com, UPPERCASE@domain.com, user@domain.co.uk
Enter fullscreen mode Exit fullscreen mode

Defense: HTML5 Living Standard Pattern

The HTML5 specification defines a practical email validation pattern that handles real-world addresses:

public partial class EmailValidator
{
    // HTML5 Living Standard email pattern
    // Source: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
    private const string Html5Pattern =
        @"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$";

    [GeneratedRegex(Html5Pattern, RegexOptions.Compiled)]
    private static partial Regex EmailRegex();

    public bool IsValidFormat(string email)
    {
        if (string.IsNullOrWhiteSpace(email))
            return false;

        return EmailRegex().IsMatch(email);
    }
}
Enter fullscreen mode Exit fullscreen mode

This pattern is a "willful violation" of RFC 5322. It rejects technically valid but practically useless addresses (like "quoted string"@domain.com) while accepting everything real users actually use.

Validation Pipeline

Combine all defenses in order of computational cost:

public async Task<ValidationResult> ValidateAsync(string email)
{
    // 1. Format (fast, no I/O)
    if (!IsValidFormat(email))
        return Failure(Error.InvalidFormat);

    var domain = ExtractDomain(email);

    // 2. Relay service (fast, in-memory lookup)
    if (IsRelayService(domain))
        return Failure(Error.RelayService);

    // 3. Disposable domain (fast, in-memory lookup)
    if (IsDisposable(domain))
        return Failure(Error.Disposable);

    // 4. MX records (slow, DNS lookup)
    if (!await HasValidMxRecordsAsync(domain))
        return Failure(Error.InvalidDomain);

    return Success();
}
Enter fullscreen mode Exit fullscreen mode

Order matters. Check cheap operations first, expensive operations last.

Open Source Libraries

Don't build this from scratch. These libraries handle normalization, relay detection, disposable blocklists, and MX validation:

Language Library Features
.NET Alos.Email.Validation Provider normalization, relay blocklist, 38K+ disposable domains, MX validation, auto-update
Node.js mailchecker 55K+ disposable domains, multi-language (JS, PHP, Python, Ruby, Go, Rust)
Node.js email-verifier MX validation, disposable detection, caching
Python email-validator RFC compliance, MX validation, internationalization
Python disposable-mail-checker Disposable domain blocklist based on community lists
Ruby valid_email2 MX validation, disposable blocklist, Rails integration
Go email-verifier Disposable detection, free provider detection, auto-update, SMTP check
PHP email-checker 1K+ disposable domains, Symfony/Laravel integration
PHP disposable-email-filter-php Auto-updated weekly, framework-agnostic, Laravel support

None of these defenses are perfect. Determined attackers can register custom domains, use corporate email, or buy access to real accounts. The goal is to raise the cost of abuse high enough that it's not worth the effort for most attackers.

Top comments (0)