[WWDC18] NSSecureCodingで信頼性の高いデータのやり取りを #WWDC18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

本記事は Apple からベータ版として公開されているドキュメントを情報源としています。 そのため、正式版と異なる情報になる可能性があります。ご留意の上、お読みください。

Data You Can Trust

A lot can go wrong when loading data into your app. Whether you work directly with JSON and property lists, or with higher-level APIs such as NSCoding and Codable, learn how to defend your customers and secure your code against invalid or malicious data. Avoid fatal assumptions by validating payload structure, type information and domain correctness, to turn the data you work with into data you can trust.

Validate First, Execute Later

WebAPIのレスポンスを受け取る際、第三者によってデータが改竄されるリスクもあります。 改竄されたデータを実行した際、アプリがクラッシュするなどの影響もあります。 それを防ぐために、バリデーションを最初に行い、その後コードの実行をすべきだと提言されました。 そのアプローチへの解の一つがセッションで紹介されました。

<br />func validate(_ listing: [String : Any]) throws {
    guard let id = listing["id"] as? Int,
        id > 0, id <= Int32.max else { throw ... }

    guard let urlString = listing["url"] as? String,
        urlString.count <= 50,
        let url URL(string: urlString) else { throw ... }

    guard url.host == "example.com" else { throw ... }
}

try listings.forEach(validate)

NSSecureCoding

  • NSCodingでアーカイブして、データのやり取りを行う方法もあります
<br />class Purchase : NSCoding {
    required init?(coder: NSCoder) {
        guard let listing = coder.decodeObject(forKey: "listing") as? Listing else {
            return nil
        }
        self.listing = listing

        guard let data = coder.decodeObject(forKey: "purchaseDate") as? Date else {
            return nil
        }
        self.purchaseDate = date
     }
}

func savePurchase(_ purchase: Purchase) throws {
    let archive = NSKeyedArchiver.archivedData(withRootObject: purchase)
    try archive.write(to: /* storage location */)
}

func sloadPurchase() throws -> [Purchase] {
    let storage: [Data] = ... /* load from storage location */)
    return try storage.map { data in
        guard let object = try NSKeyedUnarchiver.unarchiveTopLevelObject(with: data),
            let purchase = object as? Purchase else { throw ... }
        return purchase
    }
}

  • その場合は NSSecureCoding を使うとより信頼性の高いデータのやり取りが行えます
    • NSSecureCoding はオブジェクト置換攻撃に対して堅牢な方法でエンコードとデコードを可能にするプロトコルです
    • NSSecureCoding
<br />class Purchase : NSSecureCoding {
    func encode(with coder: NSCoder) { ... }
    requierd init?(coder: NSCoder) { ... }
    class var supportSecureCoding: Bool { return true }
}

  • デコード方法
let data = NSKeyedArchiver.archivedData(withRootObject: myObject, requiringSecureCoding: true)
let decode = NSKeyedUnarchiver.unarchivedObject(of: MyClass.self, from: data)

まとめ

NSSecureCoding 自体は新しく出た機能ではありませんが、アプリ側のセキュリティ要件を改めて満たすためには知っておいて損はない情報だと思いました。