Mejores prácticas para almacenar y proteger claves API privadas en aplicaciones [cerrado]
La mayoría de los desarrolladores de aplicaciones integrarán algunas bibliotecas de terceros en sus aplicaciones. Si es para acceder a un servicio, como Dropbox o YouTube, o para registrar fallos. La cantidad de bibliotecas y servicios de terceros es asombrosa. La mayoría de esas bibliotecas y servicios se integran mediante la autenticación de alguna manera con el servicio, la mayoría de las veces, esto sucede a través de una clave API. Por motivos de seguridad, los servicios suelen generar una clave pública y privada, a menudo también denominada secreta. Desafortunadamente, para conectarse a los servicios, esta clave privada debe usarse para autenticarse y, por lo tanto, probablemente ser parte de la aplicación. No hace falta decir que esto enfrenta un inmenso problema de seguridad. Las claves API públicas y privadas se pueden extraer de los APK en cuestión de minutos y se pueden automatizar fácilmente.
Suponiendo que tengo algo similar a esto, ¿cómo puedo proteger la clave secreta?
public class DropboxService {
private final static String APP_KEY = "jk433g34hg3";
private final static String APP_SECRET = "987dwdqwdqw90";
private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;
// SOME MORE CODE HERE
}
¿Cuál es, en su opinión, la mejor y más segura forma de almacenar la clave privada? Ofuscación, cifrado, ¿qué opinas?
Tal como están las cosas, su aplicación compilada contiene las cadenas de claves, pero también los nombres de constantes APP_KEY y APP_SECRET. Extraer claves de dicho código autodocumentado es trivial, por ejemplo con la herramienta estándar de Android dx.
Puedes aplicar ProGuard. Dejará las cadenas de claves intactas, pero eliminará los nombres de las constantes. También cambiará el nombre de las clases y métodos con nombres cortos y sin sentido, siempre que sea posible. Extraer las claves lleva más tiempo para determinar qué cadena sirve para qué propósito.
Tenga en cuenta que configurar ProGuard no debería ser tan difícil como cree. Para empezar, sólo necesita habilitar ProGuard, como se documenta en project.properties. Si hay algún problema con las bibliotecas de terceros, es posible que deba suprimir algunas advertencias y/o evitar que se ofusquen en proguard-project.txt. Por ejemplo:
-dontwarn com.dropbox.** -keep class com.dropbox.** { *; }
Este es un enfoque de fuerza bruta; Puede refinar dicha configuración una vez que la aplicación procesada funcione.
Puedes ofuscar las cadenas manualmente en tu código, por ejemplo con una codificación Base64 o preferiblemente con algo más complicado; tal vez incluso código nativo. Luego, un pirata informático tendrá que aplicar ingeniería inversa estática a su codificación o interceptar dinámicamente la decodificación en el lugar adecuado.
Puede aplicar un ofuscador comercial, como el hermano especializado de ProGuard, DexGuard . Además, puede cifrar/ofuscar las cadenas y clases por usted. Extraer las claves requiere aún más tiempo y experiencia.
Es posible que pueda ejecutar partes de su aplicación en su propio servidor. Si puedes guardar las llaves allí, estarán seguras.
Al final, lo que hay que hacer es una compensación económica: qué tan importantes son las claves, cuánto tiempo o software puede permitirse, qué tan sofisticados son los hackers que están interesados en las claves, cuánto tiempo querrán dedicarlas. gastar, cuánto vale un retraso antes de que las claves sean pirateadas, en qué escala los piratas informáticos exitosos distribuirán las claves, etc. Las pequeñas piezas de información, como las claves, son más difíciles de proteger que las aplicaciones completas. Intrínsecamente, nada del lado del cliente es inquebrantable, pero ciertamente puedes subir el listón.
(Soy el desarrollador de ProGuard y DexGuard)
Pocas ideas, en mi opinión sólo la primera da alguna garantía:
Guarde sus secretos en algún servidor de Internet y, cuando sea necesario, simplemente consígalos y utilícelos. Si el usuario está a punto de utilizar Dropbox, nada le impide realizar una solicitud a su sitio y obtener su clave secreta.
Coloque sus secretos en código jni, agregue código variable para hacer sus bibliotecas más grandes y más difíciles de descompilar. También puedes dividir la cadena de claves en algunas partes y guardarlas en varios lugares.
use el ofuscador, también ingrese el código secreto con hash y luego deshazlo cuando sea necesario.
Coloque su clave secreta como los últimos píxeles de una de sus imágenes en activos. Luego, cuando sea necesario, léelo en tu código. Ofuscar su código debería ayudar a ocultar el código que lo leerá.
Si desea echar un vistazo rápido a lo fácil que es leer el código apk, utilice APKAnalyser:
http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/
¡Otro enfoque es no tener el secreto en el dispositivo en primer lugar! Consulte Técnicas de seguridad de API móviles (especialmente la parte 3).
Utilizando la tradicional tradición de indirección, comparta el secreto entre su punto final API y un servicio de autenticación de aplicaciones.
Cuando su cliente quiere realizar una llamada API , solicita al servicio de autenticación de la aplicación que la autentique (utilizando técnicas sólidas de certificación remota) y recibe un token de tiempo limitado (generalmente JWT ) firmado por el secreto.
El token se envía con cada llamada a la API donde el punto final puede verificar su firma antes de actuar según la solicitud.
El secreto real nunca está presente en el dispositivo; de hecho, la aplicación nunca tiene idea de si es válida o no, simplemente solicita autenticación y transmite el token resultante. Como beneficio adicional de la indirección, si alguna vez desea cambiar el secreto, puede hacerlo sin necesidad de que los usuarios actualicen sus aplicaciones instaladas.
Entonces, si desea proteger su secreto, no tenerlo en su aplicación en primer lugar es una buena manera de hacerlo.
Antigua forma no segura:
Siga 3 sencillos pasos para proteger la API/clave secreta ( respuesta anterior )
Podemos usar Gradle para proteger la clave API o la clave secreta.
1. gradle.properties (Propiedades del proyecto): crea una variable con clave.
GoogleAPIKey = "Your API/Secret Key"
2. build.gradle (Módulo: aplicación): establece la variable en build.gradle para acceder a ella en actividad o fragmento. Agregue el siguiente código a buildTypes {}.
buildTypes.each {
it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}
3. Acceda a él en Actividad/Fragmento mediante BuildConfig de la aplicación:
BuildConfig.GoogleSecAPIKEY
Actualizar:
La solución anterior es útil en el proyecto de código abierto para comprometerse a través de Git. (Gracias a David Rawson y riyaz-ali por tu comentario).
Según los comentarios de Matthew y Pablo Cegarra, la forma anterior no es segura y Decompiler permitirá que alguien vea BuildConfig con nuestras claves secretas.
Solución:
Podemos usar NDK para proteger claves API. Podemos almacenar claves en la clase nativa C/C++ y acceder a ellas en nuestras clases Java.
Siga este blog para proteger las claves API mediante NDK.
Un seguimiento sobre cómo almacenar tokens de forma segura en Android
Agregar a la solución @Manohar Reddy, se puede usar firebase Database o firebase RemoteConfig (con valor predeterminado nulo):
- Cifra tus claves
- Guárdelo en la base de datos de Firebase
- Consíguelo durante el inicio de la aplicación o cuando sea necesario
- descifrar claves y utilizarlas
¿Qué es diferente en esta solución?
- sin credenciales para firebase
- El acceso a Firebase está protegido, por lo que solo las aplicaciones con certificado firmado tienen privilegios para realizar llamadas API.
- Cifrado/descifrado para evitar la interceptación por intermediarios. Sin embargo, ya llama a https a firebase