Clasificar phishing en el navegador sin mandar la URL a ningún servidor
Las listas de navegación segura funcionan, pero implican contarle a alguien por dónde navegas. En NorthGate Browser estoy probando lo contrario: llevar un modelo ONNX pequeño al propio navegador para que la URL no salga nunca del equipo.
- machine-learning
- onnx
- rust
- phishing
- privacidad
- navegadores
Los navegadores llevan años protegiendo contra phishing con listas de reputación. Funciona bien y tiene un coste que casi nunca se menciona: para saber si un sitio es peligroso, alguien tiene que enterarse de que vas a visitarlo. Las implementaciones seria usan trucos para reducirlo, pero la forma del problema no cambia.
NorthGate Browser es un proyecto en desarrollo temprano donde estoy explorando la otra dirección: en lugar de mandar la URL al modelo, mandar el modelo al navegador.
Qué se puede saber solo con la URL
Antes de descargar nada, la propia cadena ya dice bastante. Las señales que estoy usando salen todas de ahí:
- Longitud y profundidad del dominio y de la ruta.
- Número de subdominios.
banco.seguro.login.ejemplo.tktiene una forma característica. - Entropía de los caracteres, que delata dominios generados por algoritmo.
- Presencia de punycode, la vía clásica del ataque homográfico.
- Marca conocida en el subdominio pero no en el dominio registrable. Esta es la señal más fuerte que he encontrado, y también la más fácil de explicar:
paypal.com.verificacion-cuenta.xyzno es PayPal. - Proporción de dígitos y guiones, y el TLD.
Nada de esto es concluyente por separado. Juntas, y con suficientes ejemplos, se separan razonablemente bien.
Por qué ONNX
Entrenar es cómodo en Python y ejecutar dentro de un navegador no lo es. ONNX resuelve exactamente ese hueco: se entrena con las herramientas de siempre, se exporta a un formato intermedio y se ejecuta desde el runtime nativo, sin arrastrar Python al producto final.
En un navegador eso importa por tres motivos muy concretos:
- El tamaño se paga en cada instalación. Un modelo de decenas de megas no es viable dentro de un binario que la gente descarga.
- El presupuesto de latencia es minúsculo. Esto tiene que resolverse antes de que la página pinte. Hablamos de un puñado de milisegundos, no de cientos.
- No puede depender de la red. Si necesitara conexión para decidir, habriamos vuelto al punto de partida.
La integración va en Rust, que es el lenguaje del núcleo sobre el que trabajo, y encaja bien: sin recolector de basura, sin pausas impredecibles en una ruta que se ejecuta en cada navegación.
El umbral es la decisión difícil
La parte que más tiempo me está llevando no es el modelo, es dónde poner el corte. Y es porque los dos errores no cuestan lo mismo:
- Un falso negativo deja pasar una página de phishing. Malo, pero es el estado actual sin ninguna protección.
- Un falso positivo marca el banco real del usuario como fraudulento. Eso es peor de lo que parece: entrena a la gente a ignorar el aviso. Y un aviso que se ignora no protege de nada, solo da sensación de que sí.
Por eso el umbral está deliberadamente conservador y la señal se plantea como advertencia, no como bloqueo. Prefiero cubrir menos y que el aviso conserve su significado.
Estado
Está en desarrollo temprano y lo digo sin adornos: hay un pipeline de extracción de características, un modelo que se comporta de forma razonable en validación y una integración que todavía no consideraria lista. Lo interesante del proyecto, de momento, es la restricción de partida: si la URL no sale del equipo, hay una categoría entera de fugas que simplemente no puede ocurrir. Diseñar con esa limitación desde el principio obliga a decisiones bastante más honestas que añadir privacidad al final.
Relacionado: medí después si el desacuerdo entre varios clasificadores ayuda a priorizar qué correo revisar — la respuesta no fue la esperada.