# Fraud Prevention

## 5 protection layers

| # | Layer | Description | Response |
| --- | --- | --- | --- |
| 1 | Bot detection (UA) | User-Agent checked against known bot patterns (crawlers, spiders, headless browsers) | Empty ad response, no impression logged |
| 2 | IP blocklist | Known spam and bot IPs, as well as manually blocked IPs | HTTP 403, no ad served |
| 3 | Rate limiting | Maximum requests per IP within two time windows (Redis-based) | HTTP 429, empty response |
| 4 | Click deduplication | Repeat clicks from the same session on the same ad (requires consent cookie) | Redirect, counted as regular click only |
| 5 | Session cap | Zone-specific impression limit per visitor session (requires consent cookie) | Empty ad response (fallback HTML shown if configured) |

## Bot detection

The AdServer analyses the `User-Agent` header of every request.
Detected bots receive an empty response – they are counted neither as an impression nor as a click.

Detected bot categories:

> Search engine crawlers

> Headless browsers

> Web scrapers

> Monitoring tools

> SEO tools

> Curl / Wget

> Device type `bot` in the `HourlyTargetingStats` model shows how many bot requests were received.

## IP blocklist

> Automatic
>
> - IPs with > 10 clicks in 60 seconds are temporarily blocked
> - Known spam networks (public blocklists)

> Manual (via Admin)
>
> - Permanently block individual IPs
> - Block IP ranges (CIDR notation)

## Rate limiting

Two independent time windows protect against excessive requests from a single IP:

| Window | Limit (default) | Redis key |
| --- | --- | --- |
| 1 minute | 120 requests (ad delivery) · 30 requests (clicks) | rl:{serve|click}:m:{ip} |
| 1 hour | 1,000 requests (ad delivery) · 300 requests (clicks) | rl:{serve|click}:h:{ip} |

> Limits can be adjusted in Django settings (`settings.py`): `ADSERVER_RATE_LIMIT_MINUTE` and `ADSERVER_RATE_LIMIT_HOUR`.

## GDPR compliance

> What we do NOT store
>
> - Full IP addresses
> - Personal data of visitors
> - Browser fingerprints
> - Cross-site tracking

> What we store
>
> - Aggregated impressions/clicks (hourly)
> - IP address in short-lived rate-limit counters (Redis, expires within 1 hour); never stored with impressions or clicks
> - Session UUID in `seco_` cookie (opt-in only)
> - GeoIP country (aggregated, no link to person)

> Basic ad serving (impressions + clicks) works without any cookies. The `seco_` cookie is only set after explicit visitor consent and enables unique click tracking and frequency caps.

## GeoIP setup

For country targeting stats, GeoIP is used. The system uses the MaxMind GeoLite2 database.

### Step 1: Create a free account at **maxmind.com** and generate a license key.

### Step 2: 
Download `GeoLite2-Country.mmdb` and place it in the directory specified by `GEOIP_PATH` in `settings.py`.

### Step 3: Restart the server. Country stats will appear from the next impression onwards.

> Without the GeoIP database, country stats are stored as `XX` (unknown). All other functions are unaffected.
