# How to test object substitution attacks and NSSecureCoding

In this blog post I tell you how to test object substitution attacks and how using `NSSecureCoding` can prevent such attacks. I will also
- share example code on how to use `NSKeyedArchiver` and `NSKeyedUnArchiver` either with or without `NSSecureCoding`.
- explain how to archive / unarchive an object into different formats (binary vs. XML).

If you are not too familiar with the idea behind `NSSecureCoding` when using `NSKeyedArchiver` / `NSKeyedUnarchiver` then I recommend that you watch Apple's WWDC 2018 session [Data You Can Trust](https://developer.apple.com/videos/play/wwdc2018/222/).

Additionally I recommend to read the following articles as those helped me to deepen my understanding.
- [Object serialization in iOS](https://dmtopolog.com/object-serialization-in-ios/) by [Dmitrii Ivanov](https://twitter.com/dmtopolog)
- [NSSecureCoding](https://www.swiftjectivec.com/nssecurecoding/) by [Jordan Morgan](https://twitter.com/jordanmorgan10)

# Object substitution attack

First let's encode data with `NSKeyedArchiver` in the form of XML.

```Swift
let archiver = NSKeyedArchiver(requiringSecureCoding: false)
archiver.outputFormat = .xml
archiver.encodeRootObject(yourObject)
archiver.finishEncoding()
let data = archiver.encodedData
archive.write(to: localURL)
```
The serialization result for my custom `Model` class

```Swift
class Model: NSObject, NSCoding {
    var text: String
    var amount: Double

   // further code is omitted for readability
}
```
is the following in XML:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>$archiver</key>
	<string>NSKeyedArchiver</string>
	<key>$objects</key>
	<array>
		<string>$null</string>
		<dict>
			<key>$class</key>
			<dict>
				<key>CF$UID</key>
				<integer>3</integer>
			</dict>
			<key>amount</key>
			<real>22.219999999999999</real>
			<key>text</key>
			<dict>
				<key>CF$UID</key>
				<integer>2</integer>
			</dict>
		</dict>
		<string>Test</string>
		<dict>
			<key>$classes</key>
			<array>
				<string>NSSecureCodingExample.Model</string>
				<string>NSObject</string>
			</array>
			<key>$classname</key>
			<string>NSSecureCodingExample.Model</string>
		</dict>
	</array>
	<key>$top</key>
	<dict>
		<key>$0</key>
		<dict>
			<key>CF$UID</key>
			<integer>1</integer>
		</dict>
	</dict>
	<key>$version</key>
	<integer>100000</integer>
</dict>
</plist>

```

As an attacker I can manipulate the information in this document in such a way that not `Model` gets loaded during deserialization but another class.

For example I can change `NSSecureCodingExample.Model` to `NSSecureCodingExample.FakeModel` which corresponds to a class in my app

```Swift
import Foundation

// For Testing Purposes only
class FakeModel: NSObject, NSCoding {
    let text: String
    let amount: Double

    internal init(text: String, amount: Double) {
        self.text = text
        self.amount = amount
    }

    // ...

    required init?(coder: NSCoder) {
        /**
         If serialized object in XML format was modified to use FakeModel

         <dict>
             <key>$classes</key>
             <array>
                 <string>NSSecureCodingExample.FakeModel</string>
                 <string>NSObject</string>
             </array>
             <key>$classname</key>
             <string>NSSecureCodingExample.FakeModel</string>
         </dict>

         Then app would crash :--!!!
         */
        assert(1 == 2)
    }
}

```

I can unarchive the object with `NSKeyedUnarchiver` later.

```Swift
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data) // This initializer enables requiresSecureCoding by default
unarchiver.requiresSecureCoding = false
let decodedDataObject = try unarchiver.decodeTopLevelObject()
unarchiver.finishDecoding()
// then cast decodedDataObject to your expected structured type
```

The app will crash because `FakeModel` gets loaded during the `decodeTopLevelObject` execution and it the `FakeModel` initializer contains malicious code (an assertion).

Apple introduced the `NSSecureCoding` protocol and multiple API enhancements for `NSKeyedArchiver` and `NSKeyedUnarchiver`. Once you adopt those then the compiler will be able to perform gated class checks so that an incorrect initialization cannot occur.

You can find the complete code, incl. a SwiftUI test application, on GitHub.

%[https://github.com/MarcoEidinger/NSSecureCodingExample]

![Test Application](https://cdn.hashnode.com/res/hashnode/image/upload/v1646950720211/jDu-D1Ead.png)

I made an interesting finding as it appears that it is not possible to unarchive an object stored in XML format which was archived with Secure Coding. If I am mistaken then I'd love to hear from you :)
