跳过内容

MASTG-TECH-0137: 分析 PrivacyInfo.xcprivacy 文件

一旦你如 检索 PrivacyInfo.xcprivacy 文件中所示获取了隐私清单,你就可以继续分析它了。

让我们以 SocialApp.app/PrivacyInfo.xcprivacy 文件为例。

SocialApp.app/PrivacyInfo.xcprivacy
<?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>NSPrivacyAccessedAPITypes</key>
        <array>
                <dict>
                        <key>NSPrivacyAccessedAPIType</key>
                        <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
                        <key>NSPrivacyAccessedAPITypeReasons</key>
                        <array>
                                <string>CA92.1</string>
                                <string>1C8F.1</string>
                                <string>C56D.1</string>
                        </array>
                </dict>
                <dict>
                        <key>NSPrivacyAccessedAPIType</key>
                        <string>NSPrivacyAccessedAPICategoryActiveKeyboards</string>
                        <key>NSPrivacyAccessedAPITypeReasons</key>
                        <array>
                                <string>54BD.1</string>
                        </array>
                </dict>
        </array>
        <key>NSPrivacyCollectedDataTypes</key>
        <array>
                <dict>
                        <key>NSPrivacyCollectedDataType</key>
                        <string>NSPrivacyCollectedDataTypeName</string>
                        <key>NSPrivacyCollectedDataTypeLinked</key>
                        <true/>
                        <key>NSPrivacyCollectedDataTypePurposes</key>
                        <array>
                                <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
                                <string>NSPrivacyCollectedDataTypePurposeOther</string>
                        </array>
                        <key>NSPrivacyCollectedDataTypeTracking</key>
                        <false/>
                </dict>
                <dict>
                        <key>NSPrivacyCollectedDataType</key>
                        <string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
                        <key>NSPrivacyCollectedDataTypeLinked</key>
                        <true/>
                        <key>NSPrivacyCollectedDataTypePurposes</key>
                        <array>
                                <string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
                                <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
                                <string>NSPrivacyCollectedDataTypePurposeOther</string>
                        </array>
                        <key>NSPrivacyCollectedDataTypeTracking</key>
                        <false/>
                </dict>
        </array>
        <key>NSPrivacyTracking</key>
        <true/>
        <key>NSPrivacyTrackingDomains</key>
        <array>
                <string>trk-v2.socialapp.com</string>
                <string>trk-v2.socialapp.us</string>
                <string>trk-v2.socialapp.eu</string>
        </array>
</dict>
</plist>

PrivacyInfo.xcprivacy 文件包含

  • NSPrivacyAccessedAPITypes:列出了应用程序访问的 API 类型及其 访问原因。 在这种情况下
    • NSPrivacyAccessedAPICategoryUserDefaults: UserDefaultsCA92.11C8F.1C56D.1 等原因而被访问。
    • NSPrivacyAccessedAPICategoryActiveKeyboards:与活动键盘的交互因 54BD.1 原因而被访问。
  • NSPrivacyCollectedDataTypes:列出了应用程序收集的数据类型以及特定目的。 它还指示收集的数据是否链接到用户的身份(NSPrivacyCollectedDataTypeLinked),以及是否用于跟踪目的(NSPrivacyCollectedDataTypeTracking)。 在这种情况下
    • NSPrivacyCollectedDataTypeName:收集用户名,目的包括“应用程序功能”和“其他”(链接到用户身份,但不用于跟踪)。
    • NSPrivacyCollectedDataTypeOtherDiagnosticData:收集其他诊断数据,用于“分析”、“应用程序功能”和“其他”等目的(链接到用户身份,但不用于跟踪)。
  • NSPrivacyTracking:表明 SocialApp 使用数据进行跟踪,如应用跟踪透明度框架下所定义的那样。
  • NSPrivacyTrackingDomains:列出了用于跟踪目的的域,在本例中包括各种与 SocialApp 相关的域。

您可以使用多种工具和解析器以编程方式读取和分析这些文件。

使用 jq

如果您如 将 Plist 文件转换为 JSON 中所述将 PrivacyInfo.xcprivacy 文件转换为 JSON 格式,您可以使用 jq 进行查询。

例如,要提取每个 NSPrivacyAccessedAPIType 的所有 NSPrivacyAccessedAPITypeReasons

cat SocialApp.app/PrivacyInfo.json | jq '.NSPrivacyAccessedAPITypes[] | {api: .NSPrivacyAccessedAPIType, reasons: .NSPrivacyAccessedAPITypeReasons}'

输出结果(为便于阅读已截断)

{
  "api": "NSPrivacyAccessedAPICategoryUserDefaults",
  "reasons": [
    "CA92.1",
    "1C8F.1",
    "C56D.1"
  ]
}
{
  "api": "NSPrivacyAccessedAPICategorySystemBootTime",
  "reasons": [
    "35F9.1"
  ]
}
...

优点包括可读的输出、标准的 JSON 工具和简洁的选择语法。 缺点是日期和原始数据 Blob 会变成字符串,数字精度可能会发生变化,并且注释和键顺序会丢失。 如果你需要保留 plist 特定的类型,请考虑使用 Python 的 plistlib 模块。

使用 plistlib

使用 Python 内置的 plistlib 模块读取和操作 plist 文件,包括 PrivacyInfo.xcprivacy

例如,要提取每个 NSPrivacyAccessedAPITypeNSPrivacyAccessedAPITypeReasons

import plistlib
import json

# load the .xcprivacy plist
with open('SocialApp.app/PrivacyInfo.xcprivacy', 'rb') as fp:
    data = plistlib.load(fp)

# extract and print each API and its reasons in JSON
for item in data.get('NSPrivacyAccessedAPITypes', []):
    api = item.get('NSPrivacyAccessedAPIType')
    reasons = item.get('NSPrivacyAccessedAPITypeReasons')
    print(json.dumps({'api': api, 'reasons': reasons}, ensure_ascii=False))

输出为(为便于阅读已截断)

{"api": "NSPrivacyAccessedAPICategoryUserDefaults", "reasons": ["CA92.1", "1C8F.1", "C56D.1"]}
{"api": "NSPrivacyAccessedAPICategorySystemBootTime", "reasons": ["35F9.1"]}
...

使用 PlistBuddy

使用 PlistBuddy 直接读取和操作 plist 文件,无需将其转换为 JSON,包括 PrivacyInfo.xcprivacy

例如,您可以使用以下命令读取 NSPrivacyAccessedAPITypes

/usr/libexec/PlistBuddy -c "Print NSPrivacyAccessedAPITypes" ./SocialApp.app/PrivacyInfo.xcprivacy
Array {
    Dict {
        NSPrivacyAccessedAPIType = NSPrivacyAccessedAPICategoryUserDefaults
        NSPrivacyAccessedAPITypeReasons = Array {
            CA92.1
            1C8F.1
            C56D.1
        }
    }
    ...
}

您可以更深入地研究该文件以提取更具体的信息。例如,您可以通过以下方式获取第一个 NSPrivacyAccessedAPITypes 元素(索引 0)的 NSPrivacyAccessedAPITypeReasons

/usr/libexec/PlistBuddy -c "Print NSPrivacyAccessedAPITypes:0:NSPrivacyAccessedAPITypeReasons" ./SocialApp.app/PrivacyInfo.xcprivacy

Array {
    CA92.1
    1C8F.1
    C56D.1
}