---
title: "Catch fake signups with three layered checks"
url: "https://lookvalid.com/blog/security/stop-fake-signups-with-layered-checks.md"
canonical_url: "https://lookvalid.com/blog/security/stop-fake-signups-with-layered-checks"
last_updated: "2026-10-22"
type: "conceptual"
summary: "Combine a throwaway email domain check, a domain age check and an IP hosting check to catch fake signups, with a decision table and a note on false positives."
related: ["https://lookvalid.com/products/email-domain-check.md", "https://lookvalid.com/products/domain-age.md", "https://lookvalid.com/products/ip.md", "https://lookvalid.com/blog/security/young-domains-as-fraud-signal.md", "https://lookvalid.com/blog/tutorial/disposable-email-detection-api-tutorial.md"]
---

# Catch fake signups with three layered checks

Combine a throwaway email domain check, a domain age check and an IP hosting check to catch fake signups, with a decision table and a note on false positives.

OWASP lists mass account creation as its own automated threat, where bots use the normal sign-up form to make many accounts for later misuse [1]. No single check stops it, and every single check also catches some honest people. This post layers three cheap lookups that run before a confirmation email is sent, shows what each LookValid product returns, and gives a decision table that combines them.

## Why one signal is not enough

OWASP describes account creation as creating multiple accounts for subsequent misuse, such as spam, laundering goods or manipulating reviews and surveys [1]. The accounts come through the same form your customers use, so the defence is to score each signup on several independent facts rather than to trust one rule.

The three facts below cost one lookup each and need no interaction from the visitor: whether the email domain is a throwaway service, how long the email domain has been registered, and whether the visitor's IP address belongs to a hosting network. Each fact is weak alone. Together they separate a bot farm from a person who simply uses a privacy-minded mailbox.

## What each product returns

The `email-domain-check` product reads public DNS and open lists for the domain behind an address. It returns `disposable` (the domain is on a list of throwaway providers), `free_provider`, `role_account`, the MX records, and a `deliverability_signal` with reasons. A domain that publishes a null MX record, the single record with preference 0 and `.` as the host, tells senders it accepts no mail [2], and the product reports that as `null_mx`. It never opens a mail connection, so it cannot tell you whether a specific mailbox exists.

The `domain-age` product asks the registry over RDAP. RDAP events carry an `eventAction` and an `eventDate`, and the `registration` action marks when the object was first registered [3]. The product returns `created_at` and `age_days`, the registrar and the registry server it used. If the registry cannot be reached the answer is `unavailable`, never an age of zero.

The `ip` product returns the network owner (`asn`, `asn_org`), `is_hosting` with `hosting_provider`, and `is_tor`. Hosting is decided by matching the address against ranges that cloud providers publish themselves; AWS, for example, publishes its ranges as a JSON file meant for identifying traffic from AWS [4]. Our own `risk_score` adds 25 points for a hosting address and 70 for a Tor exit. `is_vpn` is always null, because we do not have a VPN data source, so an unknown value is never reported as false.

Check the visitor's IP address before the account is created

```bash
curl -X POST https://YOUR_API_HOST/v1/ip \
  -H "Authorization: Bearer $LOOKVALID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ip":"8.8.8.8"}'
```

Excerpt of our captured ip example for 8.8.8.8

```json
{
  "asn": 15169,
  "asn_org": "Google LLC",
  "is_hosting": true,
  "hosting_provider": "Google",
  "is_tor": false,
  "is_vpn": null,
  "risk_score": 25
}
```

## A decision table that combines the signals

The table is a starting policy we suggest. Tune the cut-offs against your own signups, where you can see which rows later produce abuse.

Suggested actions for combinations of the three signals

| Throwaway domain | Email domain age | Hosting or Tor IP | Suggested action |
| --- | --- | --- | --- |
| No | 365 days or more | No | Allow |
| No | 365 days or more | Hosting | Allow, add one risk point |
| No | Under 30 days | No | Allow low-value actions, review high-value ones |
| No | Under 30 days | Hosting | Step-up verification |
| Yes | Any | No | Step-up verification |
| Yes | Any | Hosting or Tor | Hold for manual review |
| Null MX | Any | Any | Ask for a different address, since it accepts no mail |

The same table as a scoring function

```ts
type Signals = { disposable: boolean; nullMx: boolean; ageDays: number | null; hosting: boolean; tor: boolean };

function decide(s: Signals): 'allow' | 'verify' | 'review' | 'new_address' {
  if (s.nullMx) return 'new_address';
  const network = s.hosting || s.tor;
  if (s.disposable) return network ? 'review' : 'verify';
  const young = s.ageDays !== null && s.ageDays < 30;
  return young && network ? 'verify' : 'allow';
}
```

A missing age (`ageDays` null) is treated as no signal rather than as a young domain, because a registry outage is not evidence about the visitor.

## False positives and what not to block

- Hosting addresses carry real people. Corporate gateways, remote desktops and some privacy tools exit from cloud networks, so `is_hosting` alone should never reject a signup.
- Young domains belong to new businesses. A founder who registered a domain last week is not a bot.
- The disposable list is a list. A new throwaway service is missed until it is added, and a domain can appear on it by mistake, so offer step-up verification before a hard refusal.
- A null `is_vpn` means unknown. Do not read it as a clean result.

Prefer actions that add friction to risky signups, such as a second verification step, over actions that silently drop them, and log which signal fired so you can measure the cost to honest visitors. Our [email domain check](https://lookvalid.com/products/email-domain-check.md), [domain age](https://lookvalid.com/products/domain-age.md) and [IP](https://lookvalid.com/products/ip.md) pages list every field.

## Sources

1. [OWASP Automated Threats to Web Applications: OAT-019 Account Creation](https://owasp.github.io/www-project-automated-threats-to-web-applications/assets/oats/EN/OAT-019_Account_Creation), read 2026-10-19
2. [RFC 7505: A "Null MX" No Service Resource Record for Domains That Accept No Mail](https://www.rfc-editor.org/rfc/rfc7505.txt), read 2026-10-19
3. [RFC 9083: JSON Responses for the Registration Data Access Protocol (RDAP)](https://www.rfc-editor.org/rfc/rfc9083.txt), read 2026-10-19
4. [AWS documentation: AWS IP address ranges](https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-ranges.html), read 2026-10-19

By LookValid engineering. Published 2026-10-19, updated 2026-10-19.

## Related

- [Email Domain Check API](https://lookvalid.com/products/email-domain-check.md)
- [Domain Age API](https://lookvalid.com/products/domain-age.md)
- [IP Intelligence API](https://lookvalid.com/products/ip.md)
- [Why a new domain is worth a closer look](https://lookvalid.com/blog/security/young-domains-as-fraud-signal.md)
- [Catch throwaway emails with one API call](https://lookvalid.com/blog/tutorial/disposable-email-detection-api-tutorial.md)
