La URL que no debe cruzar el puente
Un comando que acepta una URL del frontend convierte tu aplicación en un intermediario para cualquiera. Qué es esa vulnerabilidad, por qué en escritorio es distinta, y cómo se cierra.
Qué permite
// mal: el frontend decide a dónde va la petición#[tauri::command]async fn pedir(url: String) -> Result<String, Error> { Ok(reqwest::get(&url).await?.text().await?)}Con eso, cualquier código que corra en la webview —una dependencia comprometida, contenido inyectado— puede hacer que tu proceso pida cualquier cosa. Y el core alcanza sitios que la webview no:
La última fila es la peor combinación: un comando genérico que además pone el testigo manda tus credenciales a donde diga el frontend.
Las tres defensas
La defensa 1 es la respuesta correcta el 90% de las veces, y es el nivel 14.4 otra vez: las URLs que una aplicación usa son un conjunto conocido.
Cuando la URL sí es dinámica
Hay casos legítimos —descargar un adjunto cuya dirección viene de tu propio servicio, o abrir un recurso que el usuario ha pegado— y entonces la validación tiene que hacerse bien:
- Analiza la URL, no la compares como texto. Comprobar que empieza por tu dominio es un error clásico:
https://tudominio.com.atacante.netempieza por tu dominio. - Comprueba el esquema. Solo el cifrado, y nunca esquemas de fichero o propios.
- Rechaza direcciones locales y privadas, que es lo que cierra la primera fila de la tabla. Y hazlo después de resolver el nombre, porque un dominio puede resolver a una dirección local.
- Y no sigas redirecciones a ciegas: una URL permitida puede redirigir a una que no lo está. Hay que revalidar en cada salto, o desactivar el seguimiento automático.
La redirección es la que se olvida siempre y convierte una validación cuidadosa en decorativa.
Y el testigo
Una regla que cierra la peor combinación: el testigo se añade por destino, no por defecto.
// la capa de red decide si esta petición lleva credencialesfn peticion(&self, destino: &Destino) -> RequestBuilder { let b = self.cliente.request(destino.metodo(), destino.url()); if destino.es_nuestro_servicio() { b.bearer_auth(self.testigo()) } else { b }}Con eso, aunque alguien consiga que tu aplicación pida algo a un destino inesperado, no va con tus credenciales. Es una línea y elimina la consecuencia más grave.