Engenharia Estrutural Civil &
Implementação de armazenamento seguro de dados com o Keychain em Ios
Table of Contents
Introdução ao Armazenamento de Dados Seguros no iOS
Proteger dados sensíveis do usuário é uma responsabilidade fundamental de qualquer aplicativo iOS. Se você está armazenando tokens de autenticação, chaves de criptografia ou credenciais privadas, a plataforma fornece uma solução dedicada com suporte de hardware: o Keychain. Ao contrário de ou arquivos de lista de propriedades, o Keychain criptografa dados em repouso e impõe controles de acesso rigorosos. Este artigo fornece um guia abrangente para implementar armazenamento seguro de dados com o Keychain, cobrindo tanto o framework de segurança nativo quanto as melhores práticas práticas para aplicativos de produção.
Compreender o Chaveiro iOS
O Keychain é um recipiente de armazenamento seguro gerenciado pelo sistema operacional. Ele armazena itens pequenos e sensíveis, como senhas, chaves criptográficas ou certificados, em um banco de dados criptografado. Os dados escritos no Keychain são protegidos mesmo quando o dispositivo está bloqueado. As principais capacidades incluem:
- Encriptação em repouso usando AES-256 com suporte de hardware.
- Controlo de acesso via código de acesso do dispositivo, Touch ID ou Face ID.
- Persistência através de reinstalações de aplicativos (se configurado) e sincronização opcional do iCloud.
- Isolação entre aplicativos: por padrão, um aplicativo não pode ler os itens Keychain de outro aplicativo, a menos que eles compartilhem um grupo de acesso Keychain.
O Keychain não foi desenhado para grandes bolhas; mantenha cada item abaixo de alguns kilobytes. Para dados maiores, considere usar a API ou o framework juntamente com criptografia baseada em arquivos.
API de Serviços de Chaveiro vs Bibliotecas de Terceiros
A Apple fornece a API nativa Keychain Services] (baseada em C, , que é poderosa, mas verbosa. Você pode usá-la diretamente ou adotar um envoltório amigável Swift. Bibliotecas populares de terceiros como KeychainAccess[ ou SwiftKeychainWrapper[] reduzem a placa de caldeira. No entanto, entender a API subjacente é essencial para depuração e quando você precisa de controle sobre as políticas de acesso com grãos finos. Hoje, vamos focar na API nativa com Swift.
Configurar o Armazenamento de Chaves
Antes de guardar qualquer coisa, você deve decidir sobre a classe de item Keychain. A senha genérica mais comum é . Para as senhas ou certificados da Internet, existem outras classes. Cada item é referenciado por um conjunto de atributos – um dicionário (CFDictionary) que descreve o item.
O fluxo básico segue sempre este padrão:
- Compilar um dicionário de pesquisa com a classe de item e atributos.
- Chamar a função apropriada [, , , ].
- Verifique o retornado (] ou um código de erro).
Antes de escrever o código, importe o módulo de segurança:
import Security
import Foundation // for Data and String utilities
Armazenar Dados no Chaveiro
A escrever uma Senha Genérica
Para salvar um token (por exemplo, um JWT) para o usuário atual:
func saveToken(_ token: String, forAccount account: String) -> Bool {
guard let tokenData = token.data(using: .utf8) else { return false }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: tokenData,
// Optional: restrict access to when device is unlocked
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
// Delete any existing item first to avoid duplicates
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
Pontos-chave:
- atua como uma chave primária; escolha uma string única (por exemplo, o ID do usuário ou uma constante como ).
- controla quando o item pode ser lido. Use para melhor segurança; impede o backup do iCloud e restringe o acesso ao dispositivo atual.
- Chamamos antes de adicionar para evitar acumular itens duplicados. Alternativamente, você pode usar .
Adicionando Controle de Acesso (Biometria ou Código de Passagem)
Para dados altamente sensíveis, requeira Touch ID ou Face ID antes de ler:
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.userPresence, // requires passcode, Face ID, or Touch ID
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: tokenData,
kSecAttrAccessControl as String: accessControl as Any
]
SecItemAdd(query as CFDictionary, nil)
Agora qualquer chamada para este item irá desencadear uma prompt biométrica ou código de acesso. Use da LocalAutentication para lidar com a interação do usuário graciosamente.
Obtendo Dados do Chaveiro
Para ler o token armazenado:
func retrieveToken(forAccount account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess,
let data = item as? Data,
let token = String(data: data, encoding: .utf8) else {
return nil
}
return token
}
Defina para para recuperar os dados. Use para recuperar um único resultado. Se você omitir o limite, a API pode retornar um array.
Importante: Ao usar o controle de acesso (biometria), a chamada pode retornar se o usuário cancelar. Lide com este caso separadamente e nunca volte ao armazenamento de texto simples.
Atualizando e excluindo itens de chaveiro
Atualizando um item existente
Em vez de apagar e reiniciar, utilizar :
func updateToken(_ newToken: String, forAccount account: String) -> Bool {
guard let newData = newToken.data(using: .utf8) else { return false }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account
]
let attributesToUpdate: [String: Any] = [
kSecValueData as String: newData
]
let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
return status == errSecSuccess
}
Isto é mais eficiente do que um add delete+, e evita condições de corrida potenciais.
Excluindo um item
func deleteItem(forAccount account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
Tenha cuidado para não excluir itens que pertencem a outros aplicativos que compartilham o mesmo grupo de acesso – sempre explore sua consulta com se você usar Keychains compartilhados.
Atributos de Controle de Acesso e Acessibilidade
A constante define quando o item Keychain pode ser lido. Escolha a opção mais restritiva que ainda atende às necessidades do seu aplicativo:
| Attribute | Meaning |
|---|---|
kSecAttrAccessibleWhenUnlocked | Available only while device is unlocked (default). |
kSecAttrAccessibleAfterFirstUnlock | Available after device boots and is unlocked once. Allows background access. |
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly | Requires a passcode to be set. Strictest option—prevents access even after unlock if passcode is removed. |
kSecAttrAccessibleWhenUnlockedThisDeviceOnly | Same as WhenUnlocked but does not back up to iCloud, and cannot be restored to another device. |
Para a maioria dos aplicativos, atinge o equilíbrio certo entre segurança e usabilidade. Se você precisa ler itens em segundo plano (por exemplo, um token de atualização de fundo), você deve usar ] (e aceitar que os dados são ligeiramente menos protegidos).
Tratamento de Erros e Agrupamentos Comuns
As funções devolvem um . Verifique sempre e trate sempre as falhas de forma adequada. Erros comuns:
- (–25300) – Nenhum item corresponde à consulta.
- (–25299) – Já existe um item com a mesma chave primária (se não tiver apagado primeiro).
- (-128) – O utilizador cancelou o prompt biométrico.
- (–25293) – A autenticação falhou ou não está disponível biometria.
Nunca ignore um estado de não sucesso. Graciosamente degradar: mostrar uma mensagem de erro ou tentar de novo, mas nunca armazenar dados confidenciais fora do Keychain como um retorno. Você pode usar para verificar a disponibilidade biométrica antes de tentar acessar.
Melhores Práticas e Considerações sobre Produção
- Use nomes de contas descritivos únicos por usuário ou por tipo de item para evitar colisões.
- Sempre especifique um atributo de acessibilidade; caso contrário, o padrão do sistema () se aplica, o que pode não ser ideal.
- Limpar dados do Keychain quando o usuário sair—iterar sobre todas as contas conhecidas e excluir itens.
- Use Grupos de Acesso de Chaves somente quando compartilhar entre seus próprios aplicativos. Evite grupos amplos.
- Nunca guarde dados não sensíveis (como preferências de usuário) no Keychain — use ou em vez disso um banco de dados.
- Considere a utilização de com para cenários avançados (macOS Catalyst).
- Teste em um dispositivo real; o Simulator usa um software Keychain que se comporta de forma diferente do armazenamento apoiado por hardware.
Usando chaveiro com SwiftUI e Async/Await
Para aplicações modernas, enrole operações Keychain em um ator ou uma classe assync-safe para evitar bloquear o tópico principal. Exemplo usando :
actor KeychainManager {
func saveToken(_ token: String, for account: String) async -> Bool {
// same implementation as above, but now it's safe to call from any context
return saveToken(token, forAccount: account)
}
}
Se você usar biometria, a chamada pode bloquear o thread enquanto aguarda a interação do usuário. Embrulhe-o em uma fila de fundo, ou melhor, use o método antes da chamada Keychain.
Conclusão
The iOS Keychain is the correct place to store small, sensitive pieces of data. By using the native Keychain Services API, you gain direct control over encryption, accessibility, and authentication policies. Always pair your Keychain usage with solid error handling and remember to clear data when appropriate. For further reading, refer to the Apple Keychain Service Documentation and the Keychain Concepts overview. Adopting these practices will help you ship iOS apps that respect user privacy and withstand security scrutiny.