Neutron, nuestro motor de IA, obtuvo un 96.75% en el benchmark CyberGym de UC Berkeley. Más información

Seguridad

Seguridad

¿Qué es el SSL pinning? Guía para Android con OkHttp

Implemente SSL pinning en Android con Network Security Config, OkHttp y Retrofit, y después pruébelo y rote los pines de forma segura. Incluye ejemplos con bibliotecas heredadas.

SSL pinning en Android (fijación de certificados y de claves públicas)

Proteger la comunicación entre una aplicación Android y su backend empieza por TLS. TLS protege los datos en tránsito cifrando la conexión, autenticando al servidor e impidiendo que el tráfico sensible se envíe en texto claro. En una configuración estándar de Android, la aplicación se apoya en el almacén de confianza de la plataforma y en la validación normal de certificados para decidir si debe confiar en el certificado de un servidor.

El pinning añade una restricción de confianza adicional sobre esa base. En lugar de aceptar cualquier cadena de certificados que se valide mediante el modelo de confianza normal, la aplicación solo acepta las cadenas que contienen un material de clave concreto y esperado. Eso puede dificultar los ataques de intermediario (man-in-the-middle) en algunos escenarios, pero también introduce un riesgo operativo: los cambios de certificado, la migración de CA o la rotación de claves pueden interrumpir conexiones legítimas si el pinning es demasiado rígido o está mal gestionado. La guía de Android sobre la fijación de certificados es prudente en este punto: señala que, por lo general, no se recomienda el pinning para la mayoría de las aplicaciones, porque los cambios en la configuración del servidor pueden romper la conectividad a menos que se actualice el cliente.

Esta guía explica qué significa el SSL pinning en Android, cuándo resulta útil, contra qué no protege, las principales opciones de implementación, cómo validarlo de forma segura y cómo operarlo en producción sin convertir la rotación de certificados en una interrupción del servicio. También incluye ejemplos con HttpsURLConnection, OkHttp, Retrofit, Volley y Picasso para los equipos que trabajan tanto con pilas de red modernas como heredadas en Android.

Comparación de confianza en Android: la CA por defecto acepta varias cadenas de certificados; la confianza con pinning solo acepta las cadenas que contienen la clave fijada y rechaza las demás.

Tabla de contenidos

  1. Qué significa el SSL pinning en Android
  2. SSL pinning frente a TLS pinning frente a certificate pinning
  3. Contra qué no protege el pinning
  4. Modelo de amenazas: cuándo ayuda el pinning
  5. Opciones de pinning en Android
  6. Ejemplos de implementación
  7. Cómo verificar que el SSL pinning funciona
  8. Cómo operar el pinning de forma segura en producción
  9. Lecturas relacionadas
  10. Preguntas frecuentes

Qué significa el SSL pinning en Android

En una configuración estándar de Android, una aplicación confía en los certificados de servidor que se validan mediante el almacén de confianza de la plataforma. El SSL pinning añade una restricción adicional sobre ese modelo: la conexión solo se acepta si la cadena de certificados contiene un material de clave concreto y esperado para el dominio de destino.

En Android, esto se implementa normalmente fijando los hashes de la clave pública del certificado, lo que también se conoce como SPKI pinning. En lugar de confiar en cualquier cadena válida anclada en una autoridad de certificación de confianza general, la aplicación restringe la confianza a las cadenas que incluyen una de las claves públicas fijadas.

En la práctica, esto significa que la aplicación no comprueba solo si un certificado es válido, sino también si cumple una expectativa de confianza más específica definida por la propia aplicación. CertificatePinner de OkHttp sigue la misma idea al validar la cadena de certificados del servidor frente a los hashes de clave pública fijados.

SSL pinning frente a TLS pinning frente a certificate pinning

«SSL pinning» sigue siendo la expresión que utiliza la mayoría de la gente, pero en la práctica suele referirse al certificate pinning de TLS o al pinning de claves públicas. En la guía de seguridad actual de Android, el pinning se describe en términos de hashes de claves públicas. A lo largo de esta guía, los términos SSL pinning, TLS pinning, certificate pinning y public key pinning se emplean tal como los profesionales suelen buscarlos y comentarlos, manteniendo al mismo tiempo la precisión del significado técnico.

Contra qué no protege el pinning

El pinning no sustituye a una validación TLS adecuada. Si una aplicación utiliza un HostnameVerifier inseguro o un unsafe X509TrustManager, puede seguir siendo vulnerable a la suplantación y a la interceptación. Android documenta ambos casos como riesgos de seguridad explícitos, porque pueden hacer que la aplicación acepte conexiones maliciosas o no válidas.

El pinning tampoco corrige vulnerabilidades del lado del servidor, credenciales filtradas, una gestión de sesiones defectuosa, API inseguras ni la exposición de datos sensibles en otras partes de la aplicación. Es un control de seguridad del transporte, no una estrategia completa de seguridad de aplicaciones móviles. Esa es una de las razones para tratar el pinning como parte de un modelo de seguridad de Android más amplio y no como una casilla de bastionado independiente.

Modelo de amenazas: cuándo ayuda el pinning

La documentación de Android explica con claridad el escenario central: por defecto, las aplicaciones confían en muchas autoridades de certificación preinstaladas, y si una de esas CA emitiera un certificado fraudulento, la aplicación podría quedar expuesta a un atacante en la ruta de comunicación. El pinning reduce esa confianza al exigir que la cadena de certificados contenga una de las claves públicas fijadas. OkHttp describe la misma protección como una defensa frente a ataques a las autoridades de certificación y frente a autoridades de certificación man-in-the-middle, conocidas o desconocidas para el usuario.

Eso hace que el pinning sea más relevante cuando un equipo quiere un control más estricto sobre las cadenas de confianza que se aceptan para un backend concreto. Por lo general, resulta más fácil de justificar cuando el backend es estable, el ciclo de vida de los certificados se gestiona con rigor y la carga operativa de la rotación ya se ha planificado. Incluso entonces, el pinning es un control de alta fricción y no debe introducirse a la ligera. Android lo trata como una medida operativamente frágil, y la propia documentación de OkHttp es lo bastante explícita como para calificarlo así: «¡El certificate pinning es peligroso! porque limita las actualizaciones de certificados y añade complejidad operativa.

Opciones de pinning en Android

En las bases de código de Android más recientes, el pinning se gestiona con más frecuencia mediante la Android Network Security Configuration (véase el ejemplo en la sección siguiente), que admite la fijación de certificados, los pines de respaldo, la caducidad de los pines, los anclajes de confianza personalizados y las anulaciones para depuración en un archivo de configuración declarativo. Esto mantiene centralizadas las decisiones de confianza en lugar de repartir la gestión de TLS entre varias bibliotecas. Si la aplicación utiliza OkHttp directamente, CertificatePinner sigue siendo la principal opción a nivel de cliente. Dado que Retrofit se apoya en un cliente HTTP, el comportamiento del pinning en las configuraciones con Retrofit depende del cliente subyacente configurado, normalmente OkHttp.

En las bases de código de Android más antiguas, el pinning aún puede depender de una gestión de TLS personalizada en HttpsURLConnection, Volley o integraciones con Picasso. Estos ejemplos pueden seguir siendo útiles como referencias históricas o para mantener aplicaciones heredadas, pero deben tratarse como patrones de implementación antiguos y no como la opción predeterminada para proyectos nuevos. Para el pinning en Android, OWASP MASTG describe la Network Security Configuration como el enfoque preferido y recomendado, y advierte al mismo tiempo de que el pinning basado en un TrustManager personalizado es un método de bajo nivel que es propenso a errores si no se implementa con cuidado

Enfoque Admite pinning Carga de mantenimiento Principal trampa Mejor opción para
Android Network Security Configuration Sí Menor Requiere una configuración cuidadosa de dominios y de depuración La mayoría de las aplicaciones Android modernas
OkHttp CertificatePinner Sí Media Hay que planificar la rotación y la cobertura de nombres de host Aplicaciones que ya usan OkHttp directamente
Retrofit con un cliente OkHttp configurado Sí Media Retrofit hereda el comportamiento del cliente; el pinning no es independiente Aplicaciones basadas en Retrofit
Configuración de confianza personalizada con HttpsURLConnection Posible Mayor Es fácil personalizar en exceso la gestión de TLS Solo para mantenimiento de código heredado
Configuración de TLS personalizada con Volley Posible Mayor Más difícil de mantener centralizada Solo para mantenimiento de código heredado
Configuración de descargador personalizado con Picasso Posible Mayor Patrón de integración antiguo Solo para mantenimiento de código heredado

Esta comparación refleja la guía actual de Android, la documentación de OkHttp y la diferencia práctica entre la configuración declarativa a nivel de plataforma y la personalización de la confianza específica de cada biblioteca, propia de enfoques más antiguos.

Ejemplos de implementación

El pinning puede implementarse de distintas maneras según la pila de red de Android que se utilice. Para trabajo nuevo, comience con Network Security Configuration o con una configuración moderna de OkHttp. Los ejemplos de bibliotecas que siguen se entienden mejor como patrones de referencia heredados para los equipos que aún mantienen pilas más antiguas.

Recomendación moderna antes de los ejemplos heredados

Si hoy está creando una aplicación Android nueva, comience con la Android Network Security Configuration para la confianza y el pinning declarativos, o con CertificatePinner de OkHttp si su pila de red ya se centra en OkHttp. Android admite el pinning de claves públicas mediante pin-set, admite pines de respaldo, permite una fecha de caducidad opcional e incluye anulaciones solo para depuración, de modo que los equipos puedan probar de forma segura sin debilitar las compilaciones de release.

Ejemplo de pinning con Android Network Security Configuration (NSC) (declarativo)

Cree res/xml/network_security_config.xml:

    <?xml version="1.0" encoding="utf-8"?>
<network-security-config>

    <!-- Apply rules to a specific domain (and optionally its subdomains) -->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">example.com</domain>

        <!-- Public-key pins (SHA-256 of SPKI), with an expiration date -->
        <pin-set expiration="2027-12-31">
            <!-- Primary pin -->
            <pin digest="SHA-256">afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=</pin>

            <!-- Backup pin (rotate keys/certs safely) -->
            <pin digest="SHA-256">BASE64_SHA256_BACKUP_SPKI_PIN_HERE=</pin>
        </pin-set>

        <!-- Optional: customize which CAs you trust for this domain -->
        <trust-anchors>
            <certificates src="system" />
            <!-- If you also need a private CA bundled with the app: -->
            <!-- <certificates src="@raw/my_private_ca" /> -->
        </trust-anchors>
    </domain-config>

    <!-- Optional (debug builds): allow user-added / debug CAs without weakening release -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
            <!-- or a dedicated debug CA -->
            <!-- <certificates src="@raw/debug_ca" /> -->
        </trust-anchors>
    </debug-overrides>

</network-security-config>

Puntos clave: pin-set admite varios pines (principal y de respaldo) y una fecha de caducidad (formato yyyy-MM-dd), tras la cual el pinning se desactiva si no se actualiza la configuración. Conéctelo en AndroidManifest.xml

<application
    android:networkSecurityConfig="@xml/network_security_config"
    android:usesCleartextTraffic="false"
    ... >
</application>

Certificate pinning en Android con HttpsURLConnection

    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    // Generate the certificate using the certificate file under res/raw/cert.cer
    InputStream caInput = new BufferedInputStream(getResources().openRawResource(R.raw.cert));
    Certificate ca = cf.generateCertificate(caInput);
    caInput.close();

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);

    // Tell the URLConnection to use a SocketFactory from our SSLContext
    URL url = new URL("https://example.com/faq/");
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());
    InputStream in = urlConnection.getInputStream();
    theString = readInputStream(in);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");

Certificate pinning en Android con OkHTTP

    //okhttp version 3.x

    public CertificatePinning()
    {
        // We add the public key of our trusted server hashed and encoded base64
        client = new OkHttpClient.Builder().certificatePinner(new CertificatePinner.Builder()
        .add("https://example.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=").build()).build();
    }

    public void run() throws Exception
    {
        Request request = new Request.Builder().url("https://example.com/faq").build();

        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    /**
    * response.handshake() contains the TLS handshake of the connection that carried this response, or null if the response
    * was received without TLS.
    */
        if(response.handshake())
        {
            for (Certificate certificate : response.handshake().peerCertificates())
            {
                //Pin returns SHA-256 hash of the public key
                String caPin = CertificatePinner.pin(certificate);

                // then you we to check the hash value and apply the corresponding action
            }
        }
    }

Certificate pinning en Android con Retrofit

    // Use the steps above to create OkHttpClient and set the custom client when building adapter
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://example.com")
    .addConverterFactory(GsonConverterFactory.create())
    .client(client)
    .build();
  Volley:

    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    // Generate the certificate using the certificate file under res/raw/cert.cer
    InputStream caInput = new BufferedInputStream(getResources().openRawResource(R.raw.cert));
    Certificate ca = cf.generateCertificate(caInput);
    caInput.close();

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore trusted = KeyStore.getInstance(keyStoreType);
    trusted.load(null, null);
    trusted.setCertificateEntry("ca", ca);

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(trusted);

    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);

    SSLSocketFactory sf = context.getSocketFactory();
    mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HurlStack(null, sf));

Certificate pinning en Android con Picasso

    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    // Certificate file is under res/raw/cert.cer
    InputStream caInput = new BufferedInputStream(getResources().openRawResource(R.raw.cert));
    Certificate ca = cf.generateCertificate(caInput);
    caInput.close();

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);

    OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
    OkHttpDownloader okHttpDownloader = new OkHttpDownloader(okHttpClient);
    Picasso.Builder builder = new Picasso.Builder(context);
    builder.downloader(okHttpDownloader);
    Picasso sPicasso = builder.build();

Cómo verificar que el SSL pinning funciona (sin afectar a los usuarios)

Para verificar el SSL pinning en aplicaciones Android e iOS, configure un proxy como Burp Suite o mitmproxy y confirme que la aplicación bloquea la interceptación del tráfico a pesar de confiar en el certificado de la CA del proxy. Esto demuestra que el pinning evita los ataques de intermediario.

Requisitos previos:

  1. Instale mitmproxy: pip install mitmproxy.
  2. Ejecute mitmdump -p 8080 (o mitmproxy para la interfaz); anote su IP (por ejemplo, con ifconfig o ip addr).
  3. Se recomienda un Android con root o un iOS con jailbreak para una confianza completa; también funciona en emuladores y simuladores.

Pasos en Android

  1. Configure el proxy Wi-Fi del dispositivo en :8080; pruebe con HTTPS en el navegador (debería pasar por el proxy).
  2. Visite http://mitm.it/?mode=android en el navegador; descargue e instale la CA de usuario.
  3. Para la confianza del sistema (necesaria para la mayoría de las aplicaciones): openssl x509 -inform PEM -subject_hash_old -in ~/.mitmproxy/mitmproxy-ca-cert.pem | head -1 → .0; adb push .0 /system/etc/security/cacerts/; adb shell chmod 644 /system/etc/security/cacerts/.0; reinicie.
  4. Active las llamadas de red de la aplicación: si no hay flujos en mitmproxy/mitmdump y aparecen errores SSL en adb logcat | grep -i ssl, el pinning está activo.

Validación:

El funcionamiento normal de la aplicación sin el proxy confirma que el problema es el pinning y no la configuración. Si el tráfico se intercepta, no hay pinning (o use herramientas de bypass como objection para confirmarlo).

Intente hacerlo al iniciar la aplicación, pero también después, ya que algunas aplicaciones podrían comprobarlo solo durante acciones concretas e ignorar la comprobación a partir de entonces.

Cómo operar el pinning de forma segura en producción (rotación, pines de respaldo, despliegue)

El principal riesgo del pinning en producción es la rotación de certificados o claves. Android recomienda explícitamente incluir siempre una clave de respaldo para que, si necesita cambiar de claves o de CA, la conectividad de la aplicación no se pierda. También admite una fecha de caducidad para los pines, lo que puede reducir el riesgo de fallos indefinidos en versiones obsoletas de la aplicación, aunque a cambio los pines caducados dejan de proteger la conexión.

OkHttp es igual de directo sobre la carga operativa: el pinning limita la capacidad del equipo de servidores para actualizar certificados y migrar entre autoridades de certificación. Por eso el pinning debe desplegarse como una funcionalidad de producción, no simplemente integrarse como una limpieza de código. Una ruta de despliegue sensata consiste en pasar primero por las compilaciones internas, luego por la beta, después por un porcentaje limitado de producción y solo entonces desplegarlo por completo, una vez validados el comportamiento de los certificados, la monitorización y los planes de contingencia. El consejo de despliegue escalonado que se da aquí es una recomendación operativa basada en las restricciones que documentan Android y OkHttp.

Cuando el pinning falla en producción, la aplicación debe fallar de una manera comprensible y diagnosticable. Como mínimo, defina el mensaje que ve el usuario, los campos de registro, los umbrales de alerta y la ruta de triaje de soporte. Un fallo de pinning es distinto de un tiempo de espera genérico o de una situación sin conexión, por lo que no debe perderse en la misma categoría de errores.

La inspección TLS empresarial y los portales cautivos también merecen una decisión de política explícita. Dado que el pinning restringe la confianza a un material de clave esperado, los entornos que sustituyen los certificados del servidor pueden hacer que el pinning falle por diseño. Los equipos deben decidir ese comportamiento de forma deliberada antes del despliegue, en lugar de descubrirlo durante este. Para ver un ejemplo técnico de análisis del tráfico en entornos con pinning sin desactivarlo, consulte nuestro artículo Bypass universal del SSL pinning ... de la teoría a una PoC completa y funcional con LLDB.

Preguntas frecuentes

¿Vale la pena el SSL pinning en Android?

Puede valer la pena, pero solo cuando el backend, el proceso de publicación y el ciclo de vida de los certificados son lo bastante maduros para sostenerlo. La guía oficial de Android advierte expresamente de que, por lo general, no se recomienda el certificate pinning para muchas aplicaciones, porque los cambios de certificado en el backend pueden romper la conectividad a menos que se actualice el cliente. Cuando los equipos deciden usar pinning, Android recomienda pines de respaldo y una ventana de caducidad corta.

¿Cuál es la diferencia entre el certificate pinning y el public key pinning?

En la guía actual de Android, el pinning se expresa como hashes de la clave pública del certificado, y no como una comparación literal del archivo completo del certificado. CertificatePinner de OkHttp también documenta el pinning basado en SPKI. ¿Qué se rompe cuando los certificados rotan? Si la nueva cadena de certificados ya no contiene una de las claves fijadas, la aplicación puede perder la conectividad hasta que se actualicen los pines o hasta que ya exista un pin de respaldo. Por eso Android recomienda claves de respaldo y admite la caducidad opcional de los pines.

¿Admite la Android Network Security Configuration el pinning?

Sí. Android admite el certificate pinning mediante pin-set entries en la Network Security Configuration, incluidos varios pines, pines de respaldo, caducidad, anclajes de confianza personalizados y anulaciones solo para depuración.

¿Hace Retrofit pinning por sí mismo?

No. Retrofit convierte su API HTTP en una interfaz de Java o Kotlin, y el comportamiento del pinning proviene del cliente subyacente configurado, como OkHttp.

¿Cómo pruebo el pinning sin debilitar la seguridad de producción?

Utilice pruebas controladas en un entorno de staging, valide tanto las rutas de éxito como las de discrepancia y use la compatibilidad de Android con la configuración de depuración en lugar de debilitar la lógica de validación de las compilaciones de release.

¿Cómo deben gestionar las aplicaciones Android la inspección TLS empresarial?

Los equipos deben decidir esa política de forma explícita antes del despliegue. Dado que el pinning valida un material de clave concreto, los entornos con inspección TLS pueden fallar por diseño, por lo que el comportamiento esperado debe documentarse en lugar de que los usuarios lo descubran durante el despliegue.

¿Cuál es el punto de partida más moderno para las aplicaciones Android nuevas?

Para la mayoría de las aplicaciones nuevas, comience con la Network Security Configuration para un pinning declarativo y centralizado, o use OkHttp CertificatePinner cuando la capa de red ya se centre en OkHttp. Mantenga las configuraciones de TLS personalizadas y específicas de bibliotecas antiguas únicamente cuando mantenga código heredado