Segurança em aplicativos iOS

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.

Source: https://habr.com/ru/post/pt430532/


All Articles