先看结论与判断条件
- 系统指令属于模型行为配置,不是访问控制;模型遵循指令也不等于调用者已经获得业务权限。
- 把 Prompt 混淆、拆分或放进 Native 代码可以增加直接阅读成本,却不能让客户端持有内容变成可靠秘密。
- 服务端 Prompt 模板应把模型配置、输入 schema、工具声明和正文作为一个版本化资产审查,但模板服务仍不是授权服务。
- 长期凭据、角色判断、对象归属、额度和高风险工具执行必须留在服务端,客户端只持有当前会话所需的有限能力。
- 工具调用采用明确 schema、服务端 allowlist、最小 scope、对象级授权、用户确认和审计,不能从 Prompt 文本推导权限。
- 移动端加固属于纵深防御,可以保护商业实现并提高分析成本,但发布结论仍要建立在服务端控制和完整证据链上。
先区分行为指令和安全控制
系统 Prompt 的作用是向模型描述角色、语气、回答限制、工具使用偏好和上下文规则。它可以减少某些不符合预期的回答,却不负责证明调用者身份,也无法决定某个用户是否有权读取订单、修改配置或触发交易。安全边界需要稳定、确定、可审计地拒绝未授权请求,而生成模型的输出受到上下文、模型版本、输入内容和采样条件影响,两者的责任完全不同。
Firebase AI Logic system instructions 说明系统指令随请求影响模型行为,同时应用仍需控制输入、输出和工具权限。这个事实支持把 Prompt 视为行为层配置,而不是授权结论。即便模型回答“允许执行”,应用也必须忽略这句自然语言,依据当前会话、工具 schema、对象权限和服务端策略重新决策。模型回答可以成为用户界面建议,不能成为签名、令牌或权限凭证。
判断某项机制是否构成安全边界,可以问三个问题:攻击者能否观察或修改输入,拒绝结果是否由确定性代码产生,失败记录能否绑定具体策略版本。客户端内的 Prompt 很难同时满足这些条件。更合理的设计是让 Prompt 只产生结构化候选,由可信服务逐项验证,再返回范围有限、可撤销且可审计的操作结果。
| 对象 | 主要作用 | 能否作为授权依据 | 正确落点 |
|---|---|---|---|
| 系统 Prompt | 影响回答和工具选择倾向 | 不能 | 模型请求配置 |
| 输入 schema | 限制字段结构和类型 | 不能单独授权 | 客户端与服务端共同校验 |
| 会话认证 | 确认当前主体 | 提供身份基础 | 可信服务端 |
| 对象授权 | 判断主体能否操作资源 | 可以形成拒绝决定 | 业务服务端 |
| 工具策略 | 限定可调用动作和 scope | 需与会话联合判断 | 服务端策略层 |
| 用户确认 | 让高风险影响对用户可见 | 不能替代权限 | 受控交互页面 |
移动客户端天然是公共环境
原生 App 的代码、资源和运行时状态都交付到用户设备。开发者可以通过编译、压缩、混淆和加固降低直接阅读与篡改的便利程度,但无法把客户端变成只有服务端才能观察的环境。系统 Prompt 若随应用包、资源、远端响应或运行时请求进入设备,就应按可能被看到和修改来设计。安全方案不能依赖攻击者永远找不到某段字符串。
RFC 8252 OAuth 2.0 for Native Apps 把原生应用视为公共客户端,推荐使用外部 user-agent 与 PKCE,而不是依赖客户端秘密。该原则虽然针对 OAuth 原生应用流程,却清楚表达了客户端秘密的边界:安装在用户设备上的应用无法可靠保守长期静态秘密。系统 Prompt 不是 OAuth 凭据,但若团队把其中的隐藏规则当成唯一授权条件,会犯同一种信任位置错误。
客户端可保存面向体验的默认 Prompt、离线提示和低风险格式说明,但必须假设这些内容会泄露、被替换或与服务器版本不一致。泄露后的结果应最多是别人知道产品如何组织回答,而不是获得 API 权限、访问客户数据或触发高风险工具。若 Prompt 暴露就会导致越权,说明真正需要保护的控制没有放在可信边界里。
| 内容 | 客户端可见后的影响 | 推荐位置 | 必要控制 |
|---|---|---|---|
| 回答语气与格式 | 产品体验可被观察 | 客户端默认值或服务端模板 | 版本与回退记录 |
| 工具名称与参数说明 | 接口形态可被了解 | 客户端可持有公开 schema | 服务端 allowlist 和授权 |
| 长期 API 凭据 | 可能被滥用调用服务 | 服务端 | 轮换、限权和审计 |
| 角色与对象权限 | 被篡改会造成越权 | 业务服务端 | 按请求重新计算 |
| 客户敏感上下文 | 泄露会扩大数据风险 | 按需服务端检索 | 最小化和访问控制 |
| 高风险动作批准 | 伪造会改变业务状态 | 服务端命令层 | 用户确认、幂等和审计 |
隐藏、完整和服从是三件事
团队讨论系统 Prompt 安全时,经常把保密性、完整性和模型服从混在一起。保密性回答内容是否容易被读取,完整性回答当前请求使用的模板是否来自允许版本,模型服从回答输出是否符合指令。混淆或加密客户端资源主要影响静态读取成本,模板签名与版本门禁主要影响完整性,而模型服从仍受输入和模型行为影响。任何一项改善都不能替代另外两项。
如果业务需要保护 Prompt 中的商业表达,可以把主模板放在服务端,并只向通过认证的请求提供当前任务所需配置。即便如此,也应假设模型交互中有部分指令可被推断,日志或设备内存也可能出现可用表示。产品价值不应只依赖一句不可见文本,更不能把“不要调用管理工具”写在 Prompt 中后就省略工具权限检查。
完整性门禁应绑定模板 ID、模板版本、应用版本、模型配置、输入 schema、工具声明和回退值。应用收到远端模板后,要知道它适用于哪些候选、过期时如何处理、取回失败能否使用本地值。若本地回退允许的工具范围大于远端版本,网络故障可能反而放宽权限。正确策略是安全失败或使用更小能力的已知版本。
服务端模板也需要版本与回退治理
Firebase server prompt template syntax 说明服务端模板可以同时包含模型配置、system instructions、输入 schema、工具声明和模板正文。这些字段共同决定一次模型请求的行为,应作为同一配置资产评审和发布。只比较 Prompt 正文会遗漏温度、模型选择、工具定义或 schema 变化,导致报告与真实请求不一致。
Firebase server prompt templates get started 描述应用通过模板 ID 获取服务端配置。工程上需要建立 templateId、templateVersion、appVersion、modelId 和 fallbackVersion 的映射,并记录每次请求实际采用的版本。模板 ID 不应被理解成秘密,也不能直接代表“最新且安全”。服务端仍要验证调用者、应用版本和模板适用范围,并对下发失败提供明确状态。
远端配置可以缩短 Prompt 调整周期,但也增加发布治理责任。修改工具 schema 或系统指令可能改变可调用动作、字段含义和数据范围,应走与代码类似的审核、灰度和回滚流程。模板平台的语法和分发能力不保证模型行为稳定,也不替代业务授权。没有真实版本回执时,只能说明设计目标,不能宣称当前移动候选已经使用某份模板。
| 字段 | 需要绑定的对象 | 变更影响 | 放行证据 |
|---|---|---|---|
| templateId 与版本 | 模板正文和发布时间 | 决定请求配置身份 | 不可变版本记录 |
| modelId 与参数 | 目标模型和生成配置 | 输出行为可能变化 | 评审与灰度回执 |
| system instructions | 角色、限制和回答要求 | 改变行为边界 | 差异审查 |
| input schema | 允许输入字段与类型 | 改变数据面 | 正反输入用例 |
| 工具声明 | 动作名称、参数和描述 | 扩大可操作范围 | 服务端 allowlist 对照 |
| fallbackVersion | 网络失败时使用的配置 | 可能改变能力和体验 | 安全失败与回退测试 |
凭据和业务授权必须离开 Prompt
Android insecure API usage 指出移动客户端中的静态 API Key 可能被逆向或拦截,敏感服务应把长期凭据保留在服务端。系统 Prompt 更不应承载密钥、服务账号、内部令牌或“看到某句话就允许调用”的共享口令。自然语言字段缺乏专用凭据的轮换、受众、时效和使用审计,很容易进入日志、崩溃记录或调试界面。
把请求转到服务端并不自动完成安全设计。服务端代理仍要认证用户,限制允许模型、工具、数据域和额度,校验对象归属,并记录拒绝与异常调用。客户端可以获取短期、范围有限的会话能力,但不能拥有可跨用户、跨环境或长期使用的服务凭据。Prompt 中的角色描述如“你是管理员助手”只影响回答语气,不赋予请求管理员权限。
业务授权需要在工具真正执行前做最终判断。模型可以根据对话建议“读取发票”或“修改地址”,服务端必须把建议解析成明确命令,再依据当前主体、目标对象、动作 scope、对象状态和用户确认决定。任何来自 Prompt 或模型参数的 userId、role、isAdmin、price 和 approved 字段都属于不可信输入。
工具权限要用确定性策略收口
工具定义应当公开清晰,权限却不能写成模型自行解释的文字。服务端维护工具 allowlist,每个工具绑定参数 schema、最小 scope、对象授权函数、速率限制、幂等策略和确认等级。模型只选择候选工具并填写候选参数,执行器重新解析并校验。未登记工具、未知字段、重复参数和超出范围的值都应在进入业务处理器前失败。
工具返回的数据也要做最小化。模型不需要看到完成任务之外的字段,错误信息不应泄露内部权限结构或其他用户对象。服务端可以给模型返回稳定错误码和面向用户的安全摘要,把详细诊断留在受控日志。这样即使 Prompt 被观察或模型受到恶意输入影响,可到达的数据与动作仍由代码边界限制。
高风险工具需要用户确认,但确认不是授权替代。确认页面从服务端重新读取对象摘要和影响,不直接展示模型生成的事实;确认后提交一次性命令,服务端再次检查会话和状态。若对象在确认期间变化、会话切换或请求重复,幂等与状态门禁应拒绝或返回已有结果。Prompt 不能通过“用户已经同意”一句话跳过这一流程。
| 门禁 | 输入 | 拒绝条件 | 不能由谁决定 |
|---|---|---|---|
| 工具 allowlist | 候选工具名 | 未知或当前场景未开放 | 模型文本 |
| 参数 schema | 结构化候选参数 | 未知字段、类型或范围错误 | Prompt 示例 |
| 会话认证 | 访问令牌与设备状态 | 失效、受众不符或风险异常 | 客户端布尔值 |
| 对象授权 | 当前主体、动作和资源 | 无角色、无归属或状态不允许 | 模型推断 |
| 用户确认 | 服务端生成的动作摘要 | 未确认或摘要已过期 | 模型声称 |
| 幂等与审计 | 操作标识和结果状态 | 重复、过期或缺少审计上下文 | 本地 Prompt |
用扫描器发现被误当成秘密的配置
迁移第一步不是删除所有客户端 Prompt,而是识别哪些字段承担了不该承担的责任。扫描目标包括移动端 JSON、构建配置或资源清单中的 systemPrompt、hiddenPrompt、apiKey、secret、allowedRoles、authorizationRule、toolPolicy 和 privilegedTool 等字段。报告只输出字段路径、风险类别和迁移方向,不回显字段值,避免审计工具再次扩散敏感内容。
下面的 Python 脚本读取一份 JSON 配置,递归扫描键名,并区分行为配置、凭据、授权规则和工具权限。行为配置可以保留为公开默认值或迁移到版本化服务端模板;凭据必须移出客户端;授权规则和特权工具策略应迁移到可信服务端。脚本遇到非法结构会失败,发现高风险字段也返回非零状态,便于接入发布门禁。
字段名扫描只能发现显式问题,无法判断拼接字符串、压缩资源、Native 常量或运行时下发内容,也不能证明服务端代理已经正确认证和授权。它适合作为配置仓库的早期阻断,再配合构建产物检查、网络请求审计和服务端权限测试。扫描通过的结论只能写成“未发现这些已知字段”,不能写成“客户端没有任何秘密”。
from pathlib import Path
import json
import sys
if len(sys.argv) != 2:
raise SystemExit(2)
config_path = Path(sys.argv[1])
if not config_path.is_file():
raise SystemExit(2)
try:
document = json.loads(config_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
raise SystemExit(2)
if not isinstance(document, (dict, list)):
raise SystemExit(2)
rules = {
"systemprompt": ("behavior-config", "versioned server template or public client fallback"),
"hiddenprompt": ("behavior-config", "versioned server template"),
"apikey": ("credential", "server credential store"),
"secret": ("credential", "server credential store"),
"allowedroles": ("authorization", "server object authorization policy"),
"authorizationrule": ("authorization", "server object authorization policy"),
"toolpolicy": ("tool-permission", "server tool allowlist"),
"privilegedtool": ("tool-permission", "server tool allowlist")
}
findings = []
def walk(value, path):
if isinstance(value, dict):
for key, child in value.items():
normalized = "".join(character.lower() for character in str(key) if character.isalnum())
if normalized in rules:
category, destination = rules[normalized]
findings.append({"path": path + [str(key)], "category": category, "destination": destination})
walk(child, path + [str(key)])
elif isinstance(value, list):
for index, child in enumerate(value):
walk(child, path + [str(index)])
walk(document, [])
summary = {"file": config_path.name, "findingCount": len(findings), "findings": findings}
print(json.dumps(summary, ensure_ascii=False, indent=2))
if any(item["category"] in {"credential", "authorization", "tool-permission"} for item in findings):
raise SystemExit(3)移动端加固是纵深防御,不是授权替身
OWASP MASVS-RESILIENCE 把抗篡改与抗逆向放在移动安全的纵深防御范围内。对包含商业 Prompt 编排、路由逻辑和端侧模型调用的应用,加固可以提高直接分析和修改成本,也能帮助保护核心实现。但它不能改变客户端属于公共环境的事实,更不能把客户端里的长期凭据或角色判断变成可信授权。
加固范围应围绕真正需要保护的商业代码和策略客户端实现,例如请求组装、响应校验、完整性信号采集与安全失败逻辑,同时把最终授权保留在服务端。若应用被修改,服务端仍能因为会话、对象权限、工具 allowlist 和幂等门禁拒绝危险操作。这样的分层设计才不会把全部安全压力放在一个隐藏 Prompt 或单一客户端检查上。
公开内容不能根据控制目录宣称某个候选已经达到防护强度。真实结论需要同一包体、同一签名、同一服务端策略和实际测试回执。本文只给出架构判断与配置扫描方法,不提供攻击阻断、性能、客户或排名数据。项目评审还要检查具体 Prompt 分发、日志、缓存、工具链和失败模式。
迁移时按责任移动,不按字符串搬家
发现隐藏 Prompt 后,不应简单把整段文本搬到服务器就宣布完成。先把内容拆成行为要求、输入 schema、工具描述、凭据、授权规则、数据检索范围和用户提示。行为要求与 schema 进入版本化模板,公开工具描述可以随客户端或模板维护,凭据进入服务端秘密管理,授权规则进入业务策略,敏感数据通过按请求检索和最小字段返回。
迁移验收需要覆盖正常、失败和回退。应用拿到正确模板时记录模板版本;拿不到模板时使用更小能力的本地默认值或安全失败;旧应用请求新模板时按兼容表处理;工具调用仍由服务端拒绝未知动作和无权对象。测试还要证明客户端修改 Prompt、伪造角色或直接调用工具接口不会改变服务端最终决定。
准备移动 AI 安全架构评估时,可整理客户端 Prompt 和配置清单、模板版本映射、模型与应用版本、工具 schema、服务端 allowlist、认证方式、对象授权、回退策略和拒绝用例,再通过御盾中央平台提交需求。核心目标不是让系统 Prompt 更难被看到,而是让它即使被看到或替换,也无法越过真正的凭据、权限和数据边界。
| 迁移对象 | 目标位置 | 验证问题 | 通过边界 |
|---|---|---|---|
| 行为说明 | 版本化服务端模板 | 版本、回退和适用应用是否绑定 | 只影响模型行为 |
| 输入 schema | 模板与服务端校验器 | 未知字段和类型错误是否拒绝 | 不代表业务授权 |
| 长期凭据 | 服务端秘密管理 | 客户端包和日志是否不再持有 | 服务端仍需限权 |
| 工具 allowlist | 服务端执行策略 | 未知工具和超范围参数是否失败 | 按当前会话判断 |
| 对象授权 | 业务服务端 | 伪造角色和对象 id 是否被拒绝 | 绑定真实主体和状态 |
| 客户端加固 | 应用候选 | 核心实现和失败逻辑是否纳入评审 | 属于纵深防御 |
事实依据与适用边界
以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。
| 本文判断 | 事实或工程依据 | 适用限制 |
|---|---|---|
| 系统指令影响模型行为,但应用仍需控制输入、输出和工具权限。 | Firebase AI Logic system instructions 描述 system instructions 的请求作用与应用控制责任。 | 系统指令不能保证模型始终服从,也不能替代凭据、业务授权和工具执行门禁。 |
| 服务端 Prompt 模板可以同时包含模型配置、系统指令、输入 schema、工具声明和正文。 | Firebase server prompt template syntax 描述这些模板字段的结构和示例。 | 模板语法和预览能力不保证模型行为稳定,也不提供业务授权。 |
| 应用通过模板 ID 获取服务端配置,需要管理模板、应用和回退版本关系。 | Firebase server prompt templates get started 描述模板获取与接入流程。 | 模板下发不证明来源、适用版本、失败回退和当前候选已经正确治理。 |
| 移动客户端中的静态 API Key 可能被逆向或拦截,长期敏感凭据应保留在服务端。 | Android insecure API usage 描述不安全 API 使用和客户端静态密钥风险。 | 服务端代理仍需认证、最小权限、额度、审计和滥用控制。 |
| 移动端抗逆向与抗篡改属于纵深防御控制。 | OWASP MASVS-RESILIENCE 给出移动应用韧性控制范围。 | 控制目录不证明具体应用候选达到某种保护强度,也不替代服务端授权。 |
| 原生 App 属于公共客户端,不应依赖客户端秘密完成 OAuth 安全。 | RFC 8252 OAuth 2.0 for Native Apps 说明原生应用的公共客户端属性及外部 user-agent 与 PKCE 要求。 | RFC 8252 不定义移动 AI 工具的业务 scope、对象授权和授权时长。 |
| 系统 Prompt 即使完全泄露,也不应让调用者获得额外数据或工具权限。 | 工程判断:行为配置与访问控制分离后,服务端仍会按当前主体、对象和动作拒绝未授权请求。 | 是否达到该目标需要对真实客户端、服务端和工具接口做项目测试。 |
| 迁移系统 Prompt 时要按行为、schema、凭据、授权和工具策略拆分责任。 | 工程判断:把整段文本原样搬到服务端会继续混淆配置分发与安全决策。 | 本文不提供特定产品的实现、性能、攻击阻断或客户验证结论。 |
工程常见问题
把系统 Prompt 放进 Native 代码就能保密吗?
只能增加直接读取成本,不能让客户端成为可靠秘密存储。运行时仍需使用相关内容,安全设计应假设它可能被观察,并把凭据和授权放在服务端。
服务端下发 Prompt 后是否就形成安全边界?
没有。服务端下发改善配置治理,但模板仍只影响模型行为。调用者认证、工具 allowlist、对象权限和数据最小化需要独立实现。
Prompt 中写明只能管理员调用工具是否足够?
不足。模型对角色的描述不是授权证据。服务端必须从可信会话读取身份,并对具体动作和对象重新计算权限。
移动端是否可以保留本地系统 Prompt 作为离线回退?
可以保留低风险、能力更小的公开默认值,但要版本化并明确回退行为。离线 Prompt 不能包含长期凭据,也不能扩大工具或数据权限。
配置扫描器没有发现 secret 字段是否说明应用安全?
不能。扫描只能发现已知键名,还要检查构建产物、网络请求、日志、缓存和服务端授权。通过仅表示没有命中这组显式字段。
申请系统 Prompt 安全架构评估前要准备什么?
准备客户端配置、模板版本映射、工具 schema、服务端 allowlist、认证与对象授权、回退策略和拒绝用例,再通过御盾中央平台提交评估需求。