Summary
PeekList: How Brave’s Playlist bypassed FaceID Protection for Private Tabs TL;DR Brave for iOS lets you lock Private Tabs behind Face ID or a device passcode. That protection can be completely bypassed using the built-in Brave Playlist feature. Adding any video or audio to your Playlist and then selecting “Open in a New Private Tab” from the long-press menu opens a Private Tab with zero authentication prompt even when Face ID/Passcode protection is enabled. This means an attacker with brief physical access to an unlocked device could view a victim’s Private Tab content without ever being asked to authenticate. The issue was reported to Brave via HackerOne and has since been addressed. Background Brave’s Private Tabs on iOS include an optional setting that requires Face ID or a device passcode before the app will reveal any open Private Tab, particularly useful when “Keep Private Tabs” is enabled so that private browsing sessions persist between app launches. The entire point of this setting is to stop someone who picks up an already-unlocked phone from casually opening the browser and landing on private browsing content. Brave Playlist is a separate feature that lets a user save audio or video (for example, a YouTube link) for offline-style playback inside the browser. Playlist items support a long-press context menu with several actions, one of which is “Open in a New Private Tab.” That action opens the link directly in a Private Tab, but it does so through a different code path than the normal Private Tab entry point, and that path does not check whether Face ID/Passcode protection is enabled. Affected Environment
- App: Brave Browser for iOS
- Version tested: 1.88
- iOS version tested: 26.4.2
- Feature: Brave Playlist → Private Tab Face ID/Passcode protection Steps To Reproduce: Step 1: Enable FaceID Protection for Private Tabs and Keep Private Tabs in Brave Settings Get Aaron Thomas’s stories in your inbox Join Medium for free to get updates from this writer. Step 2: Open Private Tabs and browse any websites Step 3: Exit out of Private Tabs and add any song or audio to your Brave Playlist. (For this PoC, I used a Youtube video) Step 4: Enter your Brave Playlist and go to the song you just added. Step 5: Tap and hold the song until a dropdown menu appears. Step 6: Tap on the option to “Open in a New Private Tab” The Private Tab will now open immediately. No Face ID prompt or no passcode prompt, nothing standing between the attacker and the private session. A user would have the false sense of security that nobody can access the content of their private tabs session. Impact An attacker with physical access to an unlocked iPhone running Brave may be able to use this Playlist bypass to gain access into the user’s Private Tabs, defeating the entire purpose of the Face ID/Passcode setting. This is exactly the kind of “shoulder surf” or “phone left on the table for thirty seconds” scenario the protection exists to prevent, and the Playlist menu quietly gives an attacker a way around it. The Fix Brave’s patch wraps the Playlist’s “open in new tab” delegate so that, before a private tab is actually opened, it checks whether Private Tab lock is enabled and the user isn’t already in a private session — and if so, gates the tab open behind askForLocalAuthentication (Face ID/Passcode) before proceeding: player: player, delegate: .init( openTabURL: { [weak browserController] url, isPrivate in guard let browserController else { return } let isPrivate = Preferences.Privacy.privateBrowsingOnly.value ? true : isPrivate let openTab: () → Void = { browserController.dismiss(animated: true) browserController.openURLInNewTab( url, isPrivate: isPrivate, isPrivileged: false ) } if isPrivate, !browserController.privateBrowsingManager.isPrivateBrowsing, Preferences.Privacy.privateBrowsingLock.value { browserController.askForLocalAuthentication { success, _ in if success { openTab() } } } else { openTab() } }, onDismissal: { [weak self, weak player] in guard let self else { return } The openTab() closure that actually dismisses the player and calls openURLInNewTab is no longer invoked directly. It’s only reached through the conditional: if the destination tab is private, the browser isn’t already in a private browsing session, and the user has privateBrowsingLock enabled, the tab open is deferred until askForLocalAuthentication returns success — only then does openTab() fire. Previously, openURLInNewTab was called unconditionally from the Playlist delegate with no authentication gate at all, which is exactly the code path this report exploited. Conclusion A protection setting is only as strong as its weakest entry point. Brave’s Face ID/Passcode lock for Private Tabs was solid when Private Tabs were opened normally, but a secondary feature Playlist offered a side door that skipped the check entirely. Brave fixed the issue by extending the same authentication requirement to the Playlist “Open in a New Private Tab” action closing the bypass. Big thank you to the Brave Team for taking my report seriously! :D Link to Report: https://hackerone.com/reports/3693295