# Expo 应用 API 密钥安全：哪些该放服务器

> EXPO_PUBLIC_ 变量会被用户读取，密钥必须放服务器；再用两个测试账号验证访问权限。

原始页面：[Expo 应用 API 密钥安全：哪些该放服务器](https://devoria.fr/zh-hans/blog/expo-api-secret-key-security)
作者：[Mehdi Azizi](https://devoria.fr/zh-hans/mehdi-azizi)
发布：2026-09-29
内容最后修订：2026-10-09


## 环境变量不一定是密钥

在 Expo 应用中，以 `EXPO_PUBLIC_` 为前缀的变量是给应用代码用的，不能放服务器密钥。Expo 说明，这些值会被打包进编译后的 JavaScript，应用的用户可以读到。所以，把密钥挪到 `.env` 文件里并不能保护它。[来源：Expo 环境变量](https://docs.expo.dev/guides/environment-variables/)。

在让 AI“接入 API”之前，先给你交给它的东西分类：服务地址、本来就设计为公开的密钥，还是能执行特权操作的密钥。这个分类决定了代码该在哪里运行。

下表是一份设计参考，不是对你应用的审计。

| 内容                                        | 放在哪里                   | 需要检查什么                                                 |
| ------------------------------------------- | -------------------------- | ------------------------------------------------------------ |
| 后端公开地址                                | 应用配置                   | 指向正确的环境。                                             |
| Supabase publishable 密钥，或旧版 anon 密钥 | 权限配置正确时可放在应用中 | 数据规则确实能阻止访问其他用户的账号。                       |
| Supabase secret 或 service_role 密钥        | 只放服务器                 | 不出现在移动端 bundle 中，也不出现在客户端可访问的日志里。   |
| AI 服务商的密钥                             | 只放服务器                 | 应用调用你的服务器，由服务器先检查用户和限额，再调用服务商。 |

Supabase 区分了供客户端使用的密钥和供可信环境使用的密钥。公开密钥只用来标识项目，它不能代替数据权限。[来源：Supabase API 密钥](https://supabase.com/docs/guides/getting-started/api-keys)。

## 示例：一个虚构的读书笔记应用

假设有个应用，Léa 和 Sami 在里面记录自己的书和私人笔记。这个例子是用来准备测试的，并不是在某个 Devoria 应用上得到的结果。

正常流程看起来没问题：Léa 登录，看到自己的笔记。但这还没回答关键问题：如果 Léa 请求 Sami 的笔记，服务器会拒绝吗？

界面里的过滤，比如“显示当前登录用户的笔记”，只是改善显示。授权还必须在数据层执行。在 Supabase 中，Row Level Security 规则可以根据会话限制可访问的行。Supabase 建议对通过 API 暴露的表启用它，并说明了读取和写入策略。[来源：Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security)。

在你自己的测试项目里，把每条笔记关联到它的所有者，并分别定义谁可以读取、创建、修改和删除。文件也要检查：保护了数据库中的一行，并不能证明某张图片是私密的。

## 用两个测试账号执行的验收步骤

准备两个你自己控制的账号，每个账号里放一本不同的书和一条容易辨认的短笔记。在获得授权的测试环境里使用虚构数据。

1. **读取自己的笔记。** 账号 A 能看到笔记 A。
2. **用 A 读取 B 的笔记。** 请求不会泄露其私密内容。
3. **用 A 修改 B 的笔记。** 服务器拒绝，笔记 B 保持不变。
4. **用 A 以 B 的名义创建。** 服务器拒绝这种归属。
5. **无会话读取。** 不返回任何私密内容。
6. **用 A 打开 B 的私密文件。** 文件无法访问。

记录实际结果、测试的版本以及可能的错误。“规则已启用”不是测试结果。[资源页中的验收表](https://devoria.fr/zh-hans/resources)可以作为保存这些证据的基础。

## 让付费调用经过你的服务器

比如一个用[AI API](https://devoria.fr/zh-hans/blog/guideline-5-1-2-third-party-ai-att-data-sharing)总结书籍的功能，流程可以是：应用 → 你的服务器 → 服务商 → 你的服务器 → 应用。密钥留在服务器上。但光有这个架构还不够：对服务器的访问也必须受控。

在这个虚构示例中，服务器尤其要验证会话、确认这本书属于该用户、限制重复请求，并在服务商无响应时返回易懂的错误。客户端不能自己决定某个用户享有付费权限。

准备四个测试：未登录用户、其他账号的书、达到限额、服务商不可用。最后一个用来确认应用能退出加载状态，且不丢失用户输入。

## 你可以让编程助手做什么

下面是一条审查指令，按你的代码仓库调整，不要把你的密钥值贴进去：

> 列出移动端代码和服务器使用的变量，只显示变量名。找出客户端中存在的特权调用。针对私密数据，描述读取和写入的控制方式，然后提出一套使用两个测试账号的验收步骤。未经确认，不要轮换任何密钥，也不要修改生产环境。

对照相应代码阅读它的回答。助手能发现不一致之处，但它的回答不能代替真正执行测试。[用 Claude Code 创建应用](https://devoria.fr/zh-hans/blog/claude-code-iphone-app-tutorial)这篇指南讲了如何拆分这项工作。

## 继续开发应用之前

你应该能简单说清三件事：哪些值是公开的，哪些操作经过服务器，哪些测试证明一个账号看不到另一个账号的数据。如果其中有一点还模糊，先理清楚再加新功能。

[创建 iPhone 应用](https://devoria.fr/zh-hans/expertise/create-an-iphone-app)页面把这一步放回完整项目中来看。想按顺序推进的话，[iOS Kit](https://devoria.fr/zh-hans/kits/ios) 提供四个步骤的路线，以及已经包含账号和 AI 功能的 Starter Code。如果不知道该从哪一步继续，[免费诊断](https://devoria.fr/zh-hans/diagnostic)会帮你理顺项目，并把我的 Apple 防拒审清单发给你。

## 常见问题

### Expo 应用里的 API 密钥应该放在哪里？

放在你的服务器上，绝不放进应用。Supabase 的 secret 或 service_role 密钥，以及 AI 服务商的密钥，都只能留在服务器端。应用调用你的服务器，由服务器先检查用户和限额，再调用服务商。确认这些密钥都不会出现在移动端 bundle 中，也不会出现在客户端可访问的日志里。

### .env 文件能保护 Expo 里的 API 密钥吗？

单靠它不行。以 `EXPO_PUBLIC_` 为前缀的变量是给应用代码用的，不能放服务器密钥。Expo 说明，这些值会被打包进编译后的 JavaScript，应用的用户可以读到。所以，把密钥挪到 `.env` 文件里并不能保护它。

### 公开 API 密钥和私密密钥有什么区别？

公开密钥本来就是要随应用发布的：它标识项目，但不能代替数据权限。私密密钥能执行特权操作，只能在可信环境中使用。在 Supabase 中，publishable 密钥或旧版 anon 密钥在权限配置正确时可以留在应用里。secret 和 service_role 密钥则留在服务器上。

### 如何在 Expo 应用中调用 AI API 而不暴露密钥？

让调用经过你的服务器：应用调用服务器，服务器调用服务商，再把结果返回给应用。密钥留在服务器上。服务器还要验证会话、限制重复请求，并在服务商无响应时返回易懂的错误。客户端不能自己决定某个用户享有付费权限。

### 如何确认每个用户只能看到自己的数据？

准备两个你自己控制的测试账号，每个账号里放一条容易辨认的笔记。用账号 A 尝试读取、修改账号 B 的内容，以 B 的名义创建内容，再尝试打开 B 的私密文件。也要做无会话测试：每个请求都应被拒绝或不泄露任何内容。记录实际结果，因为“规则已启用”不是测试结果。

### 如何让 AI 检查我应用里的密钥？

让它列出移动端代码和服务器的变量，只显示变量名，不要贴你的密钥值。再让它找出客户端中存在的特权调用，并提出一套使用两个测试账号的验收步骤。禁止它在未经确认的情况下轮换密钥或修改生产环境。它的回答不能代替真正执行测试。


## 你的项目

[iOS Kit](https://devoria.fr/zh-hans/kits/ios)：用 Claude Code 做你的 iPhone App：我自己 App 的起步代码、检查其工作的 skills，以及 Apple Review Kit、App Store Screenshots Kit 和 ASO Kit。129,99 €。
