先看结论与判断条件

  • 模型输出、检索片段和工具结果都是内容数据,不能因为使用了 system instruction 或来自自有服务就进入可信代码通道。
  • 优先把 Markdown 解析成受控 AST 并交给原生组件渲染;无法识别的节点、原始 HTML、事件属性和嵌入对象默认拒绝。
  • 若业务必须使用 WebView,渲染实例与登录页、支付页、客服页等可信 WebView 分开,默认禁用 JavaScript、文件访问和 Native bridge。
  • URL 需要按 scheme、host、动作类型和用户意图分别判断;展示链接不等于允许自动导航、下载、打开深链或调用宿主能力。
  • 清洗、渲染、点击和外跳要使用同一 policyVersion,并记录有限决策回执,避免内容通过缓存或重试绕过最新策略。
  • Data safety、第三方 SDK 清单和发布证据要反映真实数据行为,但声明与静态校验不能替代目标 App 的运行验证。

先把模型文本从代码与权限通道中分离

AI 回复看起来像普通文字,但其中可能包含 Markdown 链接、图片、原始 HTML、代码块、表格、内联样式或带参数的 URL。模型并不知道移动端渲染器的全部权限,也不能判断某段内容是否会被 WebView 当作活动页面执行。安全边界应在应用侧:输入无论来自远端模型、端侧模型、RAG 文档还是工具结果,都先标为 untrusted_content,不能直接拼接进模板、脚本、Native bridge 参数或可信页面。

system instruction 可以约束模型的行为倾向,却不是输出安全证明。提示词可能被用户输入、检索内容、上下文截断或模型误差影响;即使模型按要求只返回 Markdown,Markdown 解析器仍可能支持原始 HTML、自动链接或扩展语法。应用要以实际解析树和目标渲染能力做决定,不使用“模型已经被告知不要输出脚本”替代结构校验。

内容信任和业务授权也应分开。文本中出现“打开设置”“上传文件”“继续支付”或某个深链,不表示用户授权执行这些动作。当前页面只讨论展示层的标签、脚本、文件、bridge 与外跳边界,不复制模型生成深链动作的完整授权方案。涉及账户、支付、文件或控制台的操作,应进入独立的宿主业务流程,而不是由渲染内容直接触发。

AI 输出进入渲染层前的信任分类
输入来源初始信任允许用途禁止捷径
模型回复不可信内容解析和展示直接执行 HTML 或脚本
RAG 片段不可信引用带来源展示覆盖宿主策略
工具结果结构化但受限按 schema 呈现拼接成 bridge 调用
服务端模板版本化资源受控布局混入未清洗用户文本
用户点击明确交互信号进入二次决策等同业务授权
宿主命令高权限动作独立校验执行从 Markdown 自动生成

优先解析 Markdown AST,而不是替换几个字符串

基于正则删除 script 或 javascript 字样很难覆盖嵌套节点、编码变化、属性组合和解析器扩展。更清晰的做法是使用固定版本的 Markdown 解析器生成 AST,再按节点类型重建输出。allowlist 只保留段落、标题、强调、列表、引用、受控代码块和业务确实需要的表格;原始 HTML、iframe、object、embed、表单、样式和未知扩展默认拒绝。重建意味着不复用原始标签字符串。

AST 节点的属性也要逐项重建。链接只接受明确 scheme 和有限长度,图片可选择完全禁用、经代理加载或只显示占位;代码块按纯文本转义,不成为可执行脚本。标题层级、列表深度、节点总数、文本长度、表格尺寸和嵌套深度应有资源上限,解析失败时显示纯文本或可解释错误,不能为了“尽量展示”切换到未清洗 HTML。

解析策略要绑定 parserVersion、policyVersion 和 contentDigest。缓存只保存重建后的 AST 或受控视图模型,并在策略版本变化时失效;不能缓存一份旧 HTML,在新版本中跳过清洗直接加载。摘要用于识别同一输入和回执,不证明内容安全。第三方 Markdown 扩展升级后,要重新检查其节点、属性与 URL 行为,不能假设库的小版本更新不会改变解析面。

Markdown AST 节点处理策略
节点类型默认动作保留条件输出形式
段落与文本保留长度与字符检查原生文本节点
标题与列表保留层级和深度受限受控布局节点
代码块文本化语言标签 allowlist不可执行等宽文本
链接重建scheme 与点击确认宿主点击事件
图片禁用或代理来源和隐私策略明确占位或受控加载
原始 HTML拒绝无默认例外安全错误提示
未知扩展拒绝策略升级后评审不渲染

原生渲染与 WebView 需要不同的故障边界

对于标题、段落、列表、引用、代码和表格,原生组件通常更容易把内容限制在显示语义:链接点击由宿主接管,代码块只作为文本,节点不能自行访问文件或调用 JavaScript。原生渲染并非自动安全,解析器、图片加载器、富文本 span 和点击处理仍需审查;它的优势是能力面可以按组件枚举,而不是继承整个浏览上下文。

当业务确实需要复杂排版而采用 WebView,应为 AI 内容创建独立实例和独立配置,不能与登录、支付、账号中心或其他可信网页共享历史、bridge、cookie 或可复用页面状态。加载前把清洗后的内容和 policyVersion 绑定,页面导航交由宿主决策。渲染失败时回退为纯文本,不切换到一个权限更宽的共用 WebView。

选择哪种渲染器要记录功能需求,而不是以实现速度决定。若唯一需求是展示 Markdown,启用 JavaScript、DOM 注入、文件选择、下载或 bridge 缺少业务依据。若某个可视化组件必须运行脚本,应把它拆成经过签名和版本控制的宿主资源,模型只提供结构化数据;模型生成的脚本仍不进入执行通道。具体架构需结合目标 App 验证,本文不宣称某个实现已经通过。

原生渲染与 WebView 的能力边界
能力原生受控组件隔离 WebView决策要求
文本排版按节点实现HTML/CSS 呈现优先最小能力
脚本无执行语义可配置但默认关闭模型内容禁止开启
链接宿主点击回调导航拦截二次确认和 allowlist
文件访问由业务显式读取必须明确关闭无展示需求即禁用
Native bridge普通组件回调高风险能力面AI 内容实例不注册
故障回退纯文本或节点错误纯文本页面不得扩大权限

WebView 配置要按最小能力重新建立

Android WebView 文档说明 JavaScript、页面导航与宿主交互需要由应用配置。AI 内容实例应从最小能力开始:JavaScript 关闭,文件访问和 content 访问关闭,不允许从文件上下文扩大网络或本地资源访问,不注册 addJavascriptInterface,也不创建不必要的 message channel。只关闭一个开关不足以形成边界,设置、页面来源、导航拦截和宿主回调要一起审查。

WebView 生命周期也要隔离。账号切换、页面关闭或 policyVersion 变化后,停止加载、清空该内容实例的待处理导航与临时状态,并销毁与 AI 内容绑定的 bridge 空集和回调。不要把一个已经注册敏感 bridge 的 WebView 先显示可信页面,再复用来展示模型 HTML;清空页面内容不等于撤销所有宿主能力和历史状态。

如果业务必须为受控宿主资源启用 JavaScript,应把资源身份、脚本版本和数据通道与模型文本分开。模型输出只进入经过编码的文本节点或结构化数据,不拼接到 script、style、事件属性或 URL。启用例外需要威胁模型、代码审查和运行回执,不能作为通用 Markdown 配置。本文不提供可执行注入样例,也不推断任何具体页面可被利用。

AI 内容 WebView 的默认设置
设置面默认值允许例外验证点
JavaScript关闭受控宿主资源另行评审模型内容不能改变
文件访问关闭无默认例外本地 URL 不可读取
content 访问关闭明确业务通道授权与 URI 范围
Native bridge不注册AI 内容实例无例外接口表为空
导航宿主拦截允许 host 与 scheme每次点击决策
实例复用不与可信页共用无默认例外cookie 与回调隔离

Bridge、frame 与 origin 不能靠页面看起来可信

Android WebView native bridge risks 指出 addJavascriptInterface 和 message channel 可能缺少充分的 frame 或 origin 隔离,并把宿主能力暴露给页面脚本。对 AI 生成内容,最可靠的默认值是不注册 bridge。即使主文档来自受控模板,模型输出中嵌入的 frame、导航或链接也不应获得宿主接口;页面视觉上显示自有品牌,不能证明执行上下文和 frame 来源可信。

若历史架构无法立即移除 bridge,迁移期间至少把 AI 内容放进不带 bridge 的独立实例,并让敏感操作回到宿主原生界面。不要尝试在模型文本中约定一个秘密函数名,或通过提示词要求模型不调用接口,这些都不是权限控制。bridge 方法的参数也不应直接接收模型构造的 JSON、文件路径、命令名称或 URL 后执行。

message channel 同样需要明确对端与消息 schema。能发送字符串不表示可以调用宿主动作;接收端先验证 channel 所属页面、消息类型、字段范围、用户状态和业务权限,再进入独立控制器。当前文章只给出展示层的禁用与迁移原则,复杂 bridge 认证需要单独设计。没有目标页面和运行证据时,不能写成风险已被阻断或接口已安全。

Native bridge 风险与替代路径
需求危险捷径推荐边界用户体验
复制文本页面调用通用 bridge宿主选择菜单用户明确点击
打开链接脚本直接导航宿主拦截并确认显示目标域名
上传文件bridge 接收路径原生文件选择流程展示选择范围
业务操作模型生成命令原生权限控制器再次确认
消息通信任意字符串通道固定 schema 与来源校验失败可解释
复杂组件在回复中执行脚本签名宿主资源加数据输入能力独立说明

链接、图片和文件要通过独立决策器

Markdown 链接至少拆成显示文本、原始 URL、解析后 scheme、host、端口、路径类别和动作类型。默认只允许明确的 https 浏览链接,并在离开应用前展示目标;未知 scheme、本地文件、content URI、脚本 scheme、数据 URI 和带业务命令的自定义 scheme 进入拒绝或专用流程。规范化应使用平台 URL 解析器,不依赖字符串前缀判断。

Android WebView unsafe file inclusion 提醒文件访问、JavaScript 与不可信内容组合会扩大本地文件和脚本风险。关闭文件访问后,仍要限制导航、下载、重定向和资源加载,因为网络 URL 也可能离开预期范围。AI 回复中的图片可以默认显示占位,由用户选择是否加载;若使用图片代理,要明确缓存、日志、账号域和删除策略,不能把原 URL 静默交给第三方 SDK。

点击回执只记录 policyVersion、决策类别、scheme 类别、目标域名摘要和用户是否确认,不保存完整查询参数、对话正文或访问令牌。重定向后的最终目标仍需策略检查,不能只验证首个 URL。下载、文件选择、深链和支付等动作不属于普通浏览链接,必须进入各自业务门禁;本文不为这些相邻意图提供第二套答案。

链接和资源的渲染决策
对象默认策略允许条件拒绝或转交
https 链接用户确认后外跳host 与动作允许未知目标拒绝
自定义 scheme不直接执行专用业务控制器普通渲染层拒绝
file URL拒绝无展示例外不交给 WebView
content URI拒绝原生授权流程不得从文本触发
远端图片占位用户选择或受控代理不静默加载
下载不自动开始独立下载策略普通链接流程转交

隐私声明和发布证据要覆盖渲染旁路

AI 回复渲染可能触发图片请求、链接预取、WebView 网络、分析事件、崩溃日志和第三方 SDK。Android Data safety 要求开发者了解应用与第三方库实际收集和共享的数据。发布前应把这些旁路与声明、隐私说明和 SDK 配置逐项对照。声明完整不代表技术隔离已经生效,声明缺少也不能单独证明发生了发送,两者需要不同证据。

NIST SSDF 提供来源、构建、验证、变更和供应链风险管理的组织方向。渲染策略应绑定 Markdown 解析器版本、allowlist、WebView 设置、URL 决策器、图片加载器、测试夹具和代码审查。任何解析器、WebView 支持库或 SDK 更新后,都重新执行未知节点、原始 HTML、脚本开关、文件访问、bridge 空表、重定向和缓存失效门禁。

运行验证使用公开安全夹具覆盖正常排版、未知节点、超深嵌套、外部链接、图片、原始 HTML、失败回退和策略升级,不使用客户会话或可执行攻击链。结果区分静态配置、设备观察和仍未验证的边界。SEO、GEO、流量、排名和客户效果没有真实数据时保持未接入,不能由审计通过、文章长度或模型复核推断。

  • 渲染数据流覆盖 WebView、图片、链接预取、日志与第三方 SDK
  • 解析器和 allowlist 版本与发布候选绑定
  • AI 内容 WebView 的脚本、文件访问和 bridge 空表有运行回执
  • 正常内容、拒绝内容、失败回退和策略升级均有受控夹具
  • Data safety 与 SDK 声明按实际网络和日志行为复核
  • 无目标证据的防护、兼容、性能和运营结论保持未证实

用策略清单拒绝高权限 WebView 配置

下面的 Python 示例读取一个公开安全的渲染策略 JSON。它要求 mode 为 ast_allowlist,rawHtml、javaScript、fileAccess、contentAccess、nativeBridge、downloads 和 automaticNavigation 全部关闭;allowedTags 必须是受控集合的非空子集,allowedSchemes 只能包含 https,externalConfirmation 必须开启。任何缺失、扩权或未知字段都产生非零退出。

示例检查的是配置声明,不解析真实 Markdown,也不能证明 WebView 设置已按清单应用、重定向受到拦截或第三方组件没有旁路。生产门禁还要把清洗后的 AST、WebView 实例、URL 决策、图片加载、网络观察和用户确认回执关联到同一 policyVersion。代码不包含真实域名、包名、凭据、服务器信息、客户数据或攻击载荷。

可同时参阅本站关于模型生成深链动作边界与 AI 输出日志隐私的技术说明,分别处理业务动作授权和结果记录;当前页面只回答渲染前隔离。准备具体 App 的输出展示评估时,可整理脱敏节点清单、WebView 设置、bridge 表、URL 规则、SDK 清单和测试夹具,再从御盾中央平台提交申请;页面框架会统一提供本站内链和中央行动按钮。

  • 策略字段与已审查 schema 完全一致,不接受未知扩权字段
  • 只允许 AST allowlist,raw HTML 默认拒绝
  • JavaScript、文件、content、bridge、下载和自动导航均关闭
  • 普通浏览链接只接受 https 并要求用户确认
  • 标签集合是有限 allowlist 的唯一非空子集
  • 结构通过后仍验证真实解析器、WebView、网络和点击回执
AI Markdown WebView 渲染策略校验器
from pathlib import Path
import json
import sys

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

required_keys = {
    "mode",
    "rawHtml",
    "javaScript",
    "fileAccess",
    "contentAccess",
    "nativeBridge",
    "downloads",
    "automaticNavigation",
    "externalConfirmation",
    "allowedTags",
    "allowedSchemes",
    "policyVersion",
}
if set(policy) != required_keys:
    raise SystemExit("policy fields do not match the reviewed schema")
if policy["mode"] != "ast_allowlist":
    raise SystemExit("rendering must use an AST allowlist")
for field in (
    "rawHtml",
    "javaScript",
    "fileAccess",
    "contentAccess",
    "nativeBridge",
    "downloads",
    "automaticNavigation",
):
    if policy[field] is not False:
        raise SystemExit(f"{field} must be disabled")
if policy["externalConfirmation"] is not True:
    raise SystemExit("external navigation requires confirmation")

safe_tags = {"paragraph", "heading", "emphasis", "list", "quote", "code", "table", "link"}
allowed_tags = policy["allowedTags"]
if not isinstance(allowed_tags, list) or not allowed_tags:
    raise SystemExit("allowedTags must be a nonempty list")
if len(allowed_tags) != len(set(allowed_tags)):
    raise SystemExit("allowedTags contains duplicates")
if not set(allowed_tags).issubset(safe_tags):
    raise SystemExit("allowedTags contains an unreviewed node")
if policy["allowedSchemes"] != ["https"]:
    raise SystemExit("only https links may enter the ordinary browser flow")
version = policy["policyVersion"]
if not isinstance(version, int) or version < 1:
    raise SystemExit("policyVersion must be a positive integer")
print(f"validated_policy={version} allowed_tags={len(allowed_tags)}")

事实依据与适用边界

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

本文判断事实或工程依据适用限制
WebView 的 JavaScript、导航和宿主交互需要由应用明确配置和控制。Build Android WebView apps 说明 WebView 内容加载、JavaScript 和页面与应用交互的基础方式。页面能够展示不代表 bridge、缓存、origin、文件访问和导航策略已经正确。
WebView Native bridge 可能因 frame 或 origin 隔离不足而暴露宿主能力。Android WebView native bridge risks 描述 addJavascriptInterface 和 message channel 的风险与缓解方向。风险指南不说明某个业务 bridge 已可被利用,也不能替代目标页面运行验证。
WebView 文件访问、JavaScript 和不可信内容组合会扩大本地文件与脚本风险。Android WebView unsafe file inclusion 说明不安全文件访问配置及相关风险和缓解措施。关闭文件访问仍需验证 URL allowlist、重定向、TLS、图片加载和 bridge 权限。
system instruction 会影响模型行为,但不能作为输出执行和工具权限的安全边界。Firebase AI Logic system instructions 说明系统指令随请求提供并影响模型回复的方式。指令不能完全阻止泄露、越权或不符合预期的输出,应用仍需独立校验渲染内容。
Android 数据声明需要覆盖应用代码与第三方 SDK 实际收集或共享的数据。Declare Android app data use 说明开发者需根据应用和第三方库的数据行为完成 Data safety 信息。商店声明不是运行时隔离、日志脱敏、链接确认或 WebView 配置控制。
渲染策略的发布过程应保留来源、构建、验证和变更证据。NIST SP 800-218 SSDF 给出组织级安全软件开发与供应链风险管理实践。SSDF 不定义具体 Markdown allowlist,也不证明御盾或任何 App 具备特定防护能力。
AI Markdown 应先解析为受控 AST,再按允许节点重建视图。工程判断:结构化重建可以把标签、属性、URL 和资源上限变成可审计门禁,避免依赖字符串替换。解析器本身、扩展语法和重建实现仍需版本绑定与目标 App 测试,静态策略不能证明无旁路。
AI 内容 WebView 应与带登录、支付或 Native bridge 的可信 WebView 分开。工程判断:独立实例能把脚本、文件、cookie、bridge 与导航能力限制在单一展示用途。隔离方案依赖目标架构;是否真正分离要检查实例、配置、缓存、回调与运行网络回执。

工程常见问题

模型只输出 Markdown,是否就不需要清洗?

仍需清洗。Markdown 解析器可能支持原始 HTML、自动链接和扩展节点,应解析为 AST 后按节点与属性 allowlist 重建。

用正则删除 script 标签能否解决危险 HTML?

不能作为完整边界。编码、嵌套、属性和解析器差异会让字符串替换失效;应拒绝原始 HTML,并对解析树执行结构化策略。

关闭 JavaScript 后,WebView 是否已经安全?

还没有。还需关闭文件与 content 访问、不注册 bridge、限制导航和图片、隔离实例,并验证缓存、重定向和用户点击。

system instruction 要求模型不要输出链接,能否替代 URL allowlist?

不能。模型输出仍可能偏离指令,应用必须解析实际 URL,检查 scheme、host、动作类型并在外跳前取得用户确认。

为什么不复用应用已有的登录 WebView 展示 AI 回复?

已有实例可能持有 cookie、历史状态、JavaScript 或 Native bridge。AI 内容应使用最小能力的独立渲染实例,避免继承高权限上下文。

策略 JSON 校验通过是否代表渲染链已经安全?

不代表。它只检查声明,还需验证真实解析器、WebView 设置、URL 重定向、图片、缓存、SDK 和设备运行回执。

想用自己的 App 验证?

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

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