Computer >> Hướng Dẫn Máy Tính >  >> Điện Thoại Thông Minh >> iPhone

Phát triển iOS an toàn trong Swift:Tránh những cạm bẫy thường gặp và củng cố ứng dụng của bạn

Phát triển iOS an toàn trong Swift:Tránh những cạm bẫy thường gặp và củng cố ứng dụng của bạn

Ngày nay, có nhiều cách kẻ tấn công có thể cố gắng xâm phạm ứng dụng của bạn. Và nhờ các cuộc tấn công mạng ngày càng gia tăng, nhu cầu về các ứng dụng di động an toàn – và nói rộng ra là mã hóa an toàn – chưa bao giờ cao hơn.

Vì vậy, nếu là nhà phát triển iOS, bạn cũng nên học cách ưu tiên bảo mật ở mọi giai đoạn phát triển ứng dụng.

Swift, ngôn ngữ lập trình hiện đại của Apple, cung cấp nhiều công cụ và khung giúp đơn giản hóa việc phát triển đồng thời tăng cường bảo mật nhưng chỉ khi được sử dụng đúng cách.

Bài viết này khám phá 10 cạm bẫy bảo mật phổ biến trong các ứng dụng iOS dựa trên Swift và đưa ra các chiến lược thiết thực để giảm thiểu chúng.

Điều kiện tiên quyết

Trước khi bắt đầu, bạn sẽ cần:

  • Kiến thức làm việc về phát triển Swift và iOS.

  • Truy cập vào Xcode.

  • Hiểu biết cơ bản về cách ứng dụng iOS giao tiếp với máy chủ.

  • Làm quen với kiến thức cơ bản về Terminal/dòng lệnh.

Các ví dụ về mã rất thực tế và được giải thích từng bước, giúp các nhà phát triển cấp dưới có thể tiếp cận chúng trong khi vẫn mang lại giá trị cho những người có kinh nghiệm đang tìm cách tăng cường bảo mật cho ứng dụng của họ.

Nội dung chúng tôi sẽ đề cập:

  • Bẫy bảo mật phổ biến nhất trong ứng dụng Swift iOS là gì?
    • 1. Lưu trữ dữ liệu không an toàn
    • 2. Giao tiếp mạng yếu
    • 3. Xác thực đầu vào không đúng
    • 4. Bí mật mã hóa cứng
    • 5. Xác thực và ủy quyền không đầy đủ
    • 6. Ghi nhật ký và xử lý lỗi không an toàn
    • 7. Bỏ qua việc làm xáo trộn mã và kỹ thuật đảo ngược
    • 8. Thư viện của bên thứ ba không an toàn
    • 9. Xác thực sinh trắc học và đa yếu tố không đầy đủ
    • 10. Bỏ qua việc kiểm tra bảo mật định kỳ

Bẫy bảo mật phổ biến nhất trong ứng dụng Swift iOS là gì?

Swift và iOS cung cấp các tính năng bảo mật mạnh mẽ nhưng vẫn xảy ra lỗi. Sau đây là những bẫy phổ biến nhất và cách khắc phục:

1. Lưu trữ dữ liệu không an toàn

Một trong những lỗi phổ biến nhất mà các nhà phát triển mắc phải là lưu trữ dữ liệu nhạy cảm không an toàn. Mật khẩu, mã thông báo hoặc thậm chí dữ liệu người dùng cá nhân có thể vô tình bị để lại trong UserDefaults hoặc bộ nhớ cục bộ ở dạng không được mã hóa.

Mặc dù UserDefaults thuận tiện cho lượng dữ liệu nhỏ nhưng nó không an toàn cho dữ liệu nhạy cảm vì kẻ tấn công có thể dễ dàng truy cập nếu thiết bị bị xâm phạm.

Cách khắc phục:

Sử dụng API dịch vụ chuỗi khóa để lưu trữ dữ liệu nhạy cảm một cách an toàn. Chuỗi khóa mã hóa dữ liệu và liên kết dữ liệu đó với thiết bị để các ứng dụng hoặc người dùng trái phép khác không thể truy cập được.

Bạn có thể lưu trữ thông tin xác thực một cách an toàn bằng cách sử dụng các thư viện trong Swift chẳng hạn như Móc khóaAccess hoặc các hàm SecItemAdd và SecItemCopyMatching tích hợp sẵn.

Ví dụ:đây là cách bạn có thể lưu trữ mật khẩu người dùng trong Chuỗi khóa để đảm bảo dữ liệu nhạy cảm được lưu trữ an toàn:

do {
try keychain.set("userPassword123", key: "userPassword")
} catch {
 print("Error saving to Keychain: \(error)")
}

Đằng sau hậu trường, đây là những gì xảy ra khi bạn gọi keychain.set("userPassword123", key: "userPassword") bên trong lớp Chuỗi khóa sử dụng khung Bảo mật gốc của Apple để lưu trữ:

import Security
class KeychainManager {
 func set(_ value: String, key: String) throws {
 // 1. Convert string to Data
 guard let data = value.data(using: .utf8) else {
 throw NSError(domain: "KeychainManager", code: -1)
 }
 // 2. Build the query dictionary
 let query: [String: Any] = [
 // Store as password
 kSecClass as String: kSecClassGenericPassword,
 // Your app's bundle identifier
 kSecAttrService as String: "com.yourapp.keychain",
 // "userPassword"
 kSecAttrAccount as String: key,
 // "userPassword123" encrypted
 kSecValueData as String: data,
 kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
 ]
 // 3. Save to keychain (iOS encrypts it automatically)
 let status = SecItemAdd(query as CFDictionary, nil)
 // 4. Check if successful
 guard status == errSecSuccess else {
 throw NSError(domain: "KeychainManager", code: Int(status))
 }
 }
}

Khi chức năng này chạy, iOS sẽ chuyển đổi giá trị chuỗi, chẳng hạn như "userPassword123", thành dữ liệu nhị phân được mã hóa và lưu trữ an toàn trong cơ sở dữ liệu Chuỗi khóa của thiết bị. Mục nhập được lưu dưới khóa được cung cấp (ví dụ:"userPassword") và chỉ ứng dụng của bạn mới có thể truy cập vào mục đó.

Đằng sau, Chuỗi khóa tận dụng các tính năng bảo mật mạnh mẽ, bao gồm mã hóa dựa trên phần cứng bằng các khóa dành riêng cho thiết bị, bảo vệ sinh trắc học tùy chọn thông qua Face ID hoặc Touch ID và cách ly cấp ứng dụng để đảm bảo rằng không ứng dụng nào khác có thể đọc hoặc sửa đổi thông tin xác thực được lưu trữ của bạn.

2. Giao tiếp mạng yếu

Truyền dữ liệu nhạy cảm qua mạng là một lĩnh vực khác dễ bị tổn thương. Việc sử dụng kết nối HTTP không được mã hóa sẽ khiến ứng dụng của bạn gặp phải các cuộc tấn công trung gian (MITM), cho phép kẻ tấn công chặn và sửa đổi dữ liệu trong quá trình truyền.

Vấn đề :Khi dữ liệu di chuyển giữa ứng dụng và máy chủ của bạn qua kết nối không an toàn, những kẻ tấn công trên cùng một mạng (như Wi-Fi công cộng) có thể:

  • Đọc thông tin nhạy cảm (mật khẩu, dữ liệu cá nhân, chi tiết thanh toán)

  • Sửa đổi yêu cầu và phản hồi

  • Mạo danh máy chủ hợp pháp của bạn

Cách khắc phục:

1. Luôn sử dụng HTTPS
HTTPS mã hóa tất cả dữ liệu trong quá trình truyền, khiến kẻ tấn công không thể đọc được. App Transport Security (ATS) của iOS thực thi điều này bằng cách chặn các kết nối HTTP không an toàn theo mặc định:

// ❌ INSECURE - HTTP connection (blocked by ATS by default)
let url = URL(string: "http://api.example.com/login")
// ✅ SECURE - HTTPS connection
let url = URL(string: "https://api.example.com/login")

Bạn sẽ muốn tránh thêm ngoại lệ ATS vào Info.plist của mình trừ khi thực sự cần thiết. Nếu API của bên thứ ba chỉ hỗ trợ HTTP, hãy liên hệ với họ để nâng cấp hoặc tìm giải pháp thay thế an toàn hơn.

2. Triển khai Ghim chứng chỉ (Bảo vệ nâng cao)

Ngay cả với HTTPS, ứng dụng của bạn vẫn có thể dễ bị tấn công MITM tinh vi. Ví dụ:kẻ tấn công có thể cài đặt chứng chỉ gian lận trên thiết bị của người dùng (thông qua phần mềm độc hại hoặc kỹ thuật xã hội) và chặn lưu lượng HTTPS có vẻ hợp lệ. Chứng chỉ giả của kẻ tấn công sẽ được thiết bị tin cậy, cho phép chúng giải mã và đọc thông tin liên lạc "an toàn".

Ghim chứng chỉ giải quyết vấn đề này bằng cách làm cho ứng dụng của bạn chỉ tin cậy chứng chỉ của máy chủ cụ thể, từ chối tất cả các chứng chỉ khác – ngay cả khi chúng hợp lệ.

Cách ghim chứng chỉ hoạt động:

Ứng dụng của bạn lưu trữ chứng chỉ dự kiến (hoặc hàm băm khóa công khai của nó) và xác thực chứng chỉ đó trong mỗi lần kết nối:

class SecureNetworkManager: NSObject, URLSessionDelegate {
 // Store your server's certificate hash
 // Get this by running: openssl x509 -in certificate.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
 private let expectedPublicKeyHash = "YOUR_CERTIFICATE_HASH_HERE"
 func urlSession(
 _ session: URLSession,
 didReceive challenge: URLAuthenticationChallenge,
 completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
 ) {
 // Step 1: Check if this is a server trust challenge (certificate validation)
 guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
 let serverTrust = challenge.protectionSpace.serverTrust
 else {
 // Not a certificate challenge; use default handling
 completionHandler(.performDefaultHandling, nil)
 return
 }
 // Step 2: Validate that the server's certificate matches our pinned certificate
 if isValidServerTrust(serverTrust) {
 // Certificate matches - proceed with the connection
 completionHandler(.useCredential, URLCredential(trust: serverTrust))
 } else {
 // Certificate doesn't match - reject the connection to prevent MITM attack
 completionHandler(.cancelAuthenticationChallenge, nil)
 }
 }
 // Validates the server's certificate against our pinned hash
 private func isValidServerTrust(_ serverTrust: SecTrust) -> Bool {
 // Extract the server's certificate
 guard let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
 return false
 }
 // Get the public key from the certificate
 let serverPublicKey = SecCertificateCopyKey(serverCertificate)
 guard let publicKey = serverPublicKey else {
 return false
 }
 // Convert the public key to data and hash it
 var error: Unmanaged<CFError>?
 guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
 return false
 }
 // Hash the public key using SHA-256
 let publicKeyHash = SHA256.hash(data: publicKeyData)
 let publicKeyHashString = Data(publicKeyHash).base64EncodedString()
 // Compare with our expected hash
 return publicKeyHashString == expectedPublicKeyHash
 }
}

Mã này làm gì, từng bước một:

  1. urlSession(_:didReceive:completionHandler:) – Phương thức này được gọi bất cứ khi nào ứng dụng của bạn tạo kết nối HTTPS. iOS hỏi:"Tôi có nên tin cậy chứng chỉ của máy chủ này không?"

  2. Kiểm tra phương thức xác thực – Chúng tôi xác minh đây là thử thách tin cậy của máy chủ (xác thực chứng chỉ), không phải một số loại xác thực khác.

  3. Xác thực chứng chỉ – Chúng tôi gọi isValidServerTrust() , trong đó:

    • Trích xuất chứng chỉ của máy chủ từ kết nối

    • Lấy khóa công khai từ chứng chỉ đó

    • Băm khóa chung bằng SHA-256

    • So sánh hàm băm với hàm băm dự kiến được lưu trữ của chúng tôi

  1. Đưa ra quyết định:
  • Nếu giá trị băm khớp thì máy chủ hợp pháp. Tiếp tục với .useCredential .

  • Nếu giá trị băm không khớp, chúng ta có nguy cơ bị tấn công MITM. Hủy bằng .cancelAuthenticationChallenge .

Vậy làm cách nào để ngăn chặn các cuộc tấn công MITM? Ngay cả khi kẻ tấn công cài đặt chứng chỉ gian lận trên thiết bị của người dùng và chặn lưu lượng truy cập, hàm băm của chứng chỉ của họ sẽ không khớp với hàm băm được ghim của bạn. Ứng dụng của bạn sẽ từ chối kết nối, ngăn kẻ tấn công giải mã lưu lượng truy cập của bạn.

3. Bảo vệ bổ sung:Đề xuất VPN trên Wi-Fi công cộng

Bạn cũng có thể khuyến nghị người dùng kết nối qua VPN khi sử dụng Wi-Fi công cộng để tăng thêm lớp bảo mật và cung cấp hướng dẫn về cách sử dụng VPN hiệu quả để giữ an toàn cho dữ liệu của họ.

Đối với các nhà phát triển, việc duy trì mức độ bảo mật ứng dụng mạnh mẽ cũng phụ thuộc vào việc có một hệ thống được tối ưu hóa tốt, việc học cách tăng tốc máy Mac chạy chậm có thể giúp đảm bảo các bản dựng mượt mà hơn, thử nghiệm nhanh hơn và quy trình phát triển tổng thể an toàn hơn.

3. Xác thực đầu vào không đúng

Một số nhà phát triển bỏ qua việc xác thực đầu vào chính xác, dẫn đến một số lỗ hổng bao gồm SQL SQL, thực thi mã từ xa và hỏng dữ liệu.

Mặc dù Swift có hỗ trợ gõ mạnh mẽ nhưng một số nhà phát triển không loại bỏ phản hồi đầu vào hoặc API của người dùng. Việc kết hợp xác thực email API theo thời gian thực đảm bảo rằng người dùng cung cấp địa chỉ email hợp pháp, được định dạng đúng trước khi chúng được lưu trữ hoặc xử lý, giảm cả rủi ro bảo mật và các vấn đề về chất lượng dữ liệu.

Cách khắc phục:

Xác thực đầu vào là tuyến phòng thủ đầu tiên của bạn chống lại dữ liệu độc hại. Đây là cách bảo vệ ứng dụng iOS của bạn:

1. Xác thực đầu vào của người dùng bằng mẫu

Luôn xác thực dữ liệu đầu vào của người dùng bằng cách sử dụng biểu thức thông thường hoặc mẫu được xác định trước trước khi xử lý. Ví dụ:khi chấp nhận địa chỉ email:

func isValidEmail(_ email: String) -> Bool {
 let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
 let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
 return emailPredicate.evaluate(with: email)
}
// Only accept properly formatted data
guard isValidEmail(userEmail) else {
 // Reject invalid input
 return
}

Điều này đảm bảo rằng chỉ những dữ liệu được định dạng đúng mới được chấp nhận, ngăn chặn dữ liệu đầu vào không đúng định dạng hoặc độc hại xâm nhập vào hệ thống của bạn.

2. Dọn dẹp các phản hồi API

Không bao giờ tin tưởng vào dữ liệu bên ngoài. Luôn xác thực và vệ sinh các phản hồi API trước khi sử dụng chúng:

if let userAge = apiResponse["age"] as? Int,
 userAge >= 0 && userAge <= 150 {
 // Safe to use
 user.age = userAge
} else {
 // Handle invalid data appropriately
 throw ValidationError.invalidAge
}

3. Sử dụng các truy vấn được tham số hóa (Quan trọng nhất)

Sai lầm nguy hiểm nhất là xây dựng truy vấn cơ sở dữ liệu thông qua nối chuỗi. Hãy xem xét mã dễ bị tấn công này:

// ❌ NEVER DO THIS - Vulnerable to SQL Injection
let username = userInput // Could be: "admin' OR '1'='1"
let query = "SELECT * FROM users WHERE username = '\(username)'"
database.execute(query)

Nếu người dùng độc hại nhập admin' OR '1'='1 làm tên người dùng của họ, truy vấn sẽ trở thành:

SELECT * FROM users WHERE username = 'admin' OR '1'='1'

Điều này sẽ trả về tất cả người dùng trong cơ sở dữ liệu thay vì chỉ một người dùng, có khả năng làm lộ dữ liệu nhạy cảm. Giải pháp bảo mật sử dụng các truy vấn được tham số hóa:

// ✅ SAFE - Using parameterized queries
let query = "SELECT * FROM users WHERE username = ?"
database.execute(query, withArgumentsIn: [username])

Trong cách tiếp cận này, ? là phần giữ chỗ mà cơ sở dữ liệu coi như một tham số, không phải là một phần của lệnh SQL. Giá trị tên người dùng được chuyển riêng trong withArgumentsIn mảng.

Điều này có nghĩa là ngay cả khi người dùng cố gắng chèn mã SQL như admin' OR '1'='1 , cơ sở dữ liệu sẽ coi toàn bộ chuỗi là tên người dùng theo nghĩa đen để tìm kiếm – không phải là mã SQL thực thi. Công cụ cơ sở dữ liệu tự động thoát khỏi mọi ký tự đặc biệt, loại bỏ hoàn toàn nguy cơ bị SQL SQL.

Bằng cách tách cấu trúc truy vấn khỏi dữ liệu, các truy vấn được tham số hóa đảm bảo rằng thông tin đầu vào của người dùng không bao giờ có thể thay đổi logic dự định của câu lệnh SQL của bạn.

4. Bí mật mã hóa cứng

Khóa API, thông tin xác thực hoặc mã thông báo riêng được mã hóa cứng trong mã nguồn là một lỗi bảo mật nghiêm trọng khác. Những kẻ tấn công có thể trích xuất những bí mật như vậy từ các tệp nhị phân được biên dịch bằng cách sử dụng các công cụ kỹ thuật đảo ngược, đặc biệt là đối với các ứng dụng được phát hành rộng rãi.

Sau khi bị lộ, những thông tin xác thực này có thể được sử dụng để truy cập vào các dịch vụ phụ trợ của bạn, có khả năng dẫn đến vi phạm dữ liệu hoặc các khoản phí trái phép.

Vấn đề – Bí mật được mã hóa cứng:

// NEVER DO THIS
class APIClient {
 private let apiKey = "1234567890abcdef"
 private let secretToken = "sk_live_51H..."
 func makeRequest() {
 // These secrets are embedded in your binary
 let headers = ["Authorization": "Bearer \(apiKey)"]
 }
}

Cách khắc phục:

Không bao giờ lưu trữ thông tin xác thực nhạy cảm trực tiếp trong mã của bạn. Dưới đây là những lựa chọn thay thế an toàn:

Giải pháp 1:Tìm nạp bí mật từ phần phụ trợ khi chạy

Cách tiếp cận an toàn nhất là không bao giờ lưu trữ bí mật trên máy khách. Thay vào đó, hãy xác thực người dùng và cho phép chương trình phụ trợ của bạn thực hiện các lệnh gọi API được ủy quyền:

class APIClient {
 private var sessionToken: String?
 // User logs in and receives a temporary session token
 func authenticateUser(email: String, password: String) async throws {
 let response = try await backend.login(email: email, password: password)
 // Store only a temporary, user-specific session token
 self.sessionToken = response.sessionToken
 }
 // Backend handles the actual API calls with the real API key
 func fetchUserData() async throws -> UserData {
 guard let token = sessionToken else {
 throw AuthError.notAuthenticated
 }
 // Your backend receives this request, validates the session token,
 // then uses its own API keys to fetch data from third-party services
 return try await backend.getUserData(sessionToken: token)
 }
}

Cách thức hoạt động:

Ứng dụng của bạn không bao giờ biết các khóa API thực tế. Khi người dùng cần dữ liệu, ứng dụng của bạn sẽ gửi yêu cầu đến máy chủ phụ trợ của riêng bạn bằng mã thông báo phiên. Phần phụ trợ của bạn xác thực mã thông báo, sau đó sử dụng các khóa API được lưu trữ an toàn của riêng mình để thực hiện lệnh gọi API thực tế của bên thứ ba. Bằng cách này, những bí mật thực sự sẽ không bao giờ rời khỏi máy chủ của bạn.

Giải pháp 2:Biến môi trường hoặc tệp cấu hình (Chỉ dành cho nhà phát triển)

Đối với môi trường phát triển, hãy sử dụng các tệp .xcconfig bị loại trừ khỏi kiểm soát phiên bản:

// Secrets.xcconfig (add to .gitignore!)
API_KEY = your_dev_api_key_here
API_SECRET = your_dev_secret_here
// Access in your code through Info.plist
class Config {
 static let apiKey: String = {
 guard let key = Bundle.main.object(forInfoDictionaryKey: "API_KEY") as? String else {
 fatalError("API_KEY not found in configuration")
 }
 return key
 }()
}

Quan trọng :Cách tiếp cận này chỉ phù hợp với môi trường phi sản xuất! Không bao giờ gửi khóa API sản xuất cùng với ứng dụng của bạn, ngay cả trong tệp cấu hình.

5. Xác thực và ủy quyền không đầy đủ

Việc dựa vào việc kiểm tra xác thực và ủy quyền phía máy khách là rất rủi ro. Những kẻ tấn công có thể khiến ứng dụng bỏ qua các bước kiểm tra này và truy cập một cách trái phép bằng cách ép buộc hoặc giả mạo ứng dụng/thời gian chạy.

Cách khắc phục:

  • Thực hiện xác thực và ủy quyền ở phía máy chủ thay vì phía máy khách.

  • Sử dụng JWT (Mã thông báo web JSON) hoặc OAuth 2.0 để đăng nhập người dùng được xác thực.

  • Logic hết hạn và làm mới mã thông báo cần được triển khai để giảm thiểu khả năng bị đánh cắp mã thông báo.

Ví dụ:Gửi JWT một cách an toàn:

let request = URLRequest(url: apiURL)
request.setValue("Bearer \(jwtToken)", forHTTPHeaderField: "Authorization")

6. Ghi nhật ký và xử lý lỗi không an toàn

Các phương pháp ghi nhật ký mở rộng và không an toàn, cũng như các trường hợp ngoại lệ chưa được phát hiện, có thể dẫn đến việc lộ thông tin nhạy cảm bao gồm tên người dùng, mật khẩu và khóa API.

Cách khắc phục:

  • Ghi lại thông tin nhạy cảm một cách cẩn thận.

  • Triển khai quản lý lỗi có kiểm soát và cung cấp lượng thông tin tối thiểu trong các thông báo do người dùng trình bày.

  • Triển khai các thư viện ghi nhật ký an toàn giúp che giấu hoặc mã hóa dữ liệu cá nhân.

do {
 try someRiskyOperation()
} catch {
 // Log error securely
 Logger.log("Operation failed: \(error.localizedDescription)")
}

7. Bỏ qua việc làm xáo trộn mã và kỹ thuật đảo ngược

Các tệp nhị phân Swift có thể được thiết kế ngược để tiết lộ logic kinh doanh, thuật toán nhạy cảm hoặc các bí mật ẩn giấu. Những kẻ tấn công sử dụng các công cụ như Hopper Disassembler, class-dump hoặc IDA Pro để dịch ngược ứng dụng của bạn và phân tích cách ứng dụng hoạt động nội bộ. Rủi ro này thường bị đánh giá thấp, đặc biệt đối với các ứng dụng nhỏ hơn nhưng bất kỳ ứng dụng nào cũng có thể là mục tiêu.

Điều này có nghĩa là khi bạn biên dịch một ứng dụng Swift, tệp nhị phân kết quả sẽ chứa:

  • Tên lớp và chữ ký phương thức

  • Chuỗi ký tự (URL, thông báo lỗi, khóa)

  • Cấu trúc logic mã của bạn

  • Triển khai thuật toán

Kẻ tấn công có thể trích xuất thông tin này và sử dụng nó để hiểu quy trình xác thực của ứng dụng và bỏ qua nó, sao chép các thuật toán độc quyền của bạn, tìm các điểm cuối hoặc khóa API được mã hóa cứng mà bạn cho là "ẩn", khám phá các tính năng cao cấp để mở khóa mà không phải trả tiền, v.v.

Tại sao nó lại tệ – Ví dụ thực tế:

Hãy tưởng tượng bạn kiểm tra tính năng cao cấp trong ứng dụng của mình:

class FeatureManager {
 func isPremiumUser() -> Bool {
 // Check if user has premium access
 let hasSubscription = UserDefaults.standard.bool(forKey: "premium_unlocked")
 return hasSubscription
 }
 func unlockPremiumFeature() {
 guard isPremiumUser() else {
 showPaywall()
 return
 }
 // Show premium content
 showPremiumContent()
 }
}

Kẻ tấn công có thể thiết kế ngược ứng dụng của bạn và phát hiện ra rằng phương thức isPremiumUser() kiểm soát quyền truy cập và nó chỉ kiểm tra UserDefaults khóa có tên là premium_unlocked . Sau đó, họ sẽ biết rằng họ có thể sử dụng các công cụ thao tác trong thời gian chạy để đặt giá trị này thành true, bỏ qua hoàn toàn tường phí của bạn.

Cách khắc phục:

1. Sử dụng Tối ưu hóa trình biên dịch Swift

Bật cờ tối ưu hóa để loại bỏ các ký hiệu gỡ lỗi và làm cho tệp nhị phân khó đọc hơn:

// In your build settings:
// - Set "Optimization Level" to "-O" (or -Osize) for release builds
// - Enable "Strip Debug Symbols During Copy" = YES
// - Set "Strip Style" to "All Symbols"

Điều này loại bỏ tên hàm và làm cho mã được biên dịch khó đọc hơn – mặc dù tên lớp/phương thức vẫn hiển thị một phần.

2. Sử dụng Công cụ làm xáo trộn biểu tượng

Các công cụ như SwiftShield có thể đổi tên các lớp, phương thức và thuộc tính của bạn thành những cái tên vô nghĩa:

// Before obfuscation (readable to attackers):
class FeatureManager {
 func isPremiumUser() -> Bool { ... }
}
// After obfuscation (harder to understand):
class a7f3b2 {
 func x9k2m() -> Bool { ... }
}

Mặc dù điều này không ngăn chặn kỹ thuật đảo ngược nhưng nó khiến kẻ tấn công khó hiểu được chức năng của mã hơn đáng kể.

3. Di chuyển logic nhạy cảm đến máy chủ (Phương pháp hay nhất)

Thay vì kiểm tra trạng thái cao cấp cục bộ, hãy xác minh phía máy chủ:

// ✅ Secure approach - Server validates everything
class FeatureManager {
 func unlockPremiumFeature() async {
 do {
 // Server checks if user truly has premium access
 let hasAccess = try await backend.verifyPremiumAccess(userId: currentUserId)
 if hasAccess {
 showPremiumContent()
 } else {
 showPaywall()
 }
 } catch {
 // Handle error
 showPaywall()
 }
 }
}

Cách thức hoạt động:

Phần phụ trợ của bạn duy trì nguồn sự thật về quyền truy cập cao cấp. Ngay cả khi kẻ tấn công thiết kế ngược ứng dụng của bạn và cố gắng bỏ qua việc kiểm tra, máy chủ sẽ từ chối các yêu cầu trái phép. Ứng dụng chỉ trở thành một lớp giao diện người dùng, trong khi tất cả các quyết định quan trọng đều diễn ra ở phía máy chủ – nơi những kẻ tấn công không thể thao túng chúng.

Nguyên tắc chính là giả sử mã ứng dụng của bạn là công khai:không bao giờ dựa vào hoạt động kiểm tra phía khách hàng đối với các hoạt động quan trọng về bảo mật như thanh toán, kiểm soát quyền truy cập hoặc xác thực. Sử dụng tính năng che giấu để làm cho kỹ thuật đảo ngược trở nên khó khăn hơn nhưng cuối cùng lại chuyển logic nhạy cảm sang phần phụ trợ an toàn của bạn

8. Thư viện của bên thứ ba không an toàn

Thư viện của bên thứ ba sẽ gặp rủi ro nếu bị tấn công hoặc lỗi thời. Các nhà phát triển có thể vô tình ưu tiên chức năng của ứng dụng hơn là các rủi ro bảo mật tiềm ẩn từ các phần phụ thuộc và các công cụ ETL có thể trợ giúp thêm bằng cách hợp lý hóa việc giám sát và xử lý dữ liệu liên quan đến phần phụ thuộc để xác định các lỗ hổng hiệu quả hơn.

Ở quy mô rộng hơn, việc triển khai các biện pháp bảo mật trung tâm dữ liệu mạnh mẽ sẽ đảm bảo rằng ngay cả khi các thành phần của bên thứ ba gây ra rủi ro thì cơ sở hạ tầng cơ bản vẫn có khả năng phục hồi trước các cuộc tấn công.

Cách khắc phục:

  • Chỉ sử dụng các thư viện chất lượng cao, được bảo trì tốt.

  • Cập nhật các phần phụ thuộc và giám sát CVE (Các lỗ hổng và nguy cơ thường gặp).

  • Kiểm tra mã thư viện nếu nó xử lý dữ liệu nhạy cảm.

9. Xác thực sinh trắc học và đa yếu tố không đầy đủ

Hầu hết các ứng dụng chỉ dựa vào mật khẩu nên rất dễ bị hack. Việc kích hoạt sinh trắc học như Face ID hoặc Touch ID sẽ tăng cường bảo mật cho người dùng.

Cách khắc phục:

  • Liên kết khung LocalAuthentication để xác thực sinh trắc học.

  • Kết hợp sinh trắc học với xác thực dựa trên máy chủ để xác thực đa yếu tố (MFA).

import LocalAuthentication
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
 context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
 localizedReason: "Access your account") { success, authError in
 DispatchQueue.main.async {
 if success {
 // Proceed securely
 } else {
 // Handle failure (authError may contain the reason)
 print("Authentication failed: \(authError?.localizedDescription ?? "Unknown error")")
 }
 }
}
} else {
// Biometrics not available, check error for details
 print("Biometrics unavailable: \(error?.localizedDescription ?? "Unknown error")")
}

10. Bỏ qua việc kiểm tra bảo mật định kỳ

Các ứng dụng, mặc dù tuân theo các phương pháp hay nhất trong quá trình phát triển, thường chứa các lỗ hổng chưa được khám phá xuất hiện từ các tương tác phức tạp, sự phụ thuộc của bên thứ ba hoặc các vectơ tấn công mới được phát hiện. Kiểm tra bảo mật thường xuyên là hết sức cần thiết để phát hiện ra những lỗ hổng này trước khi kẻ tấn công khai thác chúng.

Kiểm tra bảo mật sẽ diễn ra ở nhiều giai đoạn, sử dụng các công cụ và phương pháp có thể truy cập:

  1. Quét bảo mật tự động: Tự động chạy với mọi bản dựng để phát hiện các sự cố thường gặp.

  2. Kiểm tra mã tự thực hiện: Đánh giá thường xuyên tập trung vào bảo mật mã của riêng bạn bằng cách sử dụng các nguyên tắc đã được thiết lập.

  3. Công cụ quét lỗ hổng: Sử dụng các công cụ miễn phí như MobSF để phân tích ứng dụng của bạn để tìm lỗi bảo mật.

  4. Kiểm tra phụ thuộc: Kiểm tra thư viện của bên thứ ba để tìm các lỗ hổng bảo mật đã biết.

Cách khắc phục:

1. Triển khai quét bảo mật tự động trong CI/CD

Tích hợp các công cụ quét bảo mật vào quy trình tích hợp liên tục của bạn để mọi thay đổi mã đều được kiểm tra tự động:

# Example: GitHub Actions workflow for automated security scanning
name: Security Scan
on: [push, pull_request]
jobs:
 security-scan:
 runs-on: macos-latest
 steps:
 - name: Checkout code
 uses: actions/checkout@v3
 - name: Run MobSF Security Scan
 run: |
 # Mobile Security Framework - scans for common vulnerabilities
 docker run -v $(pwd):/app opensecurity/mobile-security-framework-mobsf
 - name: Dependency Vulnerability Check
 run: |
 # Check CocoaPods/SPM dependencies for known CVEs
 brew install dependency-check
 dependency-check --scan ./Podfile.lock --format JSON
 - name: Secret Detection
 run: |
 # Detect accidentally committed secrets
 brew install truffleHog
 truffleHog filesystem . --json
 - name: Fail build on critical issues
 run: |
 if grep -q "CRITICAL" security-report.json; then
 echo "Critical security issues found!"
 exit 1
 fi

Quét tự động kiểm tra:

  • Khóa API, mã thông báo hoặc mật khẩu được mã hóa cứng

  • Cấu hình mạng không an toàn (cho phép HTTP thay vì HTTPS)

  • Thuật toán mã hóa yếu

  • Các lỗ hổng đã biết trong thư viện của bên thứ ba

  • Xác thực chứng chỉ SSL/TLS không đúng

  • Lưu trữ dữ liệu không an toàn (lưu trữ dữ liệu nhạy cảm trong UserDefaults)

  • Quyền ứng dụng quá mức

Ví dụ đầu ra từ quá trình quét tự động:

Security Scan Results:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[CRITICAL] Hardcoded API Key Found
 File: APIClient.swift:15
 Issue: API key "sk_live_abc123..." detected in source code
[HIGH] Insecure HTTP Connection
 File: NetworkManager.swift:42
 Issue: App allows cleartext HTTP traffic to api.example.com
 Fix: Enforce HTTPS or add exception to Info.plist if required
[MEDIUM] Weak Encryption Algorithm
 File: DataEncryption.swift:28
 Issue: Using MD5 for hashing (cryptographically broken)
 Fix: Use SHA-256 or better
[LOW] Outdated Dependency
 Library: Alamofire 4.2.0
 Issue: Known vulnerability CVE-2021-12345
 Fix: Update to version 5.6.0 or later
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Build Failed: 2 critical issues must be fixed before deployment

2. Sử dụng MobSF (Khung bảo mật di động) để phân tích lỗ hổng bảo mật

MobSF là một công cụ tự động, miễn phí giúp phân tích ứng dụng iOS của bạn để tìm các vấn đề bảo mật:

# Install and run MobSF locally
docker pull opensecurity/mobile-security-framework-mobsf
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf
# Upload your .ipa file through the web interface at localhost:8000
# MobSF will analyze and provide a detailed security report

MobSF kiểm tra những gì:

  • Phân tích nhị phân cho các bí mật được mã hóa cứng

  • Các kiểu lưu trữ dữ liệu không an toàn

  • Triển khai mật mã yếu

  • Kết nối mạng không an toàn

  • Các phương pháp hay nhất về chất lượng và bảo mật mã

  • Tuân thủ các tiêu chuẩn bảo mật

3. Tiến hành kiểm tra mã thường xuyên bằng OWASP MSTG

Sử dụng Hướng dẫn kiểm tra bảo mật di động OWASP làm danh sách kiểm tra để kiểm tra mã của riêng bạn:

// Example: Following OWASP MSTG recommendations for secure storage
class SecureStorage {
 // ❌ Insecure - UserDefaults is not encrypted
 func saveTokenInsecurely(_ token: String) {
 UserDefaults.standard.set(token, forKey: "authToken")
 }
 // ✅ Secure - Using Keychain as OWASP recommends
 func saveTokenSecurely(_ token: String) throws {
 let data = token.data(using: .utf8)!
 let query: [String: Any] = [
 kSecClass as String: kSecClassGenericPassword,
 kSecAttrAccount as String: "authToken",
 kSecValueData as String: data,
 kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
 ]
 SecItemDelete(query as CFDictionary)
 let status = SecItemAdd(query as CFDictionary, nil)
 guard status == errSecSuccess else {
 throw StorageError.saveFailed
 }
 }
}

Danh sách kiểm tra MSTG của OWASP để tự kiểm tra:

  • [ ] Tất cả dữ liệu nhạy cảm có được mã hóa khi lưu trữ không?

  • [ ] HTTPS có được thực thi cho tất cả các cuộc gọi mạng không?

  • [ ] Các chứng chỉ có được xác thực đúng cách không?

  • [ ] Dữ liệu nhạy cảm có bị loại khỏi nhật ký không?

  • [ ] Khóa API và bí mật không được mã hóa cứng phải không?

  • [ ] Đầu vào của người dùng có được xác thực và vệ sinh không?

  • [ ] Mã thông báo xác thực có được lưu trữ an toàn trong Chuỗi khóa không?

4. Quét phụ thuộc tự động

Liên tục theo dõi các phần phụ thuộc của bạn để phát hiện các lỗ hổng mới được phát hiện:

# For CocoaPods projects
gem install cocoapods-audit
pod audit
# For Swift Package Manager
# Use GitHub Dependabot (free for public repos) or
brew install swift-outdated
swift-outdated

Và thiết lập cảnh báo tự động bằng các công cụ sau:

  • GitHub Dependabot: Tự động tạo PR khi phát hiện các phần phụ thuộc dễ bị tổn thương (miễn phí)

  • Snyk :Cấp miễn phí có sẵn cho các dự án nguồn mở

  • Kiểm tra phụ thuộc OWASP: Công cụ dòng lệnh miễn phí

Kết luận

Phát triển các ứng dụng iOS an toàn bằng Swift là tất cả về tư duy tiến bộ. Bạn nên làm tất cả những gì có thể để tránh những lỗi phổ biến này, như lưu trữ dữ liệu không an toàn, giao tiếp mạng kém, bí mật được mã hóa cứng hoặc xác thực kém.

Sử dụng Chuỗi khóa để biết thông tin bí mật, yêu cầu HTTPS, xác thực đầu vào và xác thực đa yếu tố đều là các bước giúp giảm rủi ro.

Việc kiểm tra thường xuyên các lỗ hổng bảo mật và hạn chế sử dụng thư viện của bên thứ ba cũng có thể nâng cao hơn nữa tính bảo mật cho ứng dụng của bạn.

An ninh là một trách nhiệm liên tục. Swift cung cấp các công cụ, nhưng các nhà phát triển cần áp dụng những công cụ đó một cách cẩn thận. Vấn đề bảo mật được xử lý ngay từ đầu sẽ bảo vệ thông tin của người dùng, tạo niềm tin và bảo vệ danh tiếng của ứng dụng.

Học cách viết mã miễn phí. Chương trình giảng dạy mã nguồn mở của freeCodeCamp đã giúp hơn 40.000 người có được việc làm với tư cách là nhà phát triển. Bắt đầu