iOSアプリのWebViewでのHSTS Super Cookieの取り扱い

この記事は[Wanoグループ Advent Calendar 2016](http://qiita.com/advent-calendar/2016/wano-group)の5日目です。

# 結論
* ATS（App Transport Security）をオンにしてたらそもそもhttpのサイトにアクセスできないから考えなくてよくね。

# 以下検証
## そもそもHSTS Super Cookieってなによ
以下参照。
http://dev.classmethod.jp/client-side/browser/hsts-super-cookies/

> HSTSという機能を実装しているブラウザは[Strict-Transport-Security]というヘッダがついているWebサイトを記録し、次回にそのサイトへのHTTPアクセスがあった場合はブラウザ側で判断し、直接HTTPSで接続しに行くというセキュリティ技術です。
（中略）
HSTSではアドレスの一文字一文字に対してtrueかfalseのbitに変換、それをバイナリ値(PIN、と言います)としてブラウザ内に保存します。そうすることで20億以上のタグ付けが可能になります。上で表示した組み合わせはこのバイナリ値をbase36変換したものです。

この保存した情報というのは、CookieとかLocal Storageとかとはデータの取り扱われ方が違うので、（対策されていないと）データが残り続ける可能性がある。
~~これでみんなトラッキングされまくり~~

## 事の経緯
会社のSlackのdeveloperチャンネルにこんな情報が投稿されてた。
http://www.radicalresearch.co.uk/lab/hstssupercookies/

先輩エンジニアよりざっくり要約
> HSTS Super Cookie はFirefox は対策済み。Safariはやばいと書いているけど実際はやばくないらしい。IEはHSTS未対応なので関係ない。そうだ。

とのこと。
あれ、WebViewは？？

## というわけで検証してみた。
上記のサイトに各webViewでアクセスして、値が変わるかどうか調べた。
検証コードは以下のように雑に書いた。

* UIWebView

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {
    var webView: UIWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        webView = UIWebView(frame: UIScreen.main.bounds)
        let url = URL(string: "http://www.radicalresearch.co.uk/lab/hstssupercookies/")!
        let request = URLRequest(url: url)
        webView.loadRequest(request)
        view.addSubview(webView)
    }
}
```

* WKWebView

```swift:ViewConroller2.swift
import UIKit
import WebKit

class ViewController2: UIViewController {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        webView = WKWebView(frame: UIScreen.main.bounds)
        let url = URL(string: "http://www.radicalresearch.co.uk/lab/hstssupercookies/")!
        let request = URLRequest(url: url)
        webView.load(request)
        view.addSubview(webView)
    }
}
```

* SFSafariViewController

```swift:ViewController3.swift
import UIKit
import SafariServices

class ViewController3: UIViewController {
    @IBOutlet weak var containerView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = URL(string: "http://www.radicalresearch.co.uk/lab/hstssupercookies/")!
        let viewController = SFSafariViewController(url: url, entersReaderIfAvailable: true)
        viewController.view.frame = UIScreen.main.bounds
        
        addChildViewController(viewController)
        containerView.addSubview(viewController.view)
        viewController.didMove(toParentViewController: self)
    }
}
```

* SFSafariViewControllerはViewじゃないのでContainerViewを使った。
* もちろんATSをオフにしてhttpサイトへの接続を許可している。
* ~~この辺で、あれ、ATSだとそもそもhttpのサイトにアクセス出来ないから関係なくね？って気づいたけど気のせい。~~

## 結果
* あり - 上記サイトで値に変化があった = HSTSで保存された情報がリセットされた
* なし - 上記サイトで値に変化がなかった = HSTSで保存された情報がリセットされなかった

|  | UIWebView | WKWebView | SFSafariViewController |
|:-:|:-:|:-:|:-:|
| webViewインスタンス作り直し | なし | なし | なし |
| アプリの終了 | なし | なし | なし |
| アプリの削除&インストール | あり | あり | なし |
| 端末の再起動 | なし | なし | あり |

## 考察
* UIWebView、WKWebViewのHSTS情報の保存領域はアプリ内っぽい。
* SFSafariViewControllerのは、そもそもwebView自体がSafariなので、Safariと同じ領域（OSのレイヤー？）に保存されている模様。
    * http://kaworu.jpn.org/security/HSTS_Super_Cookies にあるように、どうやらiCloudに保存されているらしい（未検証）
* まぁ、1月から始まるATS必須化に合わせてhttpサイトにアクセスできなくなるから、そもそも関係ないけどな！！！！

# 余談
## UIWebView、WKWebViewのHSTS情報をアプリ削除以外の方法でリセットできないだろうか
以下やってみたこと。
* UIWebViewのキャッシュ削除
* WKWebViewのキャッシュ削除
* /tmp、/Cachesディレクトリの削除

書いたコードはこんな。

```swift:AppDelegate.swift
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        // UIWebView用キャッシュ削除
        URLCache.shared.removeAllCachedResponses()
        URLCache.shared.diskCapacity = 0
        URLCache.shared.memoryCapacity = 0
        
        // WKWebView用キャッシュ削除
        let websiteDataTypes = NSSet(array: [
            WKWebsiteDataTypeDiskCache,
            WKWebsiteDataTypeMemoryCache,
            WKWebsiteDataTypeCookies,
            WKWebsiteDataTypeLocalStorage,
            WKWebsiteDataTypeSessionStorage,
            WKWebsiteDataTypeWebSQLDatabases,
            WKWebsiteDataTypeIndexedDBDatabases,
            WKWebsiteDataTypeOfflineWebApplicationCache])
        WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set<String>, modifiedSince: Date(timeIntervalSince1970: 0), completionHandler: {})
        
        // アプリ全体のキャッシュをとにかく削除
        let pathArray = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, .userDomainMask, true)
        for path in pathArray {
            try? FileManager().removeItem(atPath: path)
        }
        try? FileManager().removeItem(atPath: NSTemporaryDirectory())
        
        return true
    }
```

## 結論
* 消せない(´・ω・｀)