Ohhnews

分类导航

$ cd ..
foojay原文

App Shield:服务器不应信任调用它的应用

#app shield#应用安全#移动开发#后端验证#安全令牌

[LOADING...]

任何仅在手机上运行的安全检查都可以在该手机上被修补绕过。App Shield 通过向受保护的请求附加一个由服务器验证的短期证明令牌,将最终决策转移到您的服务器。

来自泰国的问候。我被家人拖来这里度过一个强制假期。这是个美丽的国家,但海滩、阳光和大海并不是我的菜。GitHub Actions 的停机也无济于事,所以本周进展比平时慢。还有几个有趣的 PR 仍在进行中,我们决定不急于求成。

本周一览

  • App Shield 将设备完整性、证书固定和您的后端连接起来,而不是让应用信任自身的判断。
  • OpenType 字体 现在支持 .otf 文件在受支持的各平台上使用。CSS 编译器还会在多种字体错误到达设备之前将其捕获。
  • Windows 桌面构建器 正在迁移到更新的机器。这是较旧的 JavaSE Windows 目标,而不是原生 Win32 目标。
  • Maven 仓库迁移 已进入第二阶段。新生成的项目现在将依赖项和插件指向我们的 R2 仓库。
  • push.codenameone.com 将于 8 月 8 日星期六重定向到新的云服务。

本地检查并非安全边界

Codename One App Shield 是一个企业级应用证明层。它向 Apple App Attest 或 Google Play Integrity 请求一个由硬件支持的声明,通过 Codename One 服务验证该声明,并向应用签发一个短期有效的 ES256 令牌。您的后端在执行敏感操作之前会验证该令牌。

我们已经服务多家银行客户,高安全性需求多年来塑造了 Codename One。Java 正是这种契合的一部分。这些团队获得了成熟的分析工具、熟悉的类型系统,以及一个可供审查的应用代码库,而不是各自独立的 iOS 和 Android 实现。

Java 本身并不是安全边界。构建流水线为攻击者增加了有用的阻力,但意志坚定的攻击者仍然可以对受其控制的客户端进行逆向工程。

控制项默认状态作用
iOS 原生编译开启ParparVM 将应用的 Java 字节码转换为 C 代码,然后构建为原生二进制。
代码混淆开启移除有用的名称,使静态检查更加困难。
调试标志发布构建中关闭阻断常规生产调试路径。
Root、越狱、Frida 和无障碍检查可选为特定风险添加设备端信号或启动时门禁。
安全屏幕与剪贴板限制可选减少截屏和剪贴板暴露。它不是对键盘记录器的防御。
App Shield企业版可选让您的后端基于服务器验证的证明令牌而不是客户端布尔值来操作。

最后一行的区别正是关键所在。被修改的应用可以强制本地的 isDeviceCompromised() 调用返回 false,但它无法铸造由您服务器信任的密钥签名的有效令牌。

从硬件声明到受保护的 API

App Shield 将平台证明提供方、Codename One 验证服务和您的后端连接在一起:

[LOADING...]

Nonce 可防止被捕获的平台声明成为永久性的重放凭据。该令牌标识预期的包和平台,携带策略决策,并可绑定到一个请求体。该服务还可以包含针对 Root、越狱、挂钩框架、模拟器、调试器、重打包或不可信无障碍服务的信号。

您的后端仍然是执行点。客户端报告它看到的内容,服务评估证明和策略,您的 API 决定是否转移资金、返回个人数据、要求升级认证或拒绝调用。

应用端配置是一份主机列表

codenameone_settings.properties 中启用注入引擎:

$ properties
codename1.arg.shield.enabled=true

然后注册应接收令牌的主机:

$ java
AppShield.init(new ShieldConfig()
        // A request without a token must not leave the device.
        .protect("api.mybank.example", HostPolicy.ENFORCED)
        // Other subdomains get a token when one is available.
        .protect("*.mybank.example", HostPolicy.PROTECTED));

ConnectionRequest request = new ConnectionRequest(
        "https://api.mybank.example/transfer", true);
NetworkManager.getInstance().addToQueueAndWait(request);

当 App Shield 拥有应用的网络守卫时,ConnectionRequestRestRequestBuilder 以及基于 NetworkManager 构建的其他代码会自动通过它。App Shield 在网络线程上附加 X-CN1-Attest,并对照当前 SPKI 固定集合检查证书链。未注册的主机则不受影响。

这两种策略编码了不同的故障选择:

策略如果没有有效令牌可用
HostPolicy.PROTECTED发送不带令牌的请求。后端可以降级或拒绝它。
HostPolicy.ENFORCED在请求离开设备之前失败。用于少数绝不允许未验证请求的端点。

如果您的应用已有网络守卫

NetworkManager 接受一个 NetworkGuard,并在第一次调用 setNetworkGuard() 后密封该槽位。通常的配置是在应用 init(Object) 方法的顶部调用 AppShield.init(),在任何库或应用代码安装守卫之前。

如果先安装了其他守卫,App Shield 无法替换它。初始化仍会继续,但普通请求既不会收到证明令牌,也不会收到 App Shield 的证书固定检查。这包括标记为 ENFORCED 的主机:在请求离开设备之前,如果没有盾牌守卫,任何逻辑都不会看到该策略。

需要自带守卫的应用必须安装一个复合守卫,并将每个回调转发给 App Shield 的守卫:

$ java
final NetworkGuard shield = AppShield.getNetworkGuard();

NetworkManager.setNetworkGuard(new NetworkGuard() {
    public void beforeRequest(ConnectionRequest request) throws IOException {
        request.addRequestHeader("X-My-Trace", newTraceId());
        shield.beforeRequest(request);
    }

    public boolean isCertificateCheckRequired(String url) {
        return shield.isCertificateCheckRequired(url);
    }

    public void checkCertificates(ConnectionRequest request,
            ConnectionRequest.SSLCertificate[] certificates) throws IOException {
        shield.checkCertificates(request, certificates);
    }

    public String[] interestingResponseHeaders() {
        return shield.interestingResponseHeaders();
    }

    public void afterResponse(ConnectionRequest request, int responseCode,
            String[] headers) {
        shield.afterResponse(request, responseCode, headers);
    }
});

AppShield.init(new ShieldConfig()
        .protect("api.mybank.example", HostPolicy.ENFORCED));

AppShield.getNetworkGuard() 可以在 init() 之前安全调用,因为守卫在处理请求时会读取配置。仅转发 beforeRequest() 是不够的。证书回调用于强制执行固定集合,而响应回调则让 App Shield 丢弃被拒绝的令牌。如果您的守卫还捕获响应头,请返回两个守卫的响应头名称的并集,并在向 App Shield 传入其值时保持该顺序。

模拟器现在具有 模拟 > App Shield 控件,可用于模拟被拒绝的证明、过期令牌、设备受损信号和强制证书固定不匹配。这样无需错误配置真实服务器即可测试失败路径。

公共 API 位于开源核心中。没有企业版引擎的构建会降级为文档中说明的空操作,因此共享代码仍然可以编译和运行。显式请求 shield.enabled=true 但没有授权的云构建会失败并给出解释,而不是静默生成未受保护的二进制文件。

App Shield 不保护什么

App Shield 不会让应用变得不可破解。它提高了从被修改的应用调用受保护后端的成本,并为服务器提供可进行密码学验证的策略输入。

真实的已证明设备仍然可以为攻击者中继请求。短期令牌有效期和负载绑定可以减少重放,但并不能取代后端授权、速率限制或对业务操作本身的检查。

它也无法覆盖它看不到的流量。基于 ConnectionRequest 的 API 会自动获得令牌和固定校验。第三方原生 HTTP 客户端需要手动附加令牌。BrowserComponent 可以在其初始导航时接收令牌,但已加载页面发出的请求仍不在框架范围内。在 iOS 或浏览器中,WebSocket 握手头无法通过平台套接字获取,WebSocket 证书固定也未开放。

证书固定有其自身的运营风险。App Shield 固定的是公钥而非整张证书,因此同一密钥上的证书续期不会导致应用失效。您仍然应该先在监控模式下推出服务器策略,衡量 would_deny 流量,然后才拒绝请求。

完整的线路格式、失败状态、传输边界、固定生命周期和后端示例均位于 App Shield 开发人员指南 中。

OpenType 字体现在无需重命名即可使用

PR #5508 使 .otf 成为 iOS、tvOS、watchOS、Android、JavaSE、JavaScript、Windows 和 Linux 上受支持的字体扩展名。这纠正了一个不一致的路径:某些工具可以解析 OpenType 字体,但设备打包时忽略了其扩展名。

现在,您可以将原始文件放在包含 CSS 的目录中或其子目录中:

$ style
@font-face {
    font-family: "Brand Display";
    src: url("fonts/BrandDisplay.otf");
}

Title {
    font-family: "Brand Display";
}

CSS 编译器会在构建期间读取本地字体。现在,它会报告文件缺失、字体无法读取、PostScript 名称缺失、路径超出 CSS 目录,或两个不同文件在打包后会冲突等问题。.woff 等 Web 字体格式仍不受支持。

面向 JavaSE Windows 目标的新构建器

我们正在关闭旧的 Windows 桌面构建机器,并用更新的服务器替换它们。这些机器构建基于 JavaSE 的旧版 Windows 桌面目标。它们与新的原生 Win32 目标是分开的。

旧的构建器崩溃得太频繁,使该目标落后于工具链的其余部分。我们希望替换后能减少这些失败,并最终让 Java 17 项目 可用于这条桌面构建路径。这项工作仍处于服务器部署阶段,因此请将 JDK 17 视为预期结果,直到我们完成真实构建验证。

新项目现在通过 R2 解析 Codename One 依赖

Maven 仓库迁移第二阶段 今天合并。应用原型、库原型和 Initializr 现在将 https://repo.codenameone.com/maven2 放入两个仓库列表中。

Maven 将普通依赖项和构建插件放在不同的列表中。这两个块都很重要:

$ xml
<repository>
    <id>codenameone</id>
    <url>https://repo.codenameone.com/maven2</url>
    <releases>
        <enabled>true</enabled>
    </releases>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
</repository>

<pluginRepository>
    <id>codenameone-plugins</id>
    <url>https://repo.codenameone.com/maven2</url>
    <releases>
        <enabled>true</enabled>
    </releases>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
</pluginRepository>

新生成的构建现在通过 R2 解析 Codename One 版本。现有项目可以在计划中的 8 月 28 日切换之前添加相同的块。如果观测期保持干净,我们将于该日停止向 Maven Central 发布新版本。原型查找本身仍从 Central 开始;将该查找迁移过去是第三阶段。

仓库迁移文章 解释了日期、工件保留、签名以及防止部分发布的保障措施。

推送主机名切换将于周六进行

8 月 8 日(星期六),我们将把 push.codenameone.com 重定向到 cloud.codenameone.com。我们在 Push V3 发布文章 中已经宣布了新服务和兼容性端点。

现有推送代码应该可以继续工作,因为新服务接受经典请求格式。在重定向前,您可以通过仅更改服务器上的主机名来测试完全相同的路径:

$ diff
-https://push.codenameone.com/push/push
+https://cloud.codenameone.com/push/push

向您支持的每个平台发送一个真实通知。测试可见通知、数据负载和冷启动。如果周六切换后结果与旧主机不同,请尽快 提交 Issue 或通过网站联系我们。

App Shield 是本次发布背后的更大方向:安全控制应该在应用、构建和服务器之间组合起来。OpenType 支持、新的 Windows 构建器、R2 迁移和推送切换是较小的变化,但它们遵循相同的规则。可靠的道路应该是常规道路,而失败应该在我们能够采取行动的地方显现出来。

本文 App Shield:您的服务器不应信任调用它的应用 首发于 foojay