---
title: Không deserialize dữ liệu không tin cậy bằng NSKeyedUnarchiver
impact: HIGH
impactDescription: NSKeyedUnarchiver không giới hạn lớp có thể unarchive mặc định, cho phép gadget chain attacks dẫn đến arbitrary code execution khi xử lý dữ liệu từ server không đáng tin hoặc từ clipboard.
tags: swift, ios, deserialization, nskeyedunarchiver, codable, security
---

## Không deserialize dữ liệu không tin cậy bằng NSKeyedUnarchiver

`NSKeyedUnarchiver.unarchiveObject(with:)` (deprecated nhưng vẫn phổ biến) không an toàn vì không giới hạn class. Phải dùng `unarchivedObject(ofClass:from:)` với whitelist class cụ thể, hoặc ưu tiên dùng `Codable`/`JSONDecoder` cho server data.

**Incorrect (unarchive không có class restriction):**

```swift
import Foundation

// !! Unsafe - không giới hạn class, vulnerable to gadget chain
func restoreCart(from data: Data) -> ShoppingCart? {
    // Deprecated API, không an toàn với data từ server
    return NSKeyedUnarchiver.unarchiveObject(with: data) as? ShoppingCart
}

// !! Unarchive data từ UserDefaults (attacker có thể sửa trên jailbroken device)
func loadCachedUser() -> User? {
    guard let data = UserDefaults.standard.data(forKey: "cached_user") else { return nil }
    return try? NSKeyedUnarchiver.unarchivedObject(
        ofClasses: [NSObject.self],  // !! Quá rộng - accept mọi class
        from: data
    ) as? User
}
```

**Correct (whitelist class hoặc dùng Codable):**

```swift
import Foundation

// SAFE: Whitelist class cụ thể
func restoreCart(from data: Data) throws -> ShoppingCart? {
    return try NSKeyedUnarchiver.unarchivedObject(
        ofClass: ShoppingCart.self,  // Chỉ accept ShoppingCart
        from: data
    )
}

// BEST: Dùng Codable cho server data - an toàn hơn nhiều
struct ShoppingCart: Codable {
    let id: UUID
    let items: [CartItem]
    let totalAmount: Decimal
}

struct CartItem: Codable {
    let productId: String
    let quantity: Int
    let price: Decimal
}

func parseCartResponse(data: Data) throws -> ShoppingCart {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    return try decoder.decode(ShoppingCart.self, from: data)
}

// SAFE: Nếu bắt buộc dùng NSKeyedUnarchiver, whitelist rõ ràng
func loadUserFromCache(data: Data) throws -> User? {
    return try NSKeyedUnarchiver.unarchivedObject(
        ofClasses: [User.self, NSString.self, NSNumber.self, NSUUID.self],
        from: data
    ) as? User
}
```

**Tools:** OWASP MASVS-CODE-4, Instruments, Static analysis (semgrep rule: use-of-nskeyedunarchiver)
