If you’ve ever worked on an app with time-sensitive content, you know that working with date-time objects can be problematic.
Most common issues rise from working with timezones. But what if the content we are working on is supposed to only be available for a limited period of time, without the possibility to access it afterwards?
Since time is constantly incrementing, the logical assumption would be that it’s necessary to store the time when the content first becomes available and measure the difference between right now and that point.
Pretty trivial solution, right?
I thought the same until I came across this revelatory Twitter thread pointing out that it’s possible to turn back time to access time-limited content which got my attention.
Catch 24-hours
When you type Date()
in Swift, you get an object containing the current date, time and timezone that is set in the iOS Settings -> General -> Date & time
menu. Most users probably have the “Set automatically” option checked, which is great.
However, users who dig deep enough might discover that it is possible to set device time manually, and then, as far as your phone is concerned, you can basically travel through time.
Let’s assume that an app restricts some piece of content to be available only for 60 seconds. Could be a self-destructing message, a limited time promotional offer, or a sensitive picture (wink wink).
You simply store the time when the content was accessed and then measure the difference in seconds on each access, but a resourceful user who knows what they are looking for can easily set the device time back to what it was when the content was initially accessed, making the content available again.
Assuming some malicious tech-savvy users could try to use this for the greater bad, let’s look at how this could be prevented.
Boot time to the rescue
Instead of storing Date
objects (or timeIntervalSince1970
), you should store the device “boot time”, or to simply put it, a unit of time (ie. seconds) elapsed since the operating system was last booted.
func uptime() -> Int {
var uptime = timespec()
if clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) != 0 {
fatalError("Could not execute ’clock_gettime()’")
}
return uptime.tv_sec
}
There is a flaw in this approach.
Boot time will reset every time the device resets, which usually happens after a system update these days. That means you can still “travel through time”, but on the bright side, it does put a significant barrier on malicious users.
You could still access sensitive content, but you’d need to reset the device and measure the elapsed time from system boot to its access.
Most users wouldn’t even bother with setting the device time manually, but of those that would, I reckon most of them wouldn’t bother with boot time, even if they knew about it.
Just to be on the safe side
For absolute certainty, you should offload this responsibility to the server and use Network Time Protocol. It’s a great solution because no one can (hopefully) mess with the server’s boot time.
However, in cases when you cannot use NTP, eg. your app should only work offline, or you need backup for when there is no internet access, the method described earlier will be your best bet to really make access to time-limited content – limited.