Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions best-practices/MASTG-BEST-0025.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: Secure Data Sharing Between App Extensions and Containing Apps
alias: secure-app-extension-data-sharing
id: MASTG-BEST-0025
platform: ios
---

When using [App Groups](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups) to share data between an app and its extensions, you must protect sensitive information from unauthorized access. App Groups create a shared container accessible to all members of the group, which can expose data if not properly secured.

## Encrypt Sensitive Data in Shared Containers

Store sensitive data in the shared container only when necessary. When you must share sensitive information, encrypt it before writing to the shared container and decrypt it only when needed. Use platform encryption APIs such as:

- [CryptoKit](https://developer.apple.com/documentation/cryptokit) (iOS 13+) for modern encryption operations.
- [CommonCrypto](https://developer.apple.com/security/) for legacy support or lower-level cryptographic operations.

Store encryption keys in the iOS Keychain with appropriate accessibility attributes (e.g., [`kSecAttrAccessibleWhenUnlockedThisDeviceOnly`](https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlockedthisdeviceonly)) and ensure that the Keychain items are shared via a [Keychain Access Group](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps) if both the app and extension need access to the same key.

## Minimize Data Sharing

Limit the amount of data stored in the shared container to only what is necessary for the extension's functionality. Avoid storing entire datasets when the extension only needs a subset of information. Design your data-sharing architecture to pass minimal data and retrieve additional details from a secure source (e.g., a server or the app's protected container) when needed.

## Use Keychain Sharing for Credentials

For credentials and other highly sensitive data, prefer the Keychain over the shared container. The Keychain provides stronger access controls and encryption. Configure a [Keychain Access Group](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps) to allow the app and extension to share Keychain items securely.

## Apply Appropriate File Protection

When storing files in the shared container, apply [Data Protection](https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/encrypting_your_app_s_files) to encrypt file contents. Set the appropriate protection class (e.g., `NSFileProtectionComplete`) to ensure files are encrypted when the device is locked.

```swift
try data.write(to: sharedContainerURL, options: .completeFileProtection)
```

## Validate Data Integrity

When reading data from the shared container, validate its integrity to ensure it has not been tampered with. Use message authentication codes (MACs) or digital signatures to verify that the data originates from a trusted source and has not been modified.

## Review Extension Permissions

Ensure that each app extension only has access to the App Groups it requires. Avoid granting all extensions access to the same shared container unless necessary. Configure each extension's entitlements to include only the specific App Group identifiers needed for its functionality.

## References

- [Apple Developer Documentation - App Extensions](https://developer.apple.com/app-extensions/)
- [Apple Developer Documentation - App Groups](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups)
- [Apple Developer Documentation - Sharing Data with Your Containing App](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW11)
- [Apple Developer Documentation - Data Protection](https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/encrypting_your_app_s_files)
- [Apple Developer Documentation - Keychain Access Groups](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps)
56 changes: 56 additions & 0 deletions demos/ios/MASVS-PLATFORM/MASTG-DEMO-0068/MASTG-DEMO-0068.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
platform: ios
title: Identifying Data Sharing Between App and Extensions via App Groups
id: MASTG-DEMO-0068
code: [swift]
test: MASTG-TEST-0291
---

### Sample

The code snippet below demonstrates an app that uses App Groups to share data with its app extension. The app stores sensitive user information (email, authentication tokens, API keys) in shared storage that can be accessed by both the main app and the extension.

{{ MastgTest.swift }}

The app includes a Share Extension with the following configuration files:

Extension Info.plist showing the extension type and supported data types:

{{ ShareExtension_Info.plist }}

Entitlements file showing the App Groups configuration:

{{ entitlements.plist }}

### Steps

1. Extract the app package (@MASTG-TECH-0058) and locate the `PlugIns/` folder containing app extensions (`.appex` files).
2. For each extension, examine the `Info.plist` file to identify the extension type via `NSExtensionPointIdentifier` and supported data types via `NSExtensionActivationRule`.
3. Check the entitlements file (or `embedded.mobileprovision`) for the `com.apple.security.application-groups` entitlement in both the main app and extensions.
4. Review the app's code (@MASTG-TECH-0076) to identify usage of shared storage APIs such as `UserDefaults(suiteName:)` and `FileManager.containerURL(forSecurityApplicationGroupIdentifier:)`.
5. Run the analysis script to detect these patterns:

{{ run.sh }}

### Observation

The output shows:

- The presence of an app extension (Share Extension) with identifier `com.apple.share-services`
- The extension supports text, URLs, and images as input data types
- Both the app and extension use the App Group `group.org.owasp.mastestapp`
- The code accesses shared storage at multiple locations using the App Group identifier
- Sensitive data (email addresses, authentication tokens, subscription info) is stored in shared UserDefaults
- A credentials file containing API keys and refresh tokens is written to the shared container

{{ output.txt }}

### Evaluation

The test fails because the app stores sensitive data in the App Group shared container without encryption:

- **Shared UserDefaults**: Contains `userEmail`, `authToken`, and `subscriptionLevel` in plaintext
- **Shared Files**: The `user_credentials.json` file in the shared container contains `username`, `apiKey`, and `refreshToken` in plaintext
- **Weak Protection**: No evidence of encryption or access controls protecting the sensitive data in the shared storage

Any app extension with access to `group.org.owasp.mastestapp` can read this sensitive information. According to @MASTG-BEST-0025, sensitive data shared via App Groups should be encrypted, and access should be minimized to only what each extension requires for its functionality.
52 changes: 52 additions & 0 deletions demos/ios/MASVS-PLATFORM/MASTG-DEMO-0068/MastgTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import SwiftUI
import os

// SUMMARY: This sample demonstrates data sharing between an app and its extension using App Groups.

struct MastgTest {

static func mastgTest(completion: @escaping (String) -> Void) {
// FAIL: [MASTG-TEST-0291] The app shares sensitive data via App Groups without encryption

let appGroupID = "group.org.owasp.mastestapp"

// Store sensitive user data in shared UserDefaults
if let sharedDefaults = UserDefaults(suiteName: appGroupID) {
// Storing sensitive information in shared UserDefaults
sharedDefaults.set("[email protected]", forKey: "userEmail")
sharedDefaults.set("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", forKey: "authToken")
sharedDefaults.set("Premium", forKey: "subscriptionLevel")
sharedDefaults.synchronize()

completion("Shared data stored in App Group: \(appGroupID)")
} else {
completion("Failed to access App Group")
}

// Write sensitive file to shared container
let fileManager = FileManager.default
if let containerURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) {
let fileURL = containerURL.appendingPathComponent("user_credentials.json")
let credentials = """
{
"username": "john.doe",
"apiKey": "sk-1234567890abcdef",
"refreshToken": "rt-abcdef1234567890"
}
"""

do {
try credentials.write(to: fileURL, atomically: true, encoding: .utf8)
completion("Sensitive file written to shared container: \(fileURL.path)")
} catch {
completion("Error writing file: \(error.localizedDescription)")
}
}

// PASS: [MASTG-TEST-0291] Non-sensitive data can be safely shared
if let sharedDefaults = UserDefaults(suiteName: appGroupID) {
sharedDefaults.set("en-US", forKey: "preferredLanguage")
sharedDefaults.set("light", forKey: "themeMode")
}
}
}
41 changes: 41 additions & 0 deletions demos/ios/MASVS-PLATFORM/MASTG-DEMO-0068/ShareExtension_Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Share Extension</string>
<key>CFBundleExecutable</key>
<string>ShareExtension</string>
<key>CFBundleIdentifier</key>
<string>org.owasp.mastestapp.ShareExtension</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>ShareExtension</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>
14 changes: 14 additions & 0 deletions demos/ios/MASVS-PLATFORM/MASTG-DEMO-0068/entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.org.owasp.mastestapp</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.owasp.mastestapp</string>
</array>
</dict>
</plist>
39 changes: 39 additions & 0 deletions demos/ios/MASVS-PLATFORM/MASTG-DEMO-0068/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
=== Checking for App Extensions ===

Looking for NSExtensionPointIdentifier in Info.plist files...
37: <key>NSExtensionPointIdentifier</key>

=== Extension Type and Activation Rules ===

Extension type:
<string>com.apple.share-services</string>

Supported data types:
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>

=== Checking for App Groups Entitlement ===

<key>com.apple.security.application-groups</key>
<array>
<string>group.org.owasp.mastestapp</string>
</array>

=== Checking Code for Shared Storage API Usage ===

UserDefaults with suiteName (shared UserDefaults):
14: if let sharedDefaults = UserDefaults(suiteName: appGroupID) {
47: if let sharedDefaults = UserDefaults(suiteName: appGroupID) {

FileManager containerURL (shared container access):
28: if let containerURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) {

=== Identifying Sensitive Data in Shared Storage ===

Sensitive data stored in shared UserDefaults:
sharedDefaults.set("[email protected]", forKey: "userEmail")
sharedDefaults.set("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", forKey: "authToken")
sharedDefaults.set("Premium", forKey: "subscriptionLevel")

Files written to shared container:
43 changes: 43 additions & 0 deletions demos/ios/MASVS-PLATFORM/MASTG-DEMO-0068/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash

# This script demonstrates static analysis of app extensions and App Groups usage

echo "=== Checking for App Extensions ==="
echo ""
echo "Looking for NSExtensionPointIdentifier in Info.plist files..."
grep -n "NSExtensionPointIdentifier" ShareExtension_Info.plist

echo ""
echo "=== Extension Type and Activation Rules ==="
echo ""
echo "Extension type:"
grep -A 1 "NSExtensionPointIdentifier" ShareExtension_Info.plist | tail -1

echo ""
echo "Supported data types:"
grep -A 3 "NSExtensionActivationRule" ShareExtension_Info.plist | tail -3

echo ""
echo "=== Checking for App Groups Entitlement ==="
echo ""
grep -A 3 "com.apple.security.application-groups" entitlements.plist

echo ""
echo "=== Checking Code for Shared Storage API Usage ==="
echo ""
echo "UserDefaults with suiteName (shared UserDefaults):"
grep -n "UserDefaults(suiteName:" MastgTest.swift

echo ""
echo "FileManager containerURL (shared container access):"
grep -n "containerURL(forSecurityApplicationGroupIdentifier:" MastgTest.swift

echo ""
echo "=== Identifying Sensitive Data in Shared Storage ==="
echo ""
echo "Sensitive data stored in shared UserDefaults:"
grep -B 1 -A 0 'set.*forKey' MastgTest.swift | grep -E "(Email|Token|subscription|apiKey|refreshToken)"

echo ""
echo "Files written to shared container:"
grep -B 2 'write(to: fileURL' MastgTest.swift | grep 'let credentials'
52 changes: 52 additions & 0 deletions tests-beta/ios/MASVS-PLATFORM/MASTG-TEST-0291.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
platform: ios
title: Data Sharing Between App Extensions and Containing Apps
id: MASTG-TEST-0291
type: [static, manual]
weakness: MASWE-0053
threat: [app]
prerequisites:
- identify-sensitive-data
best-practices: [MASTG-BEST-0025]
profiles: [L1, L2]
---

## Overview

iOS [app extensions](https://developer.apple.com/app-extensions/) allow apps to extend custom functionality and content beyond the app. They are separate binaries bundled with the app, each serving a specific purpose, such as sharing content, providing widgets, or integrating with other apps.

App extensions and their containing apps run as separate processes with distinct sandboxes and cannot directly access each other's containers. However, they can share data via [App Groups](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups), which enable shared storage. When an app and its extensions are configured to use the same App Group, they can read from and write to a shared container, making data accessible across the extension and the containing app.

This test verifies whether the app contains app extensions and checks if they share sensitive data with the containing app via App Groups. Insecure data sharing can expose sensitive information to other apps or processes, particularly if the shared container does not implement adequate access controls or encryption.

## Steps

1. Extract the app package and use @MASTG-TECH-0058 to explore the contents of the IPA file.
2. Verify if the app contains app extensions by checking for the presence of a `PlugIns/` folder inside the app bundle (`.app` directory). Each app extension will have a `.appex` extension.
3. For each detected app extension, inspect the extension's `Info.plist` file to determine:
- The extension type via the [`NSExtensionPointIdentifier`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsextension/nsextensionpointidentifier) key, which identifies the functionality the extension provides (e.g., share extension, widget, custom keyboard).
- The supported data types via the [`NSExtensionActivationRule`](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW8) key (for share and action extensions), which specifies the types and amounts of data the extension can handle.
4. Check both the containing app and each app extension for the presence of the [App Groups entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups) (`com.apple.security.application-groups`) in their respective entitlements files or embedded provisioning profiles (`embedded.mobileprovision`). This entitlement indicates that data sharing is enabled between the app and its extensions.
5. Review the app's and extension's code using @MASTG-TECH-0076 to identify usage of shared storage APIs:
- [`UserDefaults(suiteName:)`](https://developer.apple.com/documentation/foundation/userdefaults/1409957-init) to access shared user defaults.
- [`FileManager.containerURL(forSecurityApplicationGroupIdentifier:)`](https://developer.apple.com/documentation/foundation/filemanager/1412643-containerurl) to access shared file containers.
- [`NSPersistentContainer`](https://developer.apple.com/documentation/coredata/nspersistentcontainer) with App Group configurations for shared Core Data storage.

## Observation

The output should contain:

- A list of app extensions (`.appex` files) found in the `PlugIns/` directory.
- The extension type and supported data types for each extension identified in their `Info.plist` files.
- Confirmation of whether the App Groups entitlement is present in the containing app and each extension.
- Locations in the disassembled code where shared storage APIs are used.

## Evaluation

The test case fails if:

- The app and its extensions use the App Groups entitlement to share data via a common container, and sensitive data is stored in the shared container without adequate protection (e.g., encryption, access controls).
- Sensitive data can be accessed by any extension with the same App Group, even if the extension does not require access to that data for its intended functionality.
- Shared user defaults or shared file containers contain sensitive information in plaintext or with insufficient protection.

Determining what constitutes sensitive data is context-dependent. Review the identified code locations in the disassembled code to assess whether shared data includes sensitive information and whether appropriate safeguards are in place. Consider the functionality of each extension and whether the data sharing is necessary and minimized.
Loading