Biyometrik Kimlik Doğrulama
Biyometrik kimlik doğrulama, kullanıcı kimliğini doğrulamak için benzersiz biyolojik özellikleri kullanır. Modern mobil cihazlar parmak izi, yüz tanıma, iris tarama ve ses tanıma dahil olmak üzere çeşitli biyometrik yöntemleri destekler. Bu bölüm, güvenli biyometrik kimlik doğrulama sistemleri için kapsamlı uygulama stratejilerini içerir.
Biyometrik Kimlik Doğrulama Mimarisi
Platform Uygulamaları
iOS Face ID & Touch ID
import LocalAuthentication
import CryptoKit
class BiometricAuthenticationManager {
enum BiometricType {
case none
case touchID
case faceID
case opticID
case unknown
}
enum BiometricError: Error {
case notAvailable
case notEnrolled
case lockout
case failed
case cancelled
case fallback
case systemCancel
case appCancel
case invalidContext
case biometryNotAvailable
case biometryNotEnrolled
case biometryLockout
}
// MARK: - Biyometrik Yetenek Tespiti
func getBiometricType() -> BiometricType {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
return .none
}
if #available(iOS 17.0, *) {
switch context.biometryType {
case .touchID:
return .touchID
case .faceID:
return .faceID
case .opticID:
return .opticID
case .none:
return .none
@unknown default:
return .unknown
}
} else {
switch context.biometryType {
case .touchID:
return .touchID
case .faceID:
return .faceID
case .none:
return .none
@unknown default:
return .unknown
}
}
}
func isBiometricAvailable() -> Bool {
let context = LAContext()
var error: NSError?
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
}
func isBiometricEnrolled() -> Bool {
let context = LAContext()
var error: NSError?
let canEvaluate = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if let error = error {
return error.code != LAError.biometryNotEnrolled.rawValue
}
return canEvaluate
}
// MARK: - Temel Biyometrik Kimlik Doğrulama
func authenticateWithBiometrics(
reason: String,
fallbackTitle: String? = nil
) async throws -> Bool {
let context = LAContext()
// Bağlamı yapılandır
if let fallbackTitle = fallbackTitle {
context.localizedFallbackTitle = fallbackTitle
}
// Kullanılabilirliği kontrol et
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
throw mapLAError(error)
}
do {
let result = try await context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: reason
)
return result
} catch {
throw mapLAError(error)
}
}
func authenticateWithBiometricsOrPasscode(
reason: String
) async throws -> Bool {
let context = LAContext()
do {
let result = try await context.evaluatePolicy(
.deviceOwnerAuthentication,
localizedReason: reason
)
return result
} catch {
throw mapLAError(error)
}
}
// MARK: - Kriptografik Biyometrik Kimlik Doğrulama
func authenticateWithCryptographicKey(
keyTag: String,
reason: String
) async throws -> SecKey {
let context = LAContext()
// Anahtarı sorgula
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: keyTag,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecReturnRef as String: true,
kSecUseAuthenticationContext as String: context
]
var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess else {
throw BiometricError.failed
}
guard let secKey = result as! SecKey? else {
throw BiometricError.failed
}
// Anahtarı açmak için kimlik doğrulama yap
do {
_ = try await context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: reason
)
return secKey
} catch {
throw mapLAError(error)
}
}
func createBiometricProtectedKey(
keyTag: String,
requireBiometry: Bool = true
) throws -> SecKey {
let flags: SecAccessControlCreateFlags = requireBiometry
? [.privateKeyUsage, .biometryAny]
: [.privateKeyUsage, .devicePasscode]
guard let access = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
flags,
nil
) else {
throw BiometricError.failed
}
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: keyTag,
kSecAttrAccessControl as String: access
]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw BiometricError.failed
}
return privateKey
}
// MARK: - Gelişmiş Özellikler
func authenticateWithCustomUI(
reason: String,
onSuccess: @escaping () -> Void,
onError: @escaping (BiometricError) -> Void,
onFallback: @escaping () -> Void
) {
let context = LAContext()
context.localizedFallbackTitle = "Şifre Kullan"
context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: reason
) { success, error in
DispatchQueue.main.async {
if success {
onSuccess()
} else if let error = error as? LAError {
switch error.code {
case .userFallback:
onFallback()
default:
onError(self.mapLAError(error))
}
} else {
onError(.failed)
}
}
}
}
func invalidateBiometricContext() {
// Yeni kimlik doğrulama gerektirmek için bağlamı geçersiz kıl
let context = LAContext()
context.invalidate()
}
// MARK: - Hata Eşleştirme
private func mapLAError(_ error: Error?) -> BiometricError {
guard let laError = error as? LAError else {
return .failed
}
switch laError.code {
case .authenticationFailed:
return .failed
case .userCancel:
return .cancelled
case .userFallback:
return .fallback
case .systemCancel:
return .systemCancel
case .appCancel:
return .appCancel
case .invalidContext:
return .invalidContext
case .biometryNotAvailable:
return .biometryNotAvailable
case .biometryNotEnrolled:
return .biometryNotEnrolled
case .biometryLockout:
return .biometryLockout
case .passcodeNotSet:
return .notAvailable
default:
return .failed
}
}
}
Android Biyometrik Kimlik Doğrulama
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.fragment.app.FragmentActivity
import androidx.core.content.ContextCompat
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import java.security.KeyStore
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
class BiometricAuthenticationManager(private val activity: FragmentActivity) {
enum class BiometricCapability {
BIOMETRIC_SUCCESS,
BIOMETRIC_ERROR_HW_UNAVAILABLE,
BIOMETRIC_ERROR_NO_HARDWARE,
BIOMETRIC_ERROR_NONE_ENROLLED,
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
BIOMETRIC_ERROR_UNSUPPORTED,
BIOMETRIC_STATUS_UNKNOWN
}
enum class AuthenticatorType {
BIOMETRIC_STRONG,
BIOMETRIC_WEAK,
DEVICE_CREDENTIAL,
BIOMETRIC_STRONG_OR_DEVICE_CREDENTIAL,
BIOMETRIC_WEAK_OR_DEVICE_CREDENTIAL
}
// MARK: - Yetenek Tespiti
fun getBiometricCapability(authenticatorType: AuthenticatorType = AuthenticatorType.BIOMETRIC_STRONG): BiometricCapability {
val biometricManager = BiometricManager.from(activity)
val authenticators = when (authenticatorType) {
AuthenticatorType.BIOMETRIC_STRONG -> BiometricManager.Authenticators.BIOMETRIC_STRONG
AuthenticatorType.BIOMETRIC_WEAK -> BiometricManager.Authenticators.BIOMETRIC_WEAK
AuthenticatorType.DEVICE_CREDENTIAL -> BiometricManager.Authenticators.DEVICE_CREDENTIAL
AuthenticatorType.BIOMETRIC_STRONG_OR_DEVICE_CREDENTIAL ->
BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL
AuthenticatorType.BIOMETRIC_WEAK_OR_DEVICE_CREDENTIAL ->
BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL
}
return when (biometricManager.canAuthenticate(authenticators)) {
BiometricManager.BIOMETRIC_SUCCESS -> BiometricCapability.BIOMETRIC_SUCCESS
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> BiometricCapability.BIOMETRIC_ERROR_HW_UNAVAILABLE
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> BiometricCapability.BIOMETRIC_ERROR_NO_HARDWARE
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> BiometricCapability.BIOMETRIC_ERROR_NONE_ENROLLED
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> BiometricCapability.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> BiometricCapability.BIOMETRIC_ERROR_UNSUPPORTED
else -> BiometricCapability.BIOMETRIC_STATUS_UNKNOWN
}
}
fun isBiometricAvailable(): Boolean {
return getBiometricCapability() == BiometricCapability.BIOMETRIC_SUCCESS
}
// MARK: - Temel Kimlik Doğrulama
fun authenticateWithBiometrics(
title: String,
subtitle: String,
description: String? = null,
negativeButtonText: String = "İptal",
onSuccess: () -> Unit,
onError: (String) -> Unit,
onFailed: () -> Unit
) {
val executor = ContextCompat.getMainExecutor(activity)
val biometricPrompt = BiometricPrompt(activity, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
onError(errString.toString())
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
onSuccess()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onFailed()
}
}
)
val promptInfoBuilder = BiometricPrompt.PromptInfo.Builder()
.setTitle(title)
.setSubtitle(subtitle)
.setNegativeButtonText(negativeButtonText)
if (!description.isNullOrEmpty()) {
promptInfoBuilder.setDescription(description)
}
val promptInfo = promptInfoBuilder.build()
biometricPrompt.authenticate(promptInfo)
}
fun authenticateWithBiometricsOrDeviceCredential(
title: String,
subtitle: String,
description: String? = null,
onSuccess: () -> Unit,
onError: (String) -> Unit,
onFailed: () -> Unit
) {
val executor = ContextCompat.getMainExecutor(activity)
val biometricPrompt = BiometricPrompt(activity, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
onError(errString.toString())
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
onSuccess()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onFailed()
}
}
)
val promptInfoBuilder = BiometricPrompt.PromptInfo.Builder()
.setTitle(title)
.setSubtitle(subtitle)
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG or
BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
if (!description.isNullOrEmpty()) {
promptInfoBuilder.setDescription(description)
}
val promptInfo = promptInfoBuilder.build()
biometricPrompt.authenticate(promptInfo)
}
// MARK: - Kriptografik İşlemler
fun createBiometricKey(keyAlias: String, requireUserAuthentication: Boolean = true): Boolean {
return try {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val keyGenParameterSpecBuilder = KeyGenParameterSpec.Builder(
keyAlias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(requireUserAuthentication)
.setInvalidatedByBiometricEnrollment(true)
if (requireUserAuthentication) {
keyGenParameterSpecBuilder.setUserAuthenticationValidityDurationSeconds(300) // 5 dakika
}
keyGenerator.init(keyGenParameterSpecBuilder.build())
keyGenerator.generateKey()
true
} catch (e: Exception) {
false
}
}
fun getCipher(keyAlias: String, mode: Int): Cipher? {
return try {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val secretKey = keyStore.getKey(keyAlias, null)
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher.init(mode, secretKey)
cipher
} catch (e: Exception) {
null
}
}
fun authenticateWithCryptography(
cipher: Cipher,
title: String,
subtitle: String,
description: String? = null,
negativeButtonText: String = "İptal",
onSuccess: (BiometricPrompt.CryptoObject) -> Unit,
onError: (String) -> Unit,
onFailed: () -> Unit
) {
val executor = ContextCompat.getMainExecutor(activity)
val biometricPrompt = BiometricPrompt(activity, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
onError(errString.toString())
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
result.cryptoObject?.let { onSuccess(it) }
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onFailed()
}
}
)
val promptInfoBuilder = BiometricPrompt.PromptInfo.Builder()
.setTitle(title)
.setSubtitle(subtitle)
.setNegativeButtonText(negativeButtonText)
if (!description.isNullOrEmpty()) {
promptInfoBuilder.setDescription(description)
}
val promptInfo = promptInfoBuilder.build()
val cryptoObject = BiometricPrompt.CryptoObject(cipher)
biometricPrompt.authenticate(promptInfo, cryptoObject)
}
// MARK: - Gelişmiş Özellikler
fun authenticateWithCustomTimeout(
title: String,
subtitle: String,
timeoutSeconds: Int,
onSuccess: () -> Unit,
onError: (String) -> Unit,
onTimeout: () -> Unit
) {
val executor = ContextCompat.getMainExecutor(activity)
var authenticationCompleted = false
val biometricPrompt = BiometricPrompt(activity, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (!authenticationCompleted) {
authenticationCompleted = true
onError(errString.toString())
}
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
if (!authenticationCompleted) {
authenticationCompleted = true
onSuccess()
}
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
// Başarısız denemeler için tamamlandı olarak işaretleme
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(title)
.setSubtitle(subtitle)
.setNegativeButtonText("İptal")
.build()
biometricPrompt.authenticate(promptInfo)
// Zaman aşımı ayarla
ContextCompat.getMainExecutor(activity).execute {
android.os.Handler().postDelayed({
if (!authenticationCompleted) {
authenticationCompleted = true
biometricPrompt.cancelAuthentication()
onTimeout()
}
}, timeoutSeconds * 1000L)
}
}
fun removeBiometricKey(keyAlias: String): Boolean {
return try {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
keyStore.deleteEntry(keyAlias)
true
} catch (e: Exception) {
false
}
}
}
Güvenlik Hususları
Sahte Biyometrik Önlemleri
// Android Sahte Biyometrik Önleme Uygulaması
class BiometricAntiSpoofing {
fun enableLivenessDetection(): Boolean {
// Sadece güçlü biyometrik doğrulayıcıları kullan
val biometricManager = BiometricManager.from(context)
return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) ==
BiometricManager.BIOMETRIC_SUCCESS
}
fun validateBiometricQuality(result: BiometricPrompt.AuthenticationResult): BiometricQuality {
// Bu kavramsal bir uygulamadır
// Gerçek uygulamalar platform özel API'leri gerektirir
val cryptoObject = result.cryptoObject
if (cryptoObject != null) {
// Kriptografik kimlik doğrulama daha yüksek güvence sağlar
return BiometricQuality.HIGH
}
// Kimlik doğrulama gücünü kontrol et
return when {
isStrongBiometric() -> BiometricQuality.MEDIUM
else -> BiometricQuality.LOW
}
}
private fun isStrongBiometric(): Boolean {
// Uygulama, kullanılan biyometriğin
// güçlü kimlik doğrulama gereksinimlerini karşılayıp karşılamadığını kontrol eder
return true
}
enum class BiometricQuality {
LOW,
MEDIUM,
HIGH
}
}
Biyometrik Şablon Koruması
// iOS Biyometrik Şablon Güvenliği
class BiometricTemplateProtection {
func createSecureKey(withBiometricProtection: Bool) throws -> SecKey {
let flags: SecAccessControlCreateFlags = withBiometricProtection
? [.privateKeyUsage, .biometryAny, .invalidateWhenBiometricsChange]
: [.privateKeyUsage, .devicePasscode]
guard let access = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
flags,
nil
) else {
throw BiometricError.failed
}
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "biometric.key",
kSecAttrAccessControl as String: access
]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw BiometricError.failed
}
return privateKey
}
func invalidateKeysOnBiometricChange() {
// .invalidateWhenBiometricsChange bayrağına sahip anahtarlar
// biyometrik kayıt değiştiğinde otomatik olarak geçersiz kılınır
// Bu, sahte biyometrik veriler kullanılarak yapılan saldırıları önler
}
}
En İyi Uygulamalar
Biyometrik Kimlik Doğrulama Kontrol Listesi
[ ] Kullanılabilirlik Kontrolü
- Biyometrik donanım kullanılabilirliğini kontrol et
- Biyometrik kayıt durumunu doğrula
- Uygun yedek mekanizmaları sağla
- Cihaza özgü sınırlamaları yönet
[ ] Güvenlik Uygulaması
- Sadece platform tarafından sağlanan biyometrik API'lerini kullan
- Mümkün olduğunda kriptografik kimlik doğrulama uygula
- Biyometrik değişikliklerde anahtar geçersiz kılmayı etkinleştir
- Hassas anahtarlar için donanım destekli depolama kullan
[ ] Kullanıcı Deneyimi
- Net kimlik doğrulama istemleri sağla
- Zarif hata işleme uygula
- Alternatif kimlik doğrulama yöntemleri sun
- Kullanıcı tercihlerini ve seçimlerini saygı göster
[ ] Gizlilik ve Uyumluluk
- Biyometrik şablonları asla saklama veya iletme
- Uygun onay mekanizmaları uygula
- Platform gizlilik yönergelerini takip et
- Bölgesel biyometrik yasalara uy
[ ] Hata İşleme
- Tüm olası hata senaryolarını yönet
- Anlamlı hata mesajları sağla
- Sınırlı yeniden deneme mekanizmaları uygula
- Güvenlik olaylarını uygun şekilde kaydet
[ ] Test ve Doğrulama
- Birden fazla cihaz türünde test et
- Sahte biyometrik önlemleri doğrula
- Yedek senaryoları test et
- Güvenlik penetrasyon testleri gerçekleştir
Uygulama Yönergeleri
- Platform API'lerini asla atlama: Her zaman resmi platform biyometrik API'lerini kullan
- Derinlemesine savunma uygula: Biyometrik kimlik doğrulamayı diğer güvenlik önlemleriyle birleştir
- Başarısızlık için planla: Her zaman yedek kimlik doğrulama yöntemleri sağla
- Kullanıcı seçimine saygı göster: Kullanıcıların biyometrik kimlik doğrulamayı devre dışı bırakmasına izin ver
- Güncel kal: Platform güvenlik güncellemelerini ve en iyi uygulamaları takip et
Biyometrik kimlik doğrulama, güvenlik ve kullanıcı kolaylığı arasında güçlü bir denge sağlar. Platform tarafından sağlanan API'ler kullanılarak ve güvenlik en iyi uygulamaları takip edilerek doğru şekilde uygulandığında, mükemmel kullanıcı deneyimini korurken güçlü koruma sağlar. Anahtar, platform yeteneklerini anlamak, uygun yedekleri uygulamak ve kimlik doğrulama yaşam döngüsü boyunca güvenliği sürdürmektir.