When Disappearing Messages Don't Disappear
the 'dark' side of (macOS) notifications
05/08/2018
love these blog posts? support my tools & writing on patreon :)
In Interesting Observation
The ever vigilant Motherboard journalist Lorenzo (@lorenzoFB) b̶u̶g̶g̶e̶d̶ pinged me today with a link to an interesting tweet from Alec Muffett (@AlecMuffett):
In short, Alec noted that if using the macOS Signal App, disappearing messages may remain in macOS's Notification Center. Yikes!
Let's find out why!
Note: The goal of this blog post is simply to illustrate the cause of a publicly disclosed bug.
Moreover, I quite enjoy detailing my macOS spelunking adventures, with the hope that others find it informative and useful!
Signal is an incredible app that I use daily, so nothing but love for them! ♡
Notifications on Mac
Introduced in OSX 10.10 (Yosemite) the Notification Center, "lets you view details about your day—appointments, weather, birthdays, even a summary of what you have planned for tomorrow—and catch up on notifications you missed" (Apple).
Apple goes on to note that "To open Notification Center, click its icon in the menu bar, or swipe left with two fingers from the right edge of the trackpad."
Like many messaging apps, Signal uses the Notification Center to alert you when a new message has arrived. If you're not actively using the Signal app (i.e. it's not in foreground), the notification will be displayed by the OS in default position; at the top right corner of your screen:
If you do not interact with the notification, the OS will automatically dismiss it from the screen (more on this later!).
Let's look at how one creates such notifications in code.
The NSUserNotification class implements various methods that easily allow one to both create and post notifications to the macOS Notification Center:
//notification
NSUserNotification* notification = nil;
//alloc notification
notification = [[NSUserNotification alloc] init];
//set title
notification.title = @"Aloha 🤙";
//set subtitle
notification.subtitle = [NSString stringWithFormat:@"current time: %@", [NSDate date]];
//deliver notification
[[NSUserNotificationCenter defaultUserNotificationCenter]
deliverNotification:notification];
This code will cause the OS to display an alert, as long as the app is not the foreground ('in focus') application.
As shown in the animated gif, the OS will automatically dismiss the application after a few seconds.
Note: The default type of notification a 'banner', which (as noted here) is a "simple notification view, dismissed after a few seconds."
If an interactive notification is required, one must specify the type 'alert' in the application Info.plist file (key: 'NSUserNotificationAlertStyle').
My 'Do Not Disturb' (DND) application makes use of the 'alert' style notification:
In order to specify the notification type, DND sets the 'NSUserNotificationAlertStyle' key to'alert':
$ grep -A 1 NSUserNotificationAlertStyle /Applications/Do\ Not\ Disturb.app/Contents
/Library/LoginItems/Do\ Not\ Disturb\ Helper.app/Contents/Info.plist
<key>NSUserNotificationAlertStyle</key>
<string>alert</string>
Signal.app on the other handle, does not specify a 'NSUserNotificationAlertStyle' style:
$ grep NSUserNotificationAlertStyle /Applications/Signal.app/Contents/Info.plist | wc
0 0 0
...this means it defaults to the default type of notification; a non-interactive 'banner' (which is auto-dismissed by the OS)
Ok, time to make two relevant points:
- Even if the notification is not shown to the user (because the app is foreground/'in focus', if the application invokes [NSUserNotificationCenter defaultUserNotificationCenter] the notification will end up in the Notification Center.
- The notification is not removed from the Notification Center unless:
Why Disappearing Message Don't Disappear
As noted by Alec, on macOS, Signal's disappearing message may not disappear. And now we know why!
While the application deletes the messages (once the 'disappear' time is hit) from the app's UI - the message may still remain in macOS's Notification Center.
This apparently occurs because:
- Signal displays (posts) a message notification (with the content of the message) to the Notification Center (if the app is not in the foreground).
- The OS automatically dismisses the notification 'banner' ... but the notification (which contains the message contents) remain in the Notification Center.
- Signal, does not explicitly delete this notification when it deletes messages from the app UI.
Note: If the user is actively using Signal.app, the app doesn't post any notifications - so such messages don't end up in the Notification Center.
Moreover, (AFAIK) as notifications limit the amount of text that displayed, message contents may be truncated (but only in the Notification Center):
Back From The Dead!?
Ok, time for some fun! Can we recover 'deleted' Signal messages? ...well, if they have ended up in the Notification Center, then yes!
Notifications have to be stored somewhere ya? To find out where, let's run macOS's built in file monitoring utilty 'fs_usage' while posting a notification:
# fs_usage -w -f filesystem | grep -i notification
...
RdData[ST2] /private/var/folders/l8/jpp20zzn3vl7m5qz_7rjjpq40000gn/0/
com.apple.notificationcenter/db2/db-wal usernoted
...
Looks like the 'user notification daemon' (usernoted) is accessing a file related to a database (specifically a SQLite write-ahead log). Within the same directory ($TMPDIR/../0/com.apple.notificationcenter/db2) are other related database files such as 'db'
Running the 'file' command on the 'db2/db' file reveals (rather unsurprisingly) it's an SQLite database, that is readable with user (i.e. non-root) permissions:
$ file /private/var/folders/l8/.../com.apple.notificationcenter/db2/db
/private/var/folders/l8/.../com.apple.notificationcenter/db2/db: SQLite 3.x database
$ ls -lart /private/var/folders/l8/.../com.apple.notificationcenter/db2/db
-rw-r--r-- 1 patrick staff 4161536 May 6 21:16 db
-rw-r--r-- 1 patrick staff 32768 May 8 17:47 db-shm
-rw-r--r-- 1 patrick staff 3242472 May 9 01:23 db-wal
Let's take a peak:
Lovely, it appears to contain interesting stuff, such as the applications that have posted notifications...including Signal (id: 39):
And what about the 'record' table - that appears to contain the notifications, including their contents:
Unfortunately the data is some 'binary' format. But not to worry, won't stop us:
//bplist00
"data" : "<62706c69 73743030 d8010203 04050607 08090a0b ..."
Converting the hex to ascii, gives us: "bplist00". Ah a binary plist, we can easily decode/parse that in python (using the biplist module)!
from biplist import *
conn = sqlite3.connect(path2db)
conn.row_factory = sqlite3.Row
cursor = conn.execute("SELECT data from record");
for row in cursor:
try:
plist = readPlistFromString(row[0])
print plist
...
And oh f#$@... there's full text of my signal messages (which arrived as notifications). Some which definitely were set to "disappear":
$ cat signalMsgs
...
{
'uuid': '\xef\x1f\x87\...',
'srce': '\xb6\xc2\xa4\x99...',
'app': 'org.whispersystems.signal-desktop',
'req': {
'body': 'you mean for the 0-day?'
'titl': ' /* sender name - redacted */',
'subt': '',
...
'atta': [{
'uti': 'public.image',
'data': '\x89PNG\r\n\x1a\n\x00\x00....',
'pur': 0,
'fam': 0
}]
},
'styl': 1,
'date': 540936980.866426,
'resp': {
'act': 0
},
'orig': 4
}
Conclusion
Well Alec, hope this explains exactly why those 'disappearing' Signal messages still are hanging around. In short, anything that gets displayed as a notification (yes, including 'disappearing' Signal messages) in the macOS Notification Center, is recorded by the OS.
If the application wants the item to be removed from the Notification Center, it must ensure that the alert is dismissed by the user or programmatically! However, it is not clear that this also 'expunges' the notifications (and the their contents) from the notification database...i'm guessing not! If this is the case, Signal may have to avoid generating notifications (containing the message body) for disappearing messages...
Note: Signal's iOS application does not appear to be affected...at least is seems that messages are removed from the iOS Notification Center when viewed in the app. Whether iOS stores notifications similar to macOS should be investigated...
Also, again, only messages that are displayed in notifications are affected. Thus, one way to mitigate this is to turn off notification in the Signal applications (via 'Settings'):
...and maybe (at your own risk!) remove the notification database to truly make the 'disappearing' messages disappear!
Ok bedtime! 😴
p.s. big mahalo to Daniel Cuthbert (@dcuthbert) for unquestioningly sending me many many Signal messages to help me reproduce and understand this issue (while most everybody in the US was sleeping)!
love these blog posts & tools? you can support them via patreon! Mahalo :)