2

I run memory dump on my app's memory and find out that it still holds some
sensitive data that was not supposed to stay there after logout.
(note: I have generated the memory dump while in debug mode).
after some investigation I have found out that it is actually the full JSON I am sending via
Almofire (HTTP network library) that is still in memory. I suspected the issue is with the JSONEncoder
so I have created a small app having a simple button that run the next code:

    func encodeJson() {
        let encoder = JSONEncoder()
        
        var dic: [String: String] = [
            "email": "someMail@email.com",
            "password": "somePassord",
            "appName": "test app",
            "version": "1.1.1",
            "os": "ios",
        ]
        
    
        do {
            let data = try encoder.encode(dic)
            dic = [:]
        } catch {
            
        }
        
    }

after clicking the button multiple times in a row, I found that the JSON is still in memory (the serialized json, not the dictionary) even after
the function finished running!

Is there a known memory leak with JSONEncoder?
Is there a different reasoning to the issue that I am missing?
I have read that developers were complaining about related issues with JSONEncoder but didn't find any solutions.

the reason why it is a security issue: in a jail broken device, one can steal sensitive data after user have logged out after installing a phishing app.

vigdora
  • 319
  • 4
  • 11
  • Well it's not supposed to *encrypt* it. Why do you expect the data to *not* remain in memory? – Sweeper Jul 11 '23 at 14:40
  • 2
    Have a look at: https://forums.swift.org/t/erase-dealocated-memory/34964/ – Sweeper Jul 11 '23 at 14:43
  • @Sweeper the function finished running. I am not holding the json in global variable, why wouldn't it be cleaned up after the the function's stack is cleared? – vigdora Jul 11 '23 at 14:44
  • 1
    Yes, the memory is deallocated, but the bits are not necessarily set to 0 or anything like that. – Sweeper Jul 11 '23 at 14:46
  • @Sweeper so there is a security issue within my app -an open door tempts the thief! how can I prevent it? – vigdora Jul 11 '23 at 14:49
  • 1
    [iMAS](https://github.com/project-imas/memory-security) explored this a long time ago, but it has mostly been abandoned. In the most general form, this is not possible. The way that flash memory works means that this is incredibly difficult to actually ensure, even if you can perform an overwrite. Swift's value types makes it even worse. There is no answer today, and the problem has only gotten more difficult over time as systems are optimized in ways that make "write this value to the physical RAM that held this specific value" harder and harder. – Rob Napier Jul 11 '23 at 15:15
  • 1
    If someone has full physical access to another user's jail broken device that hasn't been rebooted, you are correct that it is quite difficult to fully protect information on that device. (This is true of desktop computers as well.) We do hope that hardware vendors will provide more powerful mechanisms for "secure memory" in the future so that users' data can be better protected. (I don't believe this will ever fully protect users who choose to bypass the security mechanisms by doing things like jail breaking, and don't take care to at least reboot. But hardware could be better.) – Rob Napier Jul 11 '23 at 15:17
  • @RobNapier my feeling though it happens mainly with jsonDecoder. may be something in the internal implementation could have been better - releasing the data accordingly wouldn't it make sense for me to write a decoder from scratch in C language, preventing this from happening? – vigdora Jul 11 '23 at 15:21
  • 1
    This has nothing to do with JSONDecoder. It is releasing the data (if it didn't, it would be a massive memory leak, and the system would run out of memory very quickly). Read the above forum thread. Releasing memory does not overwrite it. So previous values will still be in memory until the memory is reused. C would not fix this (at least by itself). `free` does not overwrite memory. It would be easier to write something that came closer using C, but given how memory is implemented, it still is hard to get it out of the cells. – Rob Napier Jul 11 '23 at 15:26
  • @RobNapier ok I understand, but writing the decoder from scratch (in C) I can overwrite the data manually and not only releasing it. i.e using char[] and than resetBytes(in:) – vigdora Jul 11 '23 at 15:29
  • Kind of. It is tricky to get it to actually overwrite the hardware RAM. But you can get closer if you do it this way. It is something you could certainly explore. Just be sure to test it very carefully (and especially in final apps, built with optimizations) to make sure it's doing what you think it is. Writing memory that is never read is something the optimizer is very eager to remove (see [memset_s](https://developer.apple.com/documentation/kernel/2876438-memset_s?language=_1)). – Rob Napier Jul 11 '23 at 15:32
  • `resetBytes(in:)` is absolutely not promised to do what you're trying to do. – Rob Napier Jul 11 '23 at 15:34
  • @RobNapier, can you shortly explain why resetBytes(in:) wont even help? – vigdora Jul 12 '23 at 10:28
  • 1
    See the link above (https://forums.swift.org/t/erase-dealocated-memory/34964/). If you write to memory and then never read it (or never make use of the data once you've read it), the optimizer is allowed to eliminate the entire write. When writing zeros, you have to watch out for optimizations that just mark a pages as "known to be zeros" without actually overwriting the page. It's very difficult to get modern systems to do something that doesn't have a clear impact on the program (like clearing memory). They're built to avoid unnecessary work. That's why you would need tools like `memset_s`. – Rob Napier Jul 12 '23 at 14:00

0 Answers0