面试题答案
一键面试本地存储数据加密处理
- 使用系统加密框架:
- 在iOS开发中,可以使用
CommonCrypto
框架(在较新系统中也可使用CryptoKit
)。例如,使用CommonCrypto
中的AES(高级加密标准)算法对本地存储的身份验证相关数据进行加密。示例代码如下(以AES - 256加密为例):
#import <CommonCrypto/CommonCryptor.h> #import <CommonCrypto/CommonKeyDerivation.h> #import <Security/Security.h> #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> NSData *AESEncrypt(NSData *plainText, NSString *key) { char keyPtr[kCCKeySizeAES256 + 1]; bzero(keyPtr, sizeof(keyPtr)); [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; NSUInteger dataLength = [plainText length]; size_t bufferSize = dataLength + kCCBlockSizeAES128; void *buffer = malloc(bufferSize); size_t numBytesEncrypted = 0; CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, keyPtr, kCCKeySizeAES256, NULL /* initialization vector (optional) */, [plainText bytes], dataLength, buffer, bufferSize, &numBytesEncrypted); if (cryptStatus == kCCSuccess) { return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; } free(buffer); return nil; }
- 使用
CryptoKit
框架(iOS 13+),以AES - GCM加密为例:
#import <CryptoKit/CryptoKit.h> NSData *AESEncryptWithCryptoKit(NSData *plainText, NSString *key) { let keyData = key.data(using:.utf8)! let symetricKey = SymmetricKey(data: keyData) let sealedBox = try! AES.GCM.seal(plainText as! Data, using: symetricKey) return sealedBox.combined as NSData }
- 在iOS开发中,可以使用
- 密钥管理:
- 密钥不应硬编码在代码中。可以使用iOS的钥匙串(Keychain)来安全存储密钥。例如,使用
SecItemAdd
等函数将密钥存储到钥匙串中。
NSDictionary *keychainQuery = @{(id)kSecClass: (id)kSecClassKey, (id)kSecAttrKeyType: (id)kSecAttrKeyTypeAES, (id)kSecAttrKeySizeInBits: @256, (id)kSecValueData: keyData, (id)kSecAttrApplicationTag: @"com.example.app.key"}; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
- 读取密钥时使用
SecItemCopyMatching
函数。
NSDictionary *keychainQuery = @{(id)kSecClass: (id)kSecClassKey, (id)kSecAttrKeyType: (id)kSecAttrKeyTypeAES, (id)kSecAttrApplicationTag: @"com.example.app.key", (id)kSecReturnData: (id)kCFBooleanTrue}; CFTypeRef result = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, &result); if (status == errSecSuccess) { NSData *keyData = (__bridge_transfer NSData *)result; }
- 密钥不应硬编码在代码中。可以使用iOS的钥匙串(Keychain)来安全存储密钥。例如,使用
与服务器交互验证结果的安全流程设计
- 使用HTTPS:
- 与服务器交互验证结果时,应使用HTTPS协议。这通过TLS(传输层安全协议)对数据进行加密,防止数据在传输过程中被窃取或篡改。在iOS中,可以使用
NSURLSession
来发起HTTPS请求。
NSURL *url = [NSURL URLWithString:@"https://example.com/api/verify"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request setHTTPMethod:@"POST"]; // 设置请求体,包含验证结果等数据 [request setHTTPBody:postData]; NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 处理响应 }]; [task resume];
- 与服务器交互验证结果时,应使用HTTPS协议。这通过TLS(传输层安全协议)对数据进行加密,防止数据在传输过程中被窃取或篡改。在iOS中,可以使用
- 数据签名:
- 在客户端,使用本地私钥对验证结果数据进行签名。可以使用
Security
框架中的函数来进行签名操作。
// 从钥匙串获取私钥 NSDictionary *privateKeyQuery = @{(id)kSecClass: (id)kSecClassKey, (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA, (id)kSecAttrApplicationTag: @"com.example.app.privateKey", (id)kSecReturnRef: (id)kCFBooleanTrue}; CFTypeRef privateKeyRef = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)privateKeyQuery, &privateKeyRef); if (status == errSecSuccess) { SecKeyRef privateKey = (__bridge SecKeyRef)privateKeyRef; uint8_t signature[256]; size_t signatureLen = sizeof(signature); status = SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA256, [data bytes], (size_t)[data length], signature, &signatureLen); if (status == errSecSuccess) { NSData *signatureData = [NSData dataWithBytes:signature length:signatureLen]; // 将签名数据与验证结果一起发送到服务器 } }
- 在服务器端,使用对应的公钥对签名进行验证,确保数据未被篡改。
- 在客户端,使用本地私钥对验证结果数据进行签名。可以使用
- 令牌(Token)机制:
- 服务器在验证成功后,返回一个包含用户身份信息的令牌(如JWT - JSON Web Token)。客户端在后续与服务器的交互中,将该令牌包含在请求头中。
- 服务器每次接收到请求时,验证令牌的有效性,例如验证签名、过期时间等。这样可以减少频繁使用身份验证(Touch ID / Face ID)的次数,同时保证交互的安全性。例如,在iOS中设置请求头:
[request setValue:[NSString stringWithFormat:@"Bearer %@", token] forHTTPHeaderField:@"Authorization"];
- 防重放攻击:
- 可以在请求中添加时间戳和随机数(Nonce)。客户端生成一个随机数,并附上当前时间戳一起发送给服务器。
- 服务器记录已接收的随机数和时间戳,对于重复的随机数或超时的时间戳请求进行拒绝,防止攻击者重放之前的有效请求。例如,在客户端生成随机数和时间戳:
NSUUID *nonce = [NSUUID UUID]; NSDate *now = [NSDate date]; NSNumber *timestamp = @((int)[now timeIntervalSince1970]); // 将nonce和timestamp添加到请求体或请求头中发送到服务器
- 在服务器端验证:
# Python示例代码,验证时间戳和随机数 import time received_nonce = request.json.get('nonce') received_timestamp = request.json.get('timestamp') current_time = int(time.time()) if received_timestamp < current_time - 60 or received_nonce in used_nonces: return jsonify({'error': 'Replay attack detected'}), 403 used_nonces.add(received_nonce)