Estructura de patrones regex efectivos
Un regex anti-SQLi eficaz equilibra sensibilidad y especificidad. El flag (?i) (case-insensitive) es crítico porque atacantes explotan variaciones como UnIoN o SeLeCt para evadir filtros básicos. El cuantificador .{1,100} captura ofuscación con espacios, tabs y comentarios: UNION/**/SELECT vs UNION SELECT.
Error común: patrones demasiado específicos tipo union\s+select. Esto falla contra union/**/select o union+select (URL-encoded). Mejor: (union\s+(all\s+)?select) que cubre UNION ALL SELECT también. Para ataques blind, el patrón and\s+1\s*=\s*1 debe considerar espacios opcionales alrededor del operador.
Los límites de palabra \b previenen falsos positivos: sin ellos, union matchea dentro de 'communion'. Pero cuidado: \b falla con caracteres no-ASCII. En campos de email, el patrón debe ser menos estricto porque direcciones legítimas pueden contener + o . que parecen operadores SQL.
Implementar validación en múltiples capas
Regex solo en backend es insuficiente. Los ataques sofisticados usan doble encoding (%2527 = %27 = '), unicode normalization (ᴜɴɪᴏɴ) o HPP (HTTP Parameter Pollution). Tu stack debe incluir: 1) WAF perimetral con reglas OWASP ModSecurity, 2) validación regex en API layer, 3) prepared statements en DAL.
Para formularios, combiná regex con whitelisting. Si el campo 'username' solo admite [a-zA-Z0-9_-], aplicá eso primero. El regex SQLi actúa como segunda línea contra payloads que pasen la whitelist. En campos de texto libre (bio, comentarios), el regex debe ser más agresivo: bloquear select, union, drop incluso en contexto legítimo, mejor que permitir un 0-day.
No confíes en client-side JavaScript para seguridad. Regex en frontend mejora UX (feedback inmediato) pero un atacante simplemente lo bypassea con curl. Todo request debe validarse en servidor. Log los matches: si alguien trigea el regex SQLi 10 veces en 1 minuto, es scan automatizado, no typo.
Detectar técnicas avanzadas de evasión
Ataques modernos explotan inline comments para fragmentar keywords: SEL/**/ECT, UN/*comment*/ION. Tu regex debe manejar /\*.*?\*/ entre caracteres. En MySQL, /*!50000 SELECT */ ejecuta solo en versiones específicas; el pattern necesita /\*!\d+.*?\*/.
El encoding alternativo es vector común: CHAR(117,110,105,111,110) construye 'union' dinámicamente. Patrones tipo char\s*\(\d+ detectan esto. En PostgreSQL, CHR() y CONCAT() sirven igual propósito. Hex encoding (0x756e696f6e) requiere 0x[0-9a-f]+.
El time-based blind injection moderno usa funciones más sutiles que SLEEP(): BENCHMARK(10000000, MD5('a')) en MySQL, WAITFOR DELAY '00:00:05' en MSSQL. Tu regex debe cubrir familias enteras de funciones de demora, no solo las obvias. Y ojo con RLIKE o REGEXP: permiten ReDoS (Regex DoS) que bloquea el servidor con patterns catastróficos.
Testing y mantenimiento de reglas
Construí un suite de test con 100+ payloads reales de sqlmap, PayloadsAllTheThings y CVEs recientes. Cada regex debe tener tasa de detección >95% sin falsos positivos en datos legítimos. Usá datasets tipo SecLists para validar.
Los falsos positivos son críticos: si tu regex bloquea búsquedas de "union membership" o nombres tipo "Shelby Andersen" (and + select), los usuarios reportan bugs en lugar de ataques. Tuneá con negative lookaheads: (?!.*membership)union excluye contextos safe.
Actualizá patterns trimestralmente. Seguí OWASP SQLi y PortSwigger Research para técnicas nuevas. Cuando SQLite lanza una función como JSONB_EXTRACT(), evaluá si necesita coverage. En producción, loguea todos los blocks: analizá mensualmente si hay patterns que ya no matchean (el ataque evolucionó) o matchean demasiado (falsos positivos). Los regex de seguridad no son set-and-forget.