iosswiftencryptionnsurlsession

Is there a way to securely store cookies using HTTPCookieStorage


I recently discovered that HTTPCookieStorage stores my application's secure JWT tokens as searchable text in a file located at <App Bundle Path>/Library/Cookies/<bundle id>.binarycookies. I had previously presumed that Apple would encrypt the tokens before storing them to disc, especially given that they are marked as secure tokens. I've searched the Internet, and most responses admit that this is true, but suggest that this is not an issue because of Apple's Data Protection technology (which uses 256-bit encryption when the device is locked). However, when the device is unlocked or jailbroken, these cookies would be readily accessible. I want to ensure that my application's JWT tokens are encrypted even when the device is unlocked or jailbroken.

I've searched the documentation for HTTPCookieStorage for any settings to encrypt secure cookies before saving them to disc and found no built-in option.


Solution

  • Turns out that HTTPCookieStorage can be subclassed. I ended up implementing an in-memory solution. This could easily be modified to use Keychain. In-memory is good enough for my current solution.

    class SecureCookieStorage : HTTPCookieStorage {
        private var inMemoryCookieStorage : [String: [HTTPCookie]] = [:]
        private let cookieQueue = DispatchQueue(label: "secure.cookie.storage", attributes: .concurrent)
    
        override func getCookiesFor(_ task: URLSessionTask, completionHandler: @escaping ([HTTPCookie]?) -> Void) {
            guard let host = task.currentRequest?.url?.host else {
                completionHandler(nil)
                return
            }
            
            cookieQueue.sync { [weak self] in
                guard let self = self else { return }
                let cookies = self.inMemoryCookieStorage[host]
                completionHandler(cookies)
            }
        }
        
        override func storeCookies(_ cookies: [HTTPCookie], for task: URLSessionTask) {
            guard let host = task.currentRequest?.url?.host else {
                return
            }
            
            cookieQueue.async(flags: .barrier) { [weak self] in
                guard let self = self else { return }
                self.inMemoryCookieStorage[host] = cookies
            }
        }
    }
    

    Then, set up URLSessionConfiguration to use SecureCookieStorage

    let configuration = URLSessionConfiguration.default
    configuration.httpCookieStorage = SecureCookieStorage()
    let urlSession = URLSession(configuration:configuration)