CVE-2026-78957
Overview
Background
- App group container
- a shared on-disk directory that Chrome for iOS uses to exchange data with its extensions (here, widgets), including per-account avatar files.
- iCloud / device backup
- iOS by default includes files in an app’s (and app group’s) containers in iCloud backups unless they are marked with the “exclude from backup” resource attribute.
- `base::apple::SetBackupExclusion`
- a Chromium helper that sets the iOS/macOS backup-exclusion attribute on a file path so it is not uploaded to backups.
- Gaia ID
- the stable Google account identifier; the commit message states the widget avatar files contain it (the fix commit says “The file contain gaiaID”).
Root Cause Analysis
Chrome for iOS writes signed-in account avatar images into the app group container for widget use, in system_account_updater.mm and via a picture-data URL helper in app_group_helper.mm (the enclosing function names are not shown in the hunks). These files were written with writeToURL:atomically: (or their target URL returned) without setting the backup-exclusion attribute, so iOS would include them in iCloud backups by default. Per the commit message, the files contain the account’s Gaia ID, so their presence in backups uploads a Google account identifier (and avatar) off-device outside Chrome’s control.
The fix calls base::apple::SetBackupExclusion(base::apple::NSURLToFilePath(...)) after each successful avatar write in system_account_updater.mm (in one place converting a previously ignored writeToURL: result into an if), and on the returned pictureDataURL in app_group_helper.mm, so these files are excluded from backup.
SetBackupExclusion.Attack Path
- User signs in on iOS A user signs in to Chrome for iOS with a Google account, causing the account avatars to be written into the app group container for widgets.
- Files written without exclusion Pre-fix, the avatar files (which per the commit contain the Gaia ID) were written with no backup-exclusion attribute.
- Device backup uploads them With iCloud backup enabled on the device, iOS includes these files in the backup, copying the Gaia ID and avatar data to iCloud storage.
- Exposure via backup access A party able to read the device backup (e.g. via access to the user’s backup or Apple account) could recover the Google account identifier (inferred); no network or web-content attacker path is shown by the patch.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifios/chrome/browser/signin/model/system_account_updater.mm |
modified |
Files Changed
ios/chrome/browser/signin/model/system_account_updater.mmios/chrome/common/app_group/app_group_helper.mm
Audit Directions
- Unexcluded writes to app group containersaudit all
writeToURL:/writeToFile:/NSFileManagerwrites underios/chrome/common/app_group/and extension-shared paths for files containing account identifiers, emails, or browsing data that lackSetBackupExclusion. - Exclusion set only on some write pathswhere the same file is written from multiple code paths (bulk update vs. single update, create vs. replace), confirm every path applies the exclusion, since atomic writes replace the file and may drop prior attributes.
- Directory-level vs. file-level exclusioncheck whether helpers that return a directory or URL (like
pictureDataURL) are excluded at creation, and whether files later created inside them inherit the intended backup policy.
Patch
From d0d5365551873437d63fd49fc63ea55560d6fecc Mon Sep 17 00:00:00 2001
From: Olivier Robin <olivierrobin@google.com>
Date: Fri, 10 Jul 2026 08:58:13 -0700
Subject: [PATCH] Exclude Widget app group avatar from iCloud backup
The file contain gaiaID so uploading them to iCloud can be considered
as a leak.
Fixed: 533123348
Change-Id: Id2767c82b69f8263c17b006bafd460264db19619
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8074063
Commit-Queue: Federica Germinario <fedegermi@google.com>
Auto-Submit: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: Gauthier Ambard <gambard@chromium.org>
Reviewed-by: Federica Germinario <fedegermi@google.com>
Cr-Commit-Position: refs/heads/main@{#1660314}
---
diff --git a/ios/chrome/browser/signin/model/system_account_updater.mm b/ios/chrome/browser/signin/model/system_account_updater.mm
index 767c9ed..b6bdcf1 100644
--- a/ios/chrome/browser/signin/model/system_account_updater.mm
+++ b/ios/chrome/browser/signin/model/system_account_updater.mm
@@ -4,6 +4,8 @@
#import "ios/chrome/browser/signin/model/system_account_updater.h"
+#import "base/apple/backup_util.h"
+#import "base/apple/foundation_util.h"
#import "base/barrier_callback.h"
#import "base/check_deref.h"
#import "base/check_is_test.h"
@@ -159,6 +161,7 @@
if (data) {
NSURL* path = info.avatar_path(avatar_folder);
if ([data writeToURL:path atomically:YES]) {
+ base::apple::SetBackupExclusion(base::apple::NSURLToFilePath(path));
[avatars addObject:path];
}
}
@@ -187,7 +190,9 @@
NSURL* path = info.avatar_path(avatar_folder);
if (NSData* data = info.avatar_data()) {
- [data writeToURL:path atomically:YES];
+ if ([data writeToURL:path atomically:YES]) {
+ base::apple::SetBackupExclusion(base::apple::NSURLToFilePath(path));
+ }
} else {
NSFileManager* manager = [NSFileManager defaultManager];
[manager removeItemAtURL:path error:nil];
diff --git a/ios/chrome/common/app_group/app_group_helper.mm b/ios/chrome/common/app_group/app_group_helper.mm
index 5f11bb5..c52b3cc 100644
--- a/ios/chrome/common/app_group/app_group_helper.mm
+++ b/ios/chrome/common/app_group/app_group_helper.mm
@@ -4,7 +4,9 @@
#import "ios/chrome/common/app_group/app_group_helper.h"
+#import "base/apple/backup_util.h"
#import "base/apple/bundle_locations.h"
+#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "ios/chrome/common/ios_app_bundle_id_prefix_buildflags.h"
@@ -70,6 +72,7 @@
error:nil]) {
return nil;
}
+ base::apple::SetBackupExclusion(base::apple::NSURLToFilePath(pictureDataURL));
return pictureDataURL;
}
Original Bug Report
Potential backup leak of unhashed Gaia IDs and avatars via iOS App Group container
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: In Chrome for iOS, the widgets avatar folder is created and written to within the shared App Group container without backup exclusion attributes. As a result, the stable, unhashed Google Gaia IDs and avatar image files of all signed-in Google accounts are included in iCloud and unencrypted local backups. Anyone who obtains access to these backups can deanonymize the device owner and other linked accounts.
Affected files:
ios/chrome/common/app_group/app_group_helper.mmios/chrome/browser/signin/model/system_account_updater.mm
Estimated timestamp from git blame: 2025-02-10
Description
On iOS, files written to the shared App Group container are included in iCloud and iTunes/Finder backups by default unless they are explicitly marked with the backup exclusion attribute (NSURLIsExcludedFromBackupKey) or placed in specific non-backed-up directories (like Library/Caches or tmp).
Chrome for iOS creates a folder at {AppGroup}/Chrome/AvatarData/ to cache 32x32 PNG avatar images of signed-in accounts for widgets. However, during the creation of this folder and the subsequent file writes, the backup exclusion attribute is never set.
Furthermore, the avatar files are named using the user’s unhashed, cleartext Google Gaia ID (e.g., {GaiaID}.png), and contain their profile picture. This allows any adversary with access to device backups to deanonymize the user and identify any work, personal, or secondary Google accounts signed into the browser.
Code Analysis
1. Folder Creation without Exclusion
In ios/chrome/common/app_group/app_group_helper.mm:54-74, the directory is created but is not excluded from backups:
+ (NSURL*)widgetsAvatarFolder {
NSString* applicationGroup = [AppGroupHelper applicationGroup];
if (!applicationGroup) { return nil; }
NSURL* groupURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:applicationGroup];
NSURL* chromeURL = [groupURL URLByAppendingPathComponent:@"Chrome" isDirectory:YES];
NSURL* pictureDataURL = [chromeURL URLByAppendingPathComponent:@"AvatarData"
isDirectory:YES];
if (![[NSFileManager defaultManager] createDirectoryAtPath:pictureDataURL.path
withIntermediateDirectories:YES
attributes:nil error:nil]) {
return nil;
}
return pictureDataURL; // <-- Missing base::apple::SetBackupExclusion()
}
2. Filename is the Cleartext Gaia ID
In ios/chrome/browser/signin/model/system_account_updater.mm:91-95, the filename is constructed directly using the cleartext Gaia ID:
NSURL* avatar_path(NSURL* directory) const {
return [directory URLByAppendingPathComponent:
[gaia_id_.ToNSString() stringByAppendingPathExtension:@"png"]];
}
3. Writing Files without Exclusion
In ios/chrome/browser/signin/model/system_account_updater.mm:147-177, the avatar image data is written atomically to disk without applying the backup exclusion key to individual files:
for (const SystemIdentityInfoData& info : list) {
NSData* data = info.avatar_data();
if (data) {
NSURL* path = info.avatar_path(avatar_folder);
if ([data writeToURL:path atomically:YES]) {
[avatars addObject:path];
}
}
}
Potential Trigger Path (Suggested Steps)
Note: The following steps are potential and suggested; our tooling does not currently have the capability to execute live code to verify this Proof of Concept.
- A user launches Chrome on iOS and signs into one or more Google accounts.
- Upon startup or identity change,
SystemAccountUpdateris initialized unconditionally, registering as an observer ofSystemIdentityManager. SystemAccountUpdater::UpdateLoadedAccounts()retrieves the active identities and schedules a background task runningWriteAvatars.WriteAvatarscreates{AppGroup}/Chrome/AvatarData/and writes{GaiaID}.pngfiles into it.- The user backs up the device via iCloud or performs an unencrypted backup via iTunes/Finder on a computer.
- An attacker gains access to the backup, extracts the App Group container contents, and locates the folder
Chrome/AvatarData/to recover the unhashed Gaia IDs and profile photos of the signed-in accounts.
Suggested Fix
Exclude the AvatarData directory from backups once it is created in +[AppGroupHelper widgetsAvatarFolder]. Import base/apple/backup_util.h and call base::apple::SetBackupExclusion on the folder URL:
#import "base/apple/backup_util.h"
#import "base/apple/foundation_util.h"
// ... inside +[AppGroupHelper widgetsAvatarFolder] ...
if (![[NSFileManager defaultManager] createDirectoryAtPath:pictureDataURL.path
withIntermediateDirectories:YES
attributes:nil error:nil]) {
return nil;
}
base::apple::SetBackupExclusion(base::apple::NSURLToFilePath(pictureDataURL));
return pictureDataURL;
Evaluated with Chrome root at commit: 84065d9121f6e48f67755f0ae963cc09617e5c85
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.