先看结论与判断条件

  • 先定义具体 AI 功能需要什么内容,再打开文件选择器;不能以未来可能使用为由收集整份附件和全部元数据。
  • 文件正文、容器属性、EXIF、文件名、批注、隐藏页、嵌入对象和缩略图属于不同数据面,应逐项决定保留或删除。
  • 预处理应产生新的最小化上传副本,原文件保持只读;副本、OCR、切片和预览必须绑定同一附件身份与删除策略。
  • 用户确认需要展示发送内容、目的、接收方和保留边界,取消、切换账号或页面销毁后不得继续上传迟到任务。
  • 访问令牌只解决调用授权,不能证明附件收集合理;scope、audience、重放防护和上传数据范围必须分别检查。
  • 商店与隐私清单要覆盖 App 和第三方 SDK 的真实数据行为,但声明不能替代运行时最小化、日志脱敏和删除回执。

文件选择器打开前先写清上传目的

附件最小化的起点不是压缩图片,而是回答这个 AI 功能究竟需要什么。识别票据金额可能只需要用户框选的图像区域,文档问答可能只需要所选页面的纯文本,图像描述可能不需要原文件名、拍摄位置或设备信息。若产品团队无法写出目的、输入字段、接收方、处理阶段和删除条件,就没有依据判断哪些数据必须上传。此时继续收集整份文件,只会把未来可能使用变成无限范围。

目的应细化到可执行的数据清单。每个字段记录 required、optional 或 forbidden,注明它来自文件正文、容器元数据、操作系统属性还是预处理派生物。optional 字段不能默认勾选;forbidden 字段在选择后立即删除或不读取。清单还要写出 AI 服务、内容审核、对象存储、崩溃分析和客服系统是否会接触附件,不能只写一个笼统的云端处理。第三方 SDK 的读取也属于应用的数据责任。

需求变化必须产生新的清单版本和用户界面,而不是在服务器端悄悄扩展使用。旧客户端若不认识新用途,应保持原范围或拒绝上传。一次确认只覆盖当次说明的附件和处理目的,不能自动延伸到模型训练、质量分析或长期留存。若具体业务存在法定、合同或监管要求,仍需由项目合规人员确认;公开工程方法不能替代特定地区的法律结论。

附件上传前的目的与字段决策
决策项需要记录不合格表现门禁动作
功能目的单一可说明任务未来可能使用拒绝进入上传流程
必要内容正文区域或页面范围默认整份文件要求缩小选择
元数据逐字段保留理由全部原样上传按 allowlist 重建
接收方服务与 SDK 清单只写云端补齐数据流图
保留期到期事件与删除主体永久或未定义阻断发布
用户选择当次明确确认后台静默追加取消任务并记录

把正文、容器和元数据拆成三个数据面

用户看到的一张图片或一个文档,内部可能包含远多于可见正文的信息。图片可能带有拍摄时间、位置、设备、方向和缩略图;文档可能包含作者、修订记录、批注、隐藏内容、嵌入对象、附件或自定义属性;文件系统还会提供原始文件名、路径、大小和修改时间。不能因为用户选择了文件,就推定其同意发送所有内部与旁路字段。最小化流程应先解析类型,再对每个数据面使用独立规则。

格式识别不能只依赖扩展名或调用方给出的 MIME。预处理器应读取公开格式签名并设置支持范围,不认识的容器直接拒绝或提示用户转换。复杂文档如果无法可靠删除隐藏内容,就提供页面导出、纯文本提取或用户预览,而不是声称已经清理。安全边界包括资源消耗限制、解析失败和嵌套对象数量,但公开示例不提供攻击链或恶意样本制作方法。

最小化后的上传对象最好是新生成的受控副本。副本只保留允许的可见内容与必要格式属性,使用随机内部标识替代原文件名,并绑定 manifestVersion、purposeId、accountId 和 attachmentId。原文件保持只读,不在失败后覆盖。副本摘要用于重试和删除关联,不用于宣称内容真实或完整;若业务需要原件证明,应由另一个明确流程处理,而不能让 AI 附件通道承担证据存档。

常见附件数据面的最小化处理
数据面示例内容默认策略允许保留的条件
可见正文选中区域或页面按任务裁剪直接服务当前目的
图片元数据时间、位置、设备删除功能明确需要并已告知
文档属性作者、修订、批注删除或单独确认用户明确选择
嵌入对象附件、媒体、脚本拒绝或剥离受控解析且任务需要
文件系统属性文件名、路径、时间不上传有限诊断标识
预览缩略图本地显示副本会话结束删除仍在当前会话使用

预处理产生的每一份派生物都要登记

AI 附件通常会经过压缩、旋转、裁剪、OCR、分块、向量化、缩略图生成和格式转换。团队常只管理最终上传文件,却忽略处理中间目录、内存缓存、任务队列、数据库记录和失败快照。数据最小化清单必须把派生物视为附件的一部分,为每个对象记录来源、用途、存储位置类别、加密状态、保留条件和删除责任。没有来源 attachmentId 的派生物不能进入队列。

处理顺序影响暴露面。可以先在设备侧完成方向修正、用户裁剪和元数据删除,再生成上传副本;OCR 若可在端侧满足目的,则只发送必要文本和引用位置。是否使用端侧或云端取决于模型能力、设备、准确性与业务要求,本文不宣称某条路径始终更安全或更快。关键是每个决策都有清单版本、用户可见说明和失败边界。

中间对象不能用同一固定文件名在多个账号或任务间复用。目录与缓存键至少绑定账号域、attachmentId、generation 和处理阶段,异步任务在写入前再次检查当前 generation。用户重新选择文件、切换账号或撤销任务后,旧 generation 只能执行删除,不能把迟到 OCR 或压缩结果登记到新附件。进程退出后的恢复也要读取同一 manifest,而不是扫描目录猜测文件用途。

附件派生物的登记字段
派生物用途身份绑定删除触发
裁剪副本限定可见区域attachmentId 与 cropVersion上传完成或取消
压缩图片降低传输负载generation 与编码参数任务终态
OCR 文本检索或提示输入页面与文本摘要会话或保留期结束
文档切片分段处理页码与 chunkId父附件删除
预览缩略图用户确认账号与会话页面销毁
失败快照可控诊断错误类别与脱敏摘要诊断窗口结束

用户确认要对应具体内容、接收方和时间

确认界面要让用户知道将发送哪一份内容,而不是只显示文件图标。图片应展示裁剪后的预览,文档应显示所选页面或抽取文本范围;元数据若保留,要列出类别和原因。界面同时说明接收的是本地组件还是远端 AI 服务,是否经过内容审核或第三方 SDK,以及预期保留边界。用户返回重新选择后,旧确认失效,不能沿用旧附件的 consentId。

取消语义要贯穿文件读取、预处理、授权、上传、服务端处理和结果回写。点击取消只改变按钮状态是不够的;客户端要停止新分片、撤销可撤销请求、标记服务端对象待删除,并阻止迟到结果进入会话。无法立即中止的阶段需明确说明最终删除回执,而不是把“已取消”解释成所有副本瞬间消失。网络断开或应用退到后台也不能自动变成永久重试。

批量附件需要逐项身份与选择。用户同意上传文档甲,不表示文档乙及其全部页面也被授权;删除其中一项后,提示词、OCR、向量和重试队列都要移除对应引用。产品若支持重新提交,应创建新的 generation 和 consentId,并复用最小化规则,而不是直接从旧临时文件重发。这样审计可以回答某个对象为什么存在、由谁确认和何时到期。

附件确认与取消的状态机
状态允许动作必须展示离开状态的条件
selected本地读取与预览原文件与范围完成最小化
minimized检查派生物实际发送副本用户确认
authorized申请限权调用接收方与保留期开始上传
uploading发送已确认分片进度与取消成功、失败或取消
processing等待服务结果远端处理状态结果或删除请求
cancelled删除与回执尚未删除的边界全部对象终态

授权令牌与附件最小化是两道不同门禁

访问令牌说明客户端在某个授权上下文中可以调用接口,不说明把整份附件发送出去就是合理的。scope、audience、调用者身份、用户账号和附件 purposeId 应分别进入请求门禁。令牌权限过宽会扩大误用后果,附件范围过宽会扩大数据暴露;两者都通过才能发送。不要把长期 API Key 放在移动端,也不要把 token 文本写入附件 manifest、文件名或诊断日志。

RFC 9700 针对 OAuth 部署强调重定向、授权码注入、令牌重放和不安全授权流程等风险。附件通道可以据此检查授权交互、令牌受众和重放防护,但规范不决定 AI 服务的商业权限、数据保留或内容处理规则。客户端还需要业务层的 attachmentId、requestId 和一次性 generation,让网络重试只能引用同一已确认副本,不能重新读取更大范围的原文件。

失败重试必须保留数据边界。若服务器返回可重试错误,队列只能发送原 manifest 对应的最小化副本和字段摘要;manifestVersion、consentId、candidateDigest 或账号域变化时,任务作废并重新确认。刷新令牌不应顺便扩大附件 scope。服务端也要拒绝同 requestId 对应不同附件摘要,具体幂等和认证实现需要目标系统证据,公开文章不声称御盾中央平台或任何 AI 服务已经采用该设计。

附件上传的双重授权检查
检查面关键字段能证明不能证明
OAuth 授权scope、audience、subject调用资格受限附件收集合理
附件目的purposeId、manifestVersion当前处理范围用户已确认
用户选择consentId、generation当次副本获确认长期留存合理
请求绑定requestId、附件摘要重试引用同一对象服务端已删除
账号域accountId、tenantId避免跨账号队列数据用途合法
删除回执objectId、状态、时间已执行声明动作所有备份立即消失

保留期必须覆盖远端对象、重试副本和备份边界

保留期不能只写在隐私政策里。附件 manifest 要包含 retentionClass、expiresAt、deleteOnCompletion 和 legalHoldPolicy 等可执行字段,并指定客户端临时文件、上传分片、对象存储、OCR 文本、向量、结果缓存和诊断材料各自由谁删除。永久、默认长期或空值都应被门禁拒绝;若法规或合同要求特殊保留,应进入单独授权流程,而不是复用普通 AI 会话设置。

删除是传播任务,不是单一文件操作。父附件进入 deleted 后,派生图中的缩略图、文本、切片、向量、缓存、重试队列和引用记录都要达到终态;正在运行的任务先失效 generation,再执行清理,避免删除后又被迟到任务重建。离线设备可在下次启动补传删除请求,但要保存最少的对象标识和回执状态,不能为了删除而长期保留原内容。

备份和日志往往有不同保留机制。项目需要明确在线删除与备份过期之间的边界,并向用户使用准确措辞。日志只保留附件摘要、阶段、错误类别和策略版本,不记录正文、文件名、访问令牌、远端 URL 查询参数或完整 OCR。若诊断必须取样,使用专用的脱敏、审批和过期流程,不能让通用崩溃 SDK 自动抓取输入对象。

附件对象的保留与删除责任
对象最短必要保留删除责任回执边界
本地最小化副本上传或任务终态客户端任务文件与索引均移除
上传分片完成组装前上传服务过期或主动清理
远端原对象当前处理窗口对象服务在线对象删除
OCR 与切片推理或检索窗口处理服务父附件级联
结果缓存用户会话所需客户端与服务端账号退出或到期
备份副本既定备份周期平台运维说明延迟删除边界

隐私声明、第三方 SDK 与发布证据要相互校验

Android Data safety 和 Apple privacy manifest 都要求开发者了解应用及第三方 SDK 的数据行为。附件功能上线前,应把清单中的数据类别、共享对象和用途与商店声明、privacy manifest、SDK 配置和实际网络路径逐项核对。声明缺少不代表运行时一定上传,声明存在也不代表技术控制已经生效;二者是不同证据。任何 SDK 版本更新后都要重查其附件、日志和诊断读取范围。

OWASP MASVS-PRIVACY-1 强调最小化敏感数据访问,并要求考虑第三方组件;MASWE-0001 则提示生产日志可能暴露凭据、令牌、个人信息和内部诊断数据。项目应把这些原则落到自动门禁:扫描日志字段、检查附件 manifest、验证派生物删除、审计 SDK 初始化参数。引用页面的状态和适用边界也要保留,不能把通用控制写成目标 App 已完成验证。

NIST SSDF 提供来源、构建、验证和变更证据的组织方向。对附件最小化,发布包应绑定需求清单版本、客户端构建、预处理器版本、服务端策略、测试夹具、审校结果和变更负责人。实际网络与删除回执仍需项目环境验证。没有真实数据时,应明确 SEO、GEO、流量、排名和用户转化数据未接入,不利用模型输出或静态清单虚构运营结论。

  • App 与第三方 SDK 的附件数据行为进入同一数据流图
  • Data safety、privacy manifest 与真实字段和接收方逐项核对
  • 日志门禁拒绝正文、文件名、令牌和完整 OCR 内容
  • 每次 SDK 或 AI 服务变更重新审计收集、共享与保留范围
  • 发布证据绑定清单、客户端、预处理器、服务端策略和测试
  • 没有项目回执的能力、效果和运营指标保持未证实

用附件 manifest 门禁拒绝过度收集和孤立派生物

下面的 Python 示例读取一个公开安全的 JSON manifest。它要求附件使用稳定安全标识,具有具体 purpose、明确 userConfirmed、有限 expiresAt、字段 allowlist 和删除策略;每个 extractedField 必须出现在 allowlist,每个 derivative 必须绑定 parentId、retentionClass 与 deleteTrigger。脚本还检查 retry 对象不会重新读取原文件,并在任何违规时输出字段问题和非零状态。

示例只校验声明的一致性,不能证明预处理器真的删除了 EXIF、服务端真的按期删除,也不能代替合规审查。生产门禁应结合受控附件夹具、网络字段观察、文件系统回执、对象存储策略和删除演练。代码没有真实账号、客户数据、私钥、令牌、服务器地址或攻击步骤,可作为 CI 中 manifest 结构审计的起点。

可同时参阅本站关于模型下载遥测最小化和 AI 输出日志隐私边界的技术说明,分别处理模型资产网络与结果日志,不在当前页面复制第二套答案。准备具体 App 的附件治理评估时,可整理脱敏数据流图、支持格式、预处理规则、SDK 清单、保留策略、删除回执与测试夹具,再从御盾中央平台提交申请;最终 CTA 和本站相关文章链接由页面框架统一呈现。

  • purpose、userConfirmed 与有限 expiresAt 均为必填门禁
  • extractedFields 只能来自预先声明且唯一的 allowlist
  • 每个派生物绑定父附件、保留类别和有限删除触发
  • 重试只使用已确认的最小化副本,不重新读取原文件
  • 结构通过后仍执行网络、文件、日志与删除回执验证
  • 任何能力与效果结论绑定当前 App、服务策略和真实证据
AI 附件数据最小化 manifest 校验器
from datetime import datetime, timezone
from pathlib import Path
import json
import re
import sys

if len(sys.argv) != 2:
    raise SystemExit("usage: attachment_gate.py manifest.json")
manifest_path = Path(sys.argv[1])
if not manifest_path.is_file():
    raise SystemExit("manifest file is missing")
try:
    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
    raise SystemExit("manifest is not valid JSON") from exc
if not isinstance(manifest, dict):
    raise SystemExit("manifest must be an object")

safe_id = re.compile(r"^[A-Za-z0-9_.-]{1,64}$")
attachment_id = str(manifest.get("attachmentId", ""))
if not safe_id.fullmatch(attachment_id):
    raise SystemExit("attachmentId is invalid")
if not str(manifest.get("purpose", "")).strip():
    raise SystemExit("purpose is required")
if manifest.get("userConfirmed") is not True:
    raise SystemExit("explicit user confirmation is required")

try:
    expires_at = datetime.fromisoformat(str(manifest["expiresAt"]).replace("Z", "+00:00"))
except (KeyError, ValueError) as exc:
    raise SystemExit("expiresAt must be an ISO timestamp") from exc
if expires_at.tzinfo is None or expires_at <= datetime.now(timezone.utc):
    raise SystemExit("expiresAt must be a future bounded timestamp")

allowed = manifest.get("allowedFields")
extracted = manifest.get("extractedFields")
if not isinstance(allowed, list) or not allowed or len(allowed) != len(set(allowed)):
    raise SystemExit("allowedFields must be a unique nonempty list")
if not isinstance(extracted, list) or any(field not in allowed for field in extracted):
    raise SystemExit("extractedFields exceed the allowlist")

derivatives = manifest.get("derivatives")
if not isinstance(derivatives, list):
    raise SystemExit("derivatives must be a list")
for number, item in enumerate(derivatives, 1):
    if not isinstance(item, dict) or item.get("parentId") != attachment_id:
        raise SystemExit(f"derivative {number} has no valid parent")
    if not str(item.get("retentionClass", "")).strip():
        raise SystemExit(f"derivative {number} lacks retentionClass")
    if item.get("deleteTrigger") not in {"task_end", "cancel", "expiry"}:
        raise SystemExit(f"derivative {number} lacks a bounded delete trigger")

retry = manifest.get("retry", {})
if retry.get("readsOriginalFile") is True:
    raise SystemExit("retry must reuse only the confirmed minimized copy")
if retry.get("manifestVersion") != manifest.get("manifestVersion"):
    raise SystemExit("retry is not bound to the current manifest")
print(f"validated_attachment={attachment_id} derivatives={len(derivatives)}")

事实依据与适用边界

以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。

本文判断事实或工程依据适用限制
Android 应用的数据声明需要覆盖应用代码和第三方 SDK 实际收集或共享的数据。Declare Android app data use 说明开发者需要根据应用及第三方库的数据行为完成 Data safety 信息。商店声明是透明度与审核材料,不会自动限制附件读取、网络发送、日志或保留。
Apple 平台的 App 与列明第三方 SDK 需要维护适用的 privacy manifest 数据与 API 声明。Apple privacy manifests 说明在 App 或第三方 SDK 中添加、聚合并验证 privacy manifest 的流程。manifest 不证明运行时最小化、用户确认、加密或删除机制已经正确执行。
移动应用应最小化敏感数据访问,并把第三方组件的数据行为纳入责任边界。OWASP MASVS-PRIVACY-1 要求应用尽量减少敏感数据访问,且控制范围包括 App 及其第三方依赖。通用控制不替代具体附件分类、用户同意、地区合规和目标系统验证。
生产日志可能暴露凭据、令牌、个人信息和内部诊断内容,附件流程必须限制日志字段。OWASP MASWE-0001 描述敏感数据进入日志的风险与删除、降级、脱敏等缓解方向。该页面处于 Beta;文章只采用稳定风险原则,不把它当作目标 App 已通过测试的证据。
附件 API 的 OAuth 部署应限制授权流程、令牌受众与重放风险。RFC 9700 OAuth Security BCP 总结重定向、授权码注入、令牌重放和不安全 grant 等安全实践。OAuth 授权不决定附件是否必要、用户是否同意、内容如何最小化或服务保留多久。
附件治理的发布过程应保留需求、构建、验证和变更证据。NIST SP 800-218 SSDF 给出组织级安全软件开发与供应链风险管理实践。SSDF 不定义具体附件字段、删除时限,也不证明任何加固产品或 AI 服务具备特定能力。
预处理产生的缩略图、OCR、切片、向量和重试副本应继承父附件的身份与删除策略。工程判断:派生物若脱离 parentId、generation 和 retentionClass,将无法完成一致撤销与删除。实际派生类型和保留条件由目标 App 数据流决定,公开清单不能代替文件与服务端回执。
用户取消或切换账号后,旧附件任务应先失效再清理,不能继续回写迟到结果。工程判断:账号域、attachmentId、generation 和 consentId 共同约束异步任务归属。取消不保证所有远端副本瞬间消失;产品需要准确说明不可立即终止的阶段与备份边界。

工程常见问题

用户主动选择文件,是否等于同意上传全部元数据?

不等于。应展示实际发送内容、元数据类别、接收方和用途;无关 EXIF、文件系统属性、批注或隐藏内容默认删除,确有必要时单独说明。

把图片压缩后上传是否已经完成数据最小化?

没有。压缩主要改变编码与体积,未必删除 EXIF、原文件名、缩略图或无关区域;还要按字段清单重建副本并核对派生物。

附件只在服务器短暂处理,可以不设置删除策略吗?

不可以。短暂仍需可执行定义,覆盖上传分片、原对象、OCR、切片、缓存、日志和失败副本,并保留在线删除与备份过期边界。

有 OAuth 访问令牌是否说明附件上传符合最小化要求?

不能。令牌只约束调用授权,还要独立检查附件目的、用户确认、字段范围、账号域、保留期与派生物删除。

上传失败自动重试时需要再次读取原文件吗?

默认不应。重试只引用已确认的最小化副本和相同 manifest;账号、清单版本、用户选择或副本摘要变化时,任务作废并重新确认。

通过 manifest 校验是否代表附件数据已经安全?

不代表。结构门禁只检查声明一致性,还需用受控夹具验证文件清理、网络字段、日志、第三方 SDK、远端对象和删除回执。

想用自己的 App 验证?

提交候选包、目标系统和关键业务路径,申请御盾 PoC 与兼容性评估。

继续阅读: 移动 AI 应用的运行时安全边界