MASTG-KNOW-0034: Vinculação de Dispositivo
O objetivo do vínculo de dispositivo (device binding) é impedir que um atacante copie um aplicativo e seu estado do dispositivo A para o dispositivo B e continue executando o aplicativo no dispositivo B. Após o dispositivo A ser considerado confiável, ele pode ter mais privilégios que o dispositivo B. Esses privilégios diferenciais não devem ser alterados quando um aplicativo é copiado do dispositivo A para o dispositivo B.
Antes de descrevermos os identificadores utilizáveis, vamos discutir rapidamente como eles podem ser usados para o vínculo. Existem três métodos que permitem o vínculo de dispositivo:
-
Reforçar as credenciais usadas para autenticação com identificadores de dispositivo. Isso faz sentido se o aplicativo precisar se reautenticar e/ou reautenticar o usuário com frequência.
-
Criptografar os dados armazenados no dispositivo com material de chave fortemente vinculado ao dispositivo pode fortalecer o vínculo de dispositivo. O Android Keystore oferece chaves privadas não exportáveis que podemos usar para isso. Quando um ator malicioso extrai esses dados de um dispositivo, não seria possível descriptografá-los, pois a chave não está acessível. A implementação disso segue as seguintes etapas:
- Gerar o par de chaves no Android Keystore usando a API
KeyGenParameterSpec.
//Fonte: <https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html> KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); keyPairGenerator.initialize( new KeyGenParameterSpec.Builder( "key1", KeyProperties.PURPOSE_DECRYPT) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) .build()); KeyPair keyPair = keyPairGenerator.generateKeyPair(); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); ... // O par de chaves também pode ser obtido do Android Keystore a qualquer momento da seguinte forma: KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); PrivateKey privateKey = (PrivateKey) keyStore.getKey("key1", null); PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();- Gerar uma chave secreta para AES-GCM:
//Fonte: <https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html> KeyGenerator keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); keyGenerator.init( new KeyGenParameterSpec.Builder("key2", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); SecretKey key = keyGenerator.generateKey(); // A chave também pode ser obtida do Android Keystore a qualquer momento da seguinte forma: KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); key = (SecretKey) keyStore.getKey("key2", null);- Criptografar os dados de autenticação e outros dados sensíveis armazenados pelo aplicativo usando uma chave secreta por meio do cipher AES-GCM e usar parâmetros específicos do dispositivo, como Instance ID, etc., como dados associados (associated data):
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); final byte[] nonce = new byte[GCM_NONCE_LENGTH]; random.nextBytes(nonce); GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce); cipher.init(Cipher.ENCRYPT_MODE, key, spec); byte[] aad = "<deviceidentifierhere>".getBytes();; cipher.updateAAD(aad); cipher.init(Cipher.ENCRYPT_MODE, key); // usar o cipher para criptografar os dados de autenticação; consulte 0x50e para mais detalhes.- Criptografar a chave secreta usando a chave pública armazenada no Android Keystore e armazenar a chave secreta criptografada no armazenamento privado do aplicativo.
- Sempre que dados de autenticação, como tokens de acesso ou outros dados sensíveis, forem necessários, descriptografar a chave secreta usando a chave privada armazenada no Android Keystore e, em seguida, usar a chave secreta descriptografada para descriptografar o texto cifrado.
- Gerar o par de chaves no Android Keystore usando a API
-
Usar autenticação de dispositivo baseada em token (Instance ID) para garantir que a mesma instância do aplicativo seja usada.