# Khóa API bí mật trong app Expo: cần bảo vệ gì?

> Khóa công khai Supabase, secret phía server, biến Expo: phân biệt thứ gì được đưa vào app iPhone và kiểm tra quyền truy cập bằng hai tài khoản.

Trang gốc: [Khóa API bí mật trong app Expo: cần bảo vệ gì?](https://devoria.fr/vi/blog/khoa-api-bi-mat-app-expo)
Tác giả: [Mehdi Azizi](https://devoria.fr/vi/mehdi-azizi)
Ngày đăng: 2026-09-29
Lần sửa nội dung gần nhất: 2026-10-09


## Biến môi trường chưa chắc đã là bí mật

Trong một app Expo, biến có tiền tố `EXPO_PUBLIC_` được dùng cho code của app. Nó không được chứa secret phía server. Expo giải thích rằng các giá trị này được nhúng vào JavaScript đã biên dịch và người dùng app có thể đọc được. Vì vậy, chỉ chuyển khóa vào file `.env` thì chưa đủ để bảo vệ nó. [Nguồn: biến môi trường Expo](https://docs.expo.dev/guides/environment-variables/).

Trước khi nhờ AI "nối API", bạn hãy phân loại thứ mình đưa cho nó: địa chỉ của một dịch vụ, một khóa vốn được thiết kế để công khai, hay một khóa cho phép thực hiện các thao tác đặc quyền. Cách phân loại này quyết định code phải chạy ở đâu.

Bảng dưới đây là khung thiết kế, không phải kết quả kiểm tra app của bạn.

| Thành phần                                   | Đặt ở đâu                              | Cần kiểm tra                                                                               |
| -------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------ |
| Địa chỉ công khai của backend                | Cấu hình của app                       | Nó trỏ đúng môi trường.                                                                    |
| Khóa Supabase publishable, hoặc khóa anon cũ | Trong app nếu quyền được cấu hình đúng | Quy tắc dữ liệu thực sự chặn việc truy cập tài khoản của người dùng khác.                  |
| Khóa Supabase secret hoặc service_role       | Chỉ trên server                        | Nó không xuất hiện trong bundle mobile, cũng không có trong log mà client truy cập được.   |
| Khóa bí mật của nhà cung cấp AI              | Chỉ trên server                        | App gọi server của bạn, server kiểm tra người dùng và giới hạn trước khi gọi nhà cung cấp. |

Supabase phân biệt khóa dành cho client và khóa bí mật dành cho môi trường tin cậy. Khóa công khai dùng để nhận diện project; nó không thay thế cho quyền truy cập dữ liệu. [Nguồn: khóa API Supabase](https://supabase.com/docs/guides/getting-started/api-keys).

## Ví dụ: một app sổ tay đọc sách giả định

Giả sử có một app trong đó Léa và Sami lưu sách và ghi chú riêng tư của họ. Ví dụ này dùng để chuẩn bị test; nó không mô tả kết quả thu được trên một app của Devoria.

Luồng bình thường có vẻ ổn: Léa đăng nhập và thấy ghi chú của mình. Nhưng điều đó chưa trả lời câu hỏi quan trọng: server có từ chối yêu cầu của Léa nhắm tới một ghi chú của Sami không?

Một bộ lọc ở giao diện, kiểu "hiển thị ghi chú của người dùng đang đăng nhập", chỉ cải thiện phần hiển thị. Việc phân quyền còn phải được áp dụng ở tầng dữ liệu. Trong Supabase, quy tắc Row Level Security cho phép giới hạn các dòng truy cập được theo phiên đăng nhập. Supabase khuyên bật chúng cho các bảng được API công khai và giải thích các policy đọc và ghi. [Nguồn: Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security).

Với project test của riêng bạn, hãy gắn mỗi ghi chú với chủ sở hữu của nó và định nghĩa riêng ai được đọc, tạo, sửa và xóa. Kiểm tra cả file nữa: bảo vệ một dòng trong database không chứng minh được một ảnh là riêng tư.

## Kịch bản test với hai tài khoản

Chuẩn bị hai tài khoản do bạn kiểm soát, mỗi tài khoản một cuốn sách khác nhau và một ghi chú ngắn dễ nhận ra. Dùng dữ liệu giả trong một môi trường test được phép.

1. **Đọc ghi chú của mình.** Tài khoản A thấy ghi chú A.
2. **Đọc ghi chú của B từ A.** Yêu cầu không làm lộ nội dung riêng tư.
3. **Sửa ghi chú của B từ A.** Server từ chối; ghi chú B không thay đổi.
4. **Tạo dữ liệu dưới tên B từ A.** Server từ chối việc gán này.
5. **Đọc khi không có phiên đăng nhập.** Không có nội dung riêng tư nào được trả về.
6. **Mở file riêng tư của B từ A.** Không truy cập được file.

Ghi lại kết quả thực tế, phiên bản đã test và lỗi nếu có. "Quy tắc đã được bật" không phải là kết quả test. [Bảng kịch bản test trong mục tài nguyên](https://devoria.fr/vi/resources) có thể làm nền để lưu lại các bằng chứng này.

## Cho các lệnh gọi trả phí đi qua server của bạn

Với một tính năng tóm tắt sách bằng [một API AI](https://devoria.fr/vi/blog/guideline-5-1-2-ai-ben-thu-ba-att-chia-se-du-lieu), luồng có thể là: app → server của bạn → nhà cung cấp → server của bạn → app. Khóa bí mật nằm lại trên server. Riêng kiến trúc này thì chưa đủ: quyền truy cập server cũng phải được kiểm soát.

Trong ví dụ giả định, server cần kiểm tra phiên đăng nhập, kiểm tra cuốn sách thuộc về người đó, giới hạn các yêu cầu lặp lại và trả về lỗi dễ hiểu nếu nhà cung cấp không phản hồi. Client không tự quyết định một người dùng có quyền truy cập trả phí.

Chuẩn bị bốn phép thử: người dùng chưa đăng nhập, sách của tài khoản khác, đã chạm giới hạn và nhà cung cấp không khả dụng. Phép thử cuối giúp kiểm tra app thoát khỏi trạng thái loading mà không mất nội dung đã nhập.

## Những gì bạn có thể nhờ trợ lý code làm

Đây là một yêu cầu review để bạn điều chỉnh theo repo của mình, không dán giá trị bí mật vào:

> Liệt kê các biến được code mobile và server sử dụng, chỉ hiển thị tên của chúng. Xác định các lệnh gọi đặc quyền nằm ở phía client. Với dữ liệu riêng tư, mô tả các kiểm soát đọc và ghi, rồi đề xuất kịch bản test với hai tài khoản test. Không xoay vòng khóa hay sửa production khi chưa được xác nhận.

Đọc câu trả lời song song với code tương ứng. Trợ lý có thể phát hiện điểm bất nhất; câu trả lời của nó không thay thế việc chạy test. Hướng dẫn [tạo app với Claude Code](https://devoria.fr/vi/blog/tao-app-iphone-voi-claude-code-huong-dan) giải thích cách chia nhỏ công việc này.

## Trước khi phát triển tiếp app

Bạn phải giải thích được ba điều một cách đơn giản: giá trị nào là công khai, thao tác nào đi qua server và test nào cho thấy một tài khoản không thấy dữ liệu của tài khoản khác. Nếu điểm nào còn mơ hồ, hãy làm rõ trước khi thêm tính năng.

Trang [tạo app iPhone](https://devoria.fr/vi/expertise/tao-ung-dung-iphone) đặt bước này vào bối cảnh toàn bộ project. Để đi đúng thứ tự, [iOS Kit](https://devoria.fr/vi/kits/ios) có lộ trình bốn bước và code khởi đầu đã có sẵn tài khoản và AI. Nếu bạn không biết nên tiếp tục từ bước nào, [bài chẩn đoán miễn phí](https://devoria.fr/vi/diagnostic) sẽ sắp xếp lại project của bạn và gửi bạn checklist chống bị Apple từ chối của mình.

## Câu hỏi thường gặp

### Nên đặt khóa API bí mật ở đâu trong app Expo?

Trên server của bạn, không bao giờ trong app. Khóa Supabase secret hoặc service_role, cũng như khóa bí mật của nhà cung cấp AI, chỉ được nằm phía server. App gọi server của bạn, server kiểm tra người dùng và giới hạn trước khi gọi nhà cung cấp. Kiểm tra để không khóa nào xuất hiện trong bundle mobile hay trong log mà client truy cập được.

### File .env có bảo vệ được khóa API trong Expo không?

Tự nó thì không. Biến có tiền tố `EXPO_PUBLIC_` được dùng cho code của app và không được chứa secret phía server. Expo giải thích rằng các giá trị này được nhúng vào JavaScript đã biên dịch và người dùng app có thể đọc được. Vì vậy, chỉ chuyển khóa vào file `.env` thì chưa đủ để bảo vệ nó.

### Khóa API công khai và khóa bí mật khác nhau thế nào?

Khóa công khai được thiết kế để đưa vào app: nó nhận diện project nhưng không thay thế cho quyền truy cập dữ liệu. Khóa bí mật cho phép các thao tác đặc quyền và chỉ dành cho môi trường tin cậy. Với Supabase, khóa publishable, hoặc khóa anon cũ, có thể nằm trong app nếu quyền được cấu hình đúng. Khóa secret và service_role nằm trên server.

### Làm sao gọi API AI từ app Expo mà không lộ khóa?

Cho lệnh gọi đi qua server của bạn: app gọi server, server gọi nhà cung cấp rồi trả kết quả về app. Khóa bí mật nằm lại trên server. Server cũng phải kiểm tra phiên đăng nhập, giới hạn các yêu cầu lặp lại và trả về lỗi dễ hiểu nếu nhà cung cấp không phản hồi. Client không tự quyết định một người dùng có quyền truy cập trả phí.

### Làm sao kiểm tra mỗi người dùng chỉ thấy dữ liệu của chính mình?

Chuẩn bị hai tài khoản test do bạn kiểm soát, mỗi tài khoản có một ghi chú dễ nhận ra. Từ tài khoản A, thử đọc, sửa và tạo dữ liệu dưới tên tài khoản B, rồi mở file riêng tư của B. Thử cả khi không có phiên đăng nhập: mọi yêu cầu đều phải bị từ chối hoặc không làm lộ gì. Ghi lại kết quả thực tế, vì "quy tắc đã được bật" không phải là kết quả test.

### Làm sao nhờ AI kiểm tra các khóa trong app của mình?

Yêu cầu nó liệt kê các biến của code mobile và server, chỉ hiển thị tên, và đừng dán giá trị bí mật vào. Yêu cầu nó xác định các lệnh gọi đặc quyền ở phía client và đề xuất kịch bản test với hai tài khoản test. Cấm mọi việc xoay vòng khóa hay sửa production khi chưa được xác nhận. Câu trả lời của nó không thay thế việc chạy test.


## Dự án của bạn

[iOS Kit](https://devoria.fr/vi/kits/ios): Tạo app iPhone với Claude Code: code khởi đầu từ các app của mình, các skill kiểm tra công việc của nó, và các kit Apple Review, Screenshots, ASO. 129,99 €.
