Skip to main content
All six detectors in ids.py share a common set of primitives: a sliding-window counter, a set-based unique-port tracker, an anti-spam throttle, and a CIDR whitelist. This page documents each primitive with the real implementation from the source.

Sliding window counters

Rate-based detectors (SYN flood, DDoS, UDP flood) measure how many packets arrive within a fixed time window. The implementation uses a plain Python list of Unix timestamps appended on every packet, then filtered to keep only entries within the window:
ids.py
# SYN Flood — 500 ms window, keyed by source IP
paquetes_por_ip[ip_src].append(t)
paquetes_por_ip[ip_src] = [ts for ts in paquetes_por_ip[ip_src] if t - ts <= 0.5]

if len(paquetes_por_ip[ip_src]) > THRESHOLD_SYN_FLOOD:
    guardar_ataque(ip_src, "SYN Flood", ...)
ids.py
# DDoS / UDP Flood — 1 second window, keyed by destination IP
paquetes_por_ip[ip_dst].append(t)
paquetes_por_ip[ip_dst] = [ts for ts in paquetes_por_ip[ip_dst] if t - ts <= 1]

if len(paquetes_por_ip[ip_dst]) > THRESHOLD_DDOS:
    guardar_ataque(ip_src, "DDoS Distribuido", ...)
The list is rebuilt from scratch on every packet. This is an O(n) operation where n is the number of timestamps in the current window — acceptable because windows are narrow (0.5 s or 1 s) and the list stays small under normal traffic.
DetectorWindowKeyed by
SYN flood500 msSource IP
DDoS distribuido1 sDestination IP
UDP flood1 sDestination IP

Port scan tracking

Port scan detection does not use a time window. Instead, detectar_escaneo_puertos maintains a set of destination ports per source IP. Because sets automatically deduplicate entries, len(set) always reflects the number of unique ports probed, regardless of how many times each port was hit:
ids.py
# defaultdict(set): each source IP maps to a set of destination ports
puertos_por_ip = defaultdict(set)

# In detectar_escaneo_puertos:
puertos_por_ip[ip_src].add(puerto)          # O(1) insertion; duplicates ignored

if len(puertos_por_ip[ip_src]) > PORT_SCAN_THRESHOLD:
    guardar_ataque(ip_src, "Escaneo de Puertos", 'TCP', puerto, ip_dst)
This counter is cumulative for the lifetime of the monitoring session. Restarting iniciar_monitoreo() resets the global puertos_por_ip dictionary.

Throttle anti-spam

A sustained attack generates thousands of matching packets per second. Without throttling, guardar_ataque() would fire thousands of times for the same IP, flooding SQLite, the CSV dataset, and the Telegram channel. The throttle uses a dictionary that records the last alert timestamp for each IP:
ids.py
TIEMPO_ENTRE_ALERTAS = 2  # seconds
ultimo_ataque_por_ip  = {}  # ip -> Unix timestamp of last alert

# At the top of guardar_ataque():
ahora = time.time()
if ahora - ultimo_ataque_por_ip.get(ip_src, 0) < TIEMPO_ENTRE_ALERTAS:
    return  # Suppress duplicate alert
ultimo_ataque_por_ip[ip_src] = ahora
The result is that each source IP can produce at most one alert every 2 seconds, regardless of how many packets arrive. The advertencias_cont[ip_src] counter still increments on every call that passes the throttle check, providing a cumulative warning count per IP.

Whitelist checking

The detectar_exploit and detectar_sql_injection detectors skip packets originating from trusted IPs before applying any pattern matching. Trust is evaluated in two steps:
  1. Exact-match setIPS_CONFIABLES contains individual IP addresses:
ids.py
IPS_CONFIABLES = {
    "192.168.0.15",
    "192.168.0.17",
    "8.243.166.74",
    "172.67.9.68",
    "104.22.1.235",  # Cloudflare
    "2.22.20.72",    # Akamai
}
  1. CIDR range matchip_en_rangos(ip) tests against a list of ipaddress.ip_network objects:
ids.py
def ip_en_rangos(ip: str) -> bool:
    try:
        ip_obj = ipaddress.ip_address(ip)
        return any(ip_obj in net for net in RANGOS_CONFIABLES)
    except ValueError:
        return False
any() short-circuits on the first match, so the check is efficient even with a long list of ranges. Trusted CIDR ranges:
RangeProvider
10.0.0.0/8RFC 1918 private class A
172.16.0.0/12RFC 1918 private class B
20.110.205.0/24Microsoft Azure
40.0.0.0/8Microsoft Azure
52.0.0.0/8Amazon Web Services
54.0.0.0/8Amazon Web Services
104.16.0.0/12Cloudflare
140.82.0.0/16GitHub
143.204.0.0/16Amazon CloudFront
34.192.0.0/12Google Cloud
35.192.0.0/12Google Cloud
172.217.0.0/16Google
2.22.20.0/24Akamai
52.178.17.0/24Microsoft Azure
The whitelist only protects detectar_exploit and detectar_sql_injection. Rate-based detectors (SYN flood, DDoS, UDP flood) do not skip whitelisted IPs, because a compromised CDN edge node could still participate in a flood.

SQL injection regex

The SQL injection detector compiles a single case-insensitive multi-branch regular expression. Each branch targets a distinct injection technique:
ids.py
sql_pattern = re.compile(
    r"(?i)(\b(select|union|insert|update|delete|drop|alter|create|"
    r"exec|execute|cast|declare|grant|revoke)\b"
    r".?(--|#|;|/\|\*/|@@|char\(|nchar\(|varchar\(|nvarchar\()|"  # keyword + comment/terminator
    r"('(\s)or(\s)\d+=\d+)|"                                     # ' OR 1=1
    r"(\bunion\b.*\bselect\b)|"                                   # UNION SELECT
    r"(\bexec\b(\s|\+)+(s|x)p\w+)|"                             # EXEC sp_* / xp_*
    r"(;?\s*--)|"                                                 # standalone comment
    r"(\bwaitfor\b\s+delay\b)|"                                  # WAITFOR DELAY
    r"(sleep\(\d+\))"                                            # sleep(N)
    r")"
)
Before the regex is applied, two pre-filters discard payloads that are unlikely to be malicious:
  • Length filter: payloads longer than 1,000 bytes are skipped (binary protocols, file uploads).
  • Exclusion list: payloads containing common HTTP parameter names (order=desc, limit=, search=, token=, session=, csrf, user-agent, etc.) are skipped to avoid false positives from normal web traffic.

Exploit port list

The PUERTOS_EXPLOIT set is defined inline inside detectar_exploit:
ids.py
PUERTOS_EXPLOIT = {135, 139, 445, 3389, 5900, 21, 22, 23, 69}
For each port, the detector only fires on SYN or SYN-ACK flags. Packets with ACK, PSH, FIN, or other flags represent established session traffic and are ignored — this prevents false positives from legitimate ongoing connections to these services.
PortServiceNotable CVEs / campaigns
21FTPCVE-2010-4221, anonymous auth abuse
22SSHBrute force, credential stuffing, Mirai
23TelnetMirai botnet, cleartext credential capture
69TFTPUnauthenticated read/write, firmware injection
135MS-RPCCVE-2003-0352 (DCOM RPC), Blaster worm
139NetBIOS-SSNSMB relay, legacy Windows enumeration
445SMBCVE-2017-0144 (EternalBlue), WannaCry
3389RDPCVE-2019-0708 (BlueKeep), DejaBlue
5900VNCCVE-2006-2369, authentication bypass

Build docs developers (and LLMs) love