Boa tarde, Habr! Apresento a você a tradução do artigo sobre os princípios básicos da segurança de dados confidenciais em aplicativos iOS
"Application Security Musts for Every
iOS App" por Arlind Aliu.
A segurança de aplicativos é um dos aspectos mais importantes do desenvolvimento de software. Os usuários de aplicativos esperam que as informações fornecidas sejam protegidas com segurança. Portanto, você não pode simplesmente fornecer informações confidenciais a alguém.
Felizmente, neste artigo, discutiremos os erros que os desenvolvedores cometem em seus aplicativos, bem como maneiras de resolvê-los.
Continua sob o corte.
Armazenamento de dados no lugar errado
Realizei um estudo de vários aplicativos da AppStore e muitos cometem o mesmo erro: informações confidenciais são armazenadas onde não deveriam estar.
Se você armazena dados pessoais em
UserDefaults , coloca-os em risco.
Os padrões do usuário são armazenados em um arquivo com uma lista de propriedades, localizada na pasta "Configurações" do seu aplicativo. Os dados são armazenados no aplicativo sem a menor sugestão de criptografia.
Ao instalar um programa de terceiros no mac, como o iMazing, você nem precisa hackear o telefone, mas vê imediatamente todos os dados dos
padrões de usuário do aplicativo instalado na AppStore. Esses programas permitem assistir e gerenciar dados de aplicativos instalados no iPhone. Você pode facilmente obter os
UserDefaults de qualquer aplicativo.
Esse é o principal motivo pelo qual decidi escrever um artigo - encontrei um monte de aplicativos na AppStore que armazenam dados nos
padrões de usuário , como: tokens, assinaturas ativas e renováveis, a quantidade de dinheiro disponível e assim por diante. Todos esses dados podem ser facilmente obtidos e usados com intenções maliciosas, desde o gerenciamento de assinaturas pagas no aplicativo até hackers no nível da rede e coisas piores.
E agora sobre como armazenar dados.
Lembre-se de que apenas uma pequena quantidade de informações deve ser armazenada nos
padrões do
usuário , como configurações no aplicativo, ou seja, dados que não são confidenciais para o usuário.
Use os serviços de segurança dedicados da Apple para armazenar informações pessoais. O serviço de API de chaveiro permite armazenar uma certa quantidade de dados do usuário em um banco de dados criptografado. Lá, você pode armazenar senhas e outros dados importantes para o usuário, como informações de cartão de crédito ou até pequenas notas importantes.
Além disso, pode haver chaves e certificados criptografados com os quais você trabalha.
Serviço de API de chaveiro
A seguir, é apresentado um exemplo de como salvar a senha de um usuário no Keychain.
class KeychainService { func save(_ password: String, for account: String) { let password = password.data(using: String.Encoding.utf8)! let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecValueData as String: password] let status = SecItemAdd(query as CFDictionary, nil) guard status == errSecSuccess else { return print("save error") } }
Parte do dicionário
kSecClass: kSecClassGenericPassword significa que as informações que precisam ser criptografadas são a senha. Em seguida, adicionamos a nova senha ao chaveiro chamando o método
SecItemAdd . Recuperar dados de um pacote configurável é semelhante a salvar.
func retrivePassword(for account: String) -> String? { let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecMatchLimit as String: kSecMatchLimitOne, kSecReturnData as String: kCFBooleanTrue] var retrivedData: AnyObject? = nil let _ = SecItemCopyMatching(query as CFDictionary, &retrivedData) guard let data = retrivedData as? Data else {return nil} return String(data: data, encoding: String.Encoding.utf8) }
Vamos escrever uma pequena verificação sobre a exatidão de salvar e receber dados.
func testPaswordRetrive() { let password = "123456" let account = "User" keyChainService.save(password, for: account) XCTAssertEqual(keyChainService.retrivePassword(for: account), password) }
À primeira vista, pode parecer que a API do Keychain seja bastante difícil de usar, especialmente se você precisar salvar mais de uma senha, por isso peço que você use o padrão Fachada para esses fins. Isso permitirá que você salve e modifique dados, dependendo das necessidades do aplicativo.
Se você quiser saber mais sobre esse padrão, bem como criar um wrapper simples para subsistemas complexos, este
artigo o ajudará. Também na Internet, há bibliotecas abertas que ajudam a usar a API Keychain, por exemplo,
SAMKeychain e
SwiftKeychainWrapper .
Salvamento e Autorização de Senha
Na minha carreira de desenvolvimento, enfrento constantemente o mesmo problema. Os desenvolvedores armazenam senhas no aplicativo ou criam uma solicitação ao servidor, que envia o nome de usuário e a senha diretamente.
Se você armazenar dados no
UserDefault , depois de ler as informações da primeira parte do artigo, você já entenderá o quanto corre o risco. Ao armazenar senhas no Keychain, você aumenta seriamente o nível de segurança do seu aplicativo, mas, novamente, antes de salvar informações confidenciais em qualquer lugar, você deve primeiro criptografá-las.
Suponha que um hacker possa nos atacar através da nossa rede. Assim, ele receberá senhas na forma de texto bruto. É melhor, é claro, hash todas as senhas.
Criptografia de dados pessoais
O hash pode parecer um pouco
esmagador se você fizer isso sozinho; portanto, neste artigo, usaremos a biblioteca
CryptoSwift . Ele coletou muitos algoritmos de criptografia confiáveis padrão usados no Swift.
Vamos tentar salvar e recuperar a senha do chaveiro usando os algoritmos
CryptoSwift .
func saveEncryptedPassword(_ password: String, for account: String) { let salt = Array("salty".utf8) let key = try! HKDF(password: Array(password.utf8), salt: salt, variant: .sha256).calculate().toHexString() keychainService.save(key, for: account) }
A função acima registra o nome de usuário e a senha e os salva no Keychain como uma string criptografada.
Vamos ver o que acontece por dentro:
- O login e a senha são gravados na variável salt como uma sequência
- sha256 preenche o hash SHA-2
- HKDF é uma função de geração de chave (
KDF ) baseada no código de autenticação de mensagens (
HMAC )
Criamos a variável salt para complicar a tarefa dos hackers. Nós poderíamos apenas criptografar a senha, mas, neste caso, o invasor pode ter uma lista das senhas mais usadas, ele as criptografará sem problemas e as comparará com a nossa senha criptografada. Então, encontrar a senha para uma conta específica não é difícil.
Agora podemos fazer login usando nossa conta e a chave gerada.
authManager.login(key, user)
Obviamente, o servidor deve saber o que está criptografado em nossa variável salt. O back-end poderá comparar chaves usando o mesmo algoritmo para identificar o usuário.
O uso dessa abordagem aumentará bastante a segurança do seu aplicativo.
Como conclusão
Nunca negligencie a segurança do seu aplicativo. Neste artigo, primeiro descobrimos quais poderiam ser as conseqüências ao armazenar dados confidenciais nos
UserDefaults e por que o Keychain é necessário.
Na segunda parte, falaremos sobre um nível de segurança mais sério, criptografar dados antes de salvá-los e também discutir como transferir corretamente informações com dados pessoais para o servidor.