MAC Protection
Per-device rate limiting for MAG/STB endpoints using MAC addresses
MAC protection adds a device-level firewall layer on top of the
existing IP-based rate limiting. It's designed for endpoints like /c
that are called by MAG set-top boxes and similar IPTV devices which
send their hardware MAC address as a query parameter.
Regular IP rate limiting can't tell the difference between one device making 5 requests and 50 bots sharing a proxy. MAC protection can — it tracks each device independently and catches bots that cycle through stolen MAC addresses.
[!info] MAC protection stops bots scanning for MACs. To stop a single stolen MAC being reused from another country or shared across many IPs, turn on Device Binding — an anti-theft layer built on top of this page's settings.
How it works
When a request hits a protected path (e.g. /c), the firewall:
- Extracts the MAC from
?mac=,?sn=, or theX-Device-MACheader - Validates the format — must be a valid
XX:XX:XX:XX:XX:XXpattern (colon or dash separated) - Checks the per-MAC rate limit — each MAC address has its own token bucket
- Tracks unique MACs per IP — if one IP sends too many different MACs in a time window, it's auto-banned (this catches bot farms cycling through MAC lists)
Whitelisted IPs bypass MAC protection entirely, same as all other firewall checks.
Which paths to protect
A MAG/Stalker portal answers two very different kinds of request, and they must both be protected:
/c— the portal bootstrap. A static loader page (and a/c→/c/redirect) that the panel serves to every MAC, with no auth, by design. Its HTTP status tells you nothing about whether a device is real — a scanner spraying random MAC addresses gets the same200/301a real box does./portal.php— the portal API. Where the device actually authenticates (?type=stb&action=handshake,get_profile,create_link, …) and where the MAC-cycling brute-force lives. On many panels this path is/portal.php; some use/server/load.phpor/stalker_portal/server/load.php— protect whichever your boxes hit.
If you protect only /c, the brute-force runs unthrottled against the
API and never gets the per-IP MAC cap. Set Paths = ["/c", "/portal.php"] (add your panel's API path if it differs).
[!info] Even when the API path isn't in Paths, the gateway still applies the per-IP MAC-cycling cap to any Stalker API request it recognizes (
type=…&action=…) as a safety net. Listing the path explicitly is still better — it also brings the country/region blocks and Device Binding enforcement onto the API.
Configuration
Sidebar → Firewall → MAC Protection card:
| Field | Default | Notes |
|---|---|---|
| Enabled | off | Master toggle |
| Paths | ["/c", "/portal.php"] | URL prefixes to protect — the bootstrap and the portal API (see Which paths to protect). Exact prefixes: /c matches /c and /c/... but not /config |
| Requests/sec | 3 | Sustained per-MAC rate limit |
| Burst | 20 | Allows short spikes (e.g. portal boot) |
| Require MAC | off | Block requests that have no ?mac= parameter at all |
| Max MACs per IP | 25 | Unique MACs from one IP before auto-ban (we suggest 15 — see below) |
| Window (seconds) | 600 | Time window for counting unique MACs |
| Ban duration (minutes) | 15 | How long to ban IPs that exceed MAC thresholds |
Recommended settings
These defaults balance protection against bots while being safe for real users in various network setups:
{
"enabled": true,
"paths": ["/c", "/portal.php"],
"requests_per_second": 100,
"burst": 200,
"max_macs_per_ip": 15,
"mac_window_seconds": 600,
"ban_duration_minutes": 15,
"require_mac": false
}Why these numbers?
100 req/s + burst 200 — Once the portal API (/portal.php) is
protected, the entire device session flows through the per-MAC limiter,
not just the bootstrap: every channel switch, EPG fetch, category open,
and logo load is its own API call, and a portal boot fires a dense burst
of them. Keep the per-MAC limit generous so a real box is never throttled
— the per-MAC bucket is not what stops a MAC-cycling spray anyway
(each sprayed MAC sends only a request or two, never hitting its own
limit). The spray is caught by Max MACs per IP below. If you protect
only /c, much lower values (e.g. 3 / 20) are enough, since the
bootstrap is a handful of requests.
15 max MACs per IP — A household has 1-3 MAG boxes, so 15 leaves plenty of room for a small office or shared router while a bot cycling through a stolen MAC list still blows past it in seconds. Hotels, businesses, and carrier-grade NAT (CGNAT) can put 20+ real devices behind one public IP — raise to 25-50 there. Avoid very low values (e.g. 5): now that the portal API is protected too, they false-positive ban legitimate users who share one public IP.
600s (10 min) window — Long enough to catch slow-drip bots that pace their MAC cycling to avoid detection. Real users behind CGNAT won't hit 25 unique MACs in 10 minutes — even a 50-room hotel doesn't have 25 MAGs booting simultaneously under normal conditions.
15 min ban — Short enough that a false positive self-heals quickly. A banned bot still has to wait 15 minutes, then gets banned again on the next burst — effective deterrence with minimal risk to real users.
require_mac off — Some legitimate third-party players (TiviMate,
IPTV Smarters, VLC) don't send a ?mac= parameter. Keep this off
unless you're certain all your users are on MAG hardware.
When to use "Require MAC"
Turn this on only if:
- All your users are on MAG/Infomir hardware (which always sends
?mac=) - You're seeing bots that hit
/cwithout any MAC parameter - You don't support third-party players like TiviMate or Smarters
When enabled, any request to a protected path without a ?mac=
query parameter gets a 403 Forbidden immediately.
Tuning
False positives (real users getting blocked)
- Raise
burst— if users complain about blocks during app startup, their device may send more boot requests than expected - Raise
max_macs_per_ip— if you serve hotels or large NAT networks, increase to 50+ - Lower
ban_duration_minutes— reduce the impact per false positive (e.g. 5 minutes instead of 15) - Add the IP to the whitelist — for known business customers with many devices behind one IP
False negatives (bots getting through)
- Lower
requests_per_second— drop to 2 or even 1 if you see bots making sustained low-rate requests per MAC - Lower
max_macs_per_ip— if bots stay under the limit, tighten carefully (watch CGNAT/shared-IP users — don't go as low as 5) - Turn on
require_mac— forces all requests to include a MAC, blocking simple scrapers - Shorten
mac_window_seconds— a 60s window catches faster bursts but forgives legitimate slow accumulation
Interaction with other firewall rules
The order of checks per request:
- Image redirect → bypass
- Whitelist hit → bypass everything below
- Banned IP →
403 - VPN/proxy check →
403if enabled and flagged - IP rate limit →
429if over limit - MAC protection →
403if MAC invalid, rate exceeded, or too many MACs per IP - Device binding →
403if the MAC's country or IP cap is violated (see Device Binding) - Country block →
403if blocked - Forward to backend
MAC protection runs after the general IP rate limit. This means a bot hammering from one IP gets caught by IP rate limiting first (cheaper check). MAC protection then catches the smarter bots that distribute across IPs but reuse or cycle MACs.
Logging
Every MAC request is logged for audit. Look for these patterns in Sidebar → Logs:
| Log prefix | Meaning |
|---|---|
MAC_REQUEST | Valid MAC request processed (ip, mac, path, country) |
MAC_BLOCK | Blocked — missing or invalid MAC format |
MAC_RATELIMIT | Blocked — per-MAC rate limit exceeded |
MAC_AUTOBAN | IP auto-banned — too many unique MACs in window |
Example log lines:
MAC_REQUEST ip=185.22.174.8 mac=00:1A:79:AA:BB:CC path=/c country=DE
MAC_RATELIMIT ip=45.134.26.9 mac=00:1A:79:11:22:33 path=/c country=US reason=MAC rate limit exceeded (mac=00:1A:79:11:22:33, limit=3/s)
MAC_AUTOBAN ip=91.108.4.55 mac=AA:BB:CC:DD:EE:FF path=/c country=RU reason=too many unique MACs from IP (>25 in window) ban_minutes=15Monitoring
MAC protection stats are available at:
- Dashboard — the firewall stats overview shows
MAC blockedcount alongside VPN blocked - API —
GET /internal/firewall/mac-statsreturns active MAC buckets, tracked IPs, and total blocked count
Persistence
Like auto-ban, MAC rate-limit and bot-detection state is in-memory. A proxy restart clears all MAC buckets and IP-MAC trackers, and bans triggered by MAC protection clear on restart too.
Device Binding state is the
exception — country pins and IP history are saved to
shared-config/mac_bindings.json and reloaded on startup, so they
survive restarts and upgrades.