Biến Môi Trường: khái niệm, cách sử dụng và cấu hình trong dự án di động

Tác giả: IT Sectr Đã đăng: 2026-05-31 Thời gian đọc: 8 phút

Biến môi trường là các giá trị động được truyền cho ứng dụng khi khởi động để cấu hình hành vi mà không cần thay đổi mã. Chúng cho phép tách biệt cấu hình phát triển, kiểm thử và sản xuất. Theo Twelve-Factor App, 2025, cấu hình nên được lưu trữ trong biến môi trường, không phải trong mã. Biến môi trường đảm bảo quản lý an toàn các khóa API, URL backend và cờ tính năng.

Những điểm chính

  • Biến môi trường tách biệt cấu hình ứng dụng khỏi mã nguồn cho các môi trường chạy khác nhau
  • Tệp .env lưu trữ biến ở định dạng KEY=VALUE và bị loại khỏi kho lưu trữ qua .gitignore
  • iOS sử dụng xcconfig và Build Settings để truyền biến tại thời điểm biên dịch
  • Android sử dụng BuildConfig và gradle.properties để tạo trường cấu hình
  • Bảo mật: khóa và mã thông báo nên được tải qua CI/CD, không lưu trữ trong mã hoặc kho lưu trữ

Biến môi trường là gì

Biến môi trường là cặp khóa-giá trị có thể truy cập được cho tiến trình ứng dụng thông qua API hệ điều hành. Chúng được truyền cho tiến trình khi nó được tạo và chỉ tồn tại trong thời gian chạy của nó. Không giống như các tham số cấu hình được nhúng trong mã nguồn, biến môi trường không yêu cầu biên dịch lại để thay đổi giá trị. Đây là nguyên tắc cơ bản của Twelve-Factor App, đảm bảo sự tách biệt rõ ràng giữa mã và cấu hình.

Trong phát triển di động, biến môi trường giải quyết vấn đề cấu hình khác nhau cho các môi trường: nhà phát triển sử dụng máy chủ cục bộ, người kiểm thử sử dụng staging và người dùng sử dụng sản xuất. Thay vì lưu trữ ba URL backend trong mã với các câu lệnh điều kiện if-else, nhà phát triển truyền một URL qua biến môi trường tại thời điểm xây dựng. Điều này đơn giản hóa mã và loại bỏ rủi ro vô tình sử dụng máy chủ sản xuất trong môi trường kiểm thử.

Lợi thế chính là bảo mật: dữ liệu nhạy cảm không lọt vào kho mã. Khóa API, bí mật Firebase, mã thông báo truy cập backend và chứng chỉ được tải qua CI/CD trực tiếp vào môi trường xây dựng. Nếu kẻ tấn công truy cập được vào kho mã, chúng sẽ không tìm thấy bí mật ở đó, vì chúng được lưu trữ trong kho được bảo vệ của hệ thống CI và chỉ được truyền ở giai đoạn xây dựng tệp nhị phân.

Tại sao cần biến môi trường trong phát triển di động

Các dự án di động có ít nhất ba môi trường: phát triển, staging và sản xuất. Mỗi môi trường yêu cầu bộ cấu hình riêng: URL máy chủ, tên gói, lược đồ ký và chứng chỉ thông báo đẩy. Nếu không có biến môi trường, nhà phát triển phải thay đổi cấu hình thủ công trước mỗi lần xây dựng, dẫn đến lỗi: khóa sản xuất bị quên trong bản dựng kiểm thử có thể gửi thông báo cho người dùng thực hoặc tiêu thụ API trả phí.

Tách biệt môi trường

Biến môi trường cho phép chuyển đổi backend mà không cần thay đổi mã: chỉ cần thay đổi giá trị trong biến API_BASE_URL. Cờ tính năng được quản lý qua các biến như FEATURE_CHAT_ENABLED=true, cho phép bật các tính năng mới trong staging mà không ảnh hưởng đến sản xuất. Mỗi môi trường có tệp .env riêng được tải tại thời điểm xây dựng.

dart
class AppConfig {
  static final String apiBaseUrl =
    const String.fromEnvironment('API_BASE_URL',
      defaultValue: 'http://localhost:8080');
}

Bảo mật khóa

Khóa được mã hóa cứng là một lỗ hổng phổ biến trong ứng dụng di động. Kẻ tấn công dịch ngược APK hoặc IPA bằng các công cụ như jadx hoặc Hopper và trích xuất bí mật từ tệp nhị phân. Ngay cả làm rối mã cũng không bảo vệ được các chuỗi ký tự — chúng dễ dàng bị tìm thấy trong mã sau khi dịch ngược. Biến môi trường giải quyết vấn đề này bằng cách truyền khóa tại thời điểm xây dựng qua CI/CD, nơi chúng bị che trong nhật ký.

kotlin
object Config {
    val apiKey: String =
        System.getenv("API_KEY") ?: throw
            IllegalStateException("API_KEY not set")
}

Tích hợp CI/CD

Biến môi trường tích hợp với các đường ống xây dựng: GitHub Actions, GitLab CI, Bitrise và CircleCI hỗ trợ biến bí mật không hiển thị trong nhật ký. Tại thời điểm xây dựng, CI thay thế các giá trị thích hợp tùy theo nhánh hoặc thẻ: cho nhánh develop sử dụng staging, cho thẻ v* sử dụng sản xuất. Điều này tự động hóa quy trình và loại bỏ yếu tố con người, đảm bảo mỗi bản dựng nhận được bộ cấu hình chính xác.

Tệp .env và thư viện quản lý

Tệp .env là cách tiêu chuẩn để lưu trữ biến môi trường ở định dạng KEY=VALUE. Nó không được bao gồm trong kho lưu trữ; thay vào đó, .env.example được thêm vào với một mẫu của tất cả các biến và giá trị trống. Mỗi nhà phát triển tạo tệp .env riêng với cài đặt cục bộ mà không ảnh hưởng đến cấu hình của các thành viên khác trong nhóm. Các tệp riêng biệt được sử dụng cho các môi trường khác nhau: .env.dev, .env.stage, .env.prod.

bash
# .env.example — mẫu cho nhà phát triển
API_BASE_URL=http://localhost:8080
FEATURE_CHAT_ENABLED=true
SENTRY_DSN=

Đối với các dự án di động, có các thư viện chuyên dụng để làm việc với tệp .env:

  • flutter_dotenv (Flutter) — tải biến từ .env tại thời gian chạy qua dotenv.load()
  • BuildConfig (Android) — tạo các trường được định kiểu từ giá trị build.gradle
  • xcconfig (iOS) — kết nối tệp cấu hình với các lược đồ xây dựng Xcode khác nhau
  • react-native-config (React Native) — quản lý biến qua tệp .env

Cài đặt rẽ nhánh trong CI/CD cho phép thay thế các tệp .env khác nhau: .env.dev cho máy chủ kiểm thử, .env.stage cho tiền phát hành và .env.prod cho xuất bản lên cửa hàng ứng dụng. Các tệp có bí mật được tải từ kho lưu trữ an toàn (Vault, AWS Secrets Manager) và không được lưu trữ trong kho lưu trữ. Điều này đảm bảo rằng ngay cả khi hệ thống kiểm soát phiên bản bị xâm phạm, các bí mật vẫn được bảo vệ.

Biến môi trường trong dự án iOS

Hệ sinh thái iOS sử dụng tệp xcconfig để quản lý biến ở cấp độ xây dựng. Chúng được đính kèm vào lược đồ Xcode và cho phép ghi đè giá trị cho cấu hình Debug và Release. Tệp xcconfig hỗ trợ kế thừa: bạn có thể tạo tệp cơ sở với cài đặt chung và các tệp cụ thể cho từng môi trường.

Cấu hình tệp xcconfig

Tệp xcconfig lưu trữ biến ở định dạng KEY = VALUE và được đính kèm vào lược đồ xây dựng trong Xcode qua cài đặt Configuration. Các biến từ xcconfig có sẵn trong Info.plist qua cú pháp $(TÊN_BIẾN), cho phép các định danh gói và tên ứng dụng khác nhau cho các lược đồ khác nhau. Để nhận dạng môi trường nhanh chóng, hậu tố Dev hoặc Staging được thêm vào tên ứng dụng.

bash
# Config/Dev.xcconfig — cấu hình phát triển
API_BASE_URL = http://localhost:3000
BUNDLE_ID_SUFFIX = .dev
APP_DISPLAY_NAME = MyApp Dev

Mã Swift để đọc biến

Để truy cập biến tại thời gian chạy trong iOS, tệp Configuration.swift được sử dụng, đọc giá trị từ Info.plist qua Bundle.main.object(forInfoDictionaryKey:). Cách tiếp cận này đảm bảo rằng các biến được xác định tại thời điểm xây dựng và có sẵn cho ứng dụng ngay sau khi khởi động. Các giá trị được đọc một lần trong quá trình khởi tạo mô-đun và được lưu vào bộ nhớ đệm để truy cập nhanh trong suốt vòng đời ứng dụng.

swift
enum AppEnvironment {
    static var apiBaseURL: URL {
        guard let urlString = Bundle.main
            .object(forInfoDictionaryKey: "API_BASE_URL"),
              let url = URL(string: urlString as! String)
        else { fatalError("API_BASE_URL is not configured") }
        return url
    }

    static var isChatEnabled: Bool {
        Bundle.main.object(
            forInfoDictionaryKey: "FEATURE_CHAT_ENABLED"
        ) as? Bool ?? false
    }
}

Biến môi trường trong dự án Android

Android hỗ trợ biến môi trường qua BuildConfig — một lớp được tạo tự động có các trường được xác định trong tệp build.gradle của mô-đun. BuildConfig được tạo tại thời điểm biên dịch cho mỗi flavor và loại xây dựng riêng biệt. Điều này cho phép có các giá trị khác nhau cho debug và release mà không cần sử dụng toán tử điều kiện trong mã, cải thiện hiệu suất và bảo mật.

Cấu hình trường BuildConfig

Các trường BuildConfig được đặt qua buildConfigField trong defaultConfig hoặc trong các buildTypes cụ thể. Một buildType hoặc productFlavor riêng được tạo cho mỗi môi trường. Điều này đảm bảo sự cô lập cấu hình nghiêm ngặt: debug sử dụng máy chủ cục bộ, release sử dụng sản xuất. Các trường BuildConfig được định kiểu tĩnh, loại bỏ lỗi khi truy cập chúng trong mã.

groovy
// build.gradle (Module: app)
android {
    defaultConfig {
        buildConfigField "String", "API_BASE_URL",
            "\"http://localhost:8080\""
    }
    buildTypes {
        debug {
            buildConfigField "String", "API_BASE_URL",
                "\"http://dev.api.itsectr.com\""
        }
        release {
            buildConfigField "String", "API_BASE_URL",
                "\"https://api.itsectr.com\""
        }
    }
}

gradle.properties cho các giá trị dùng chung

Tệp gradle.properties trong thư mục gốc dự án lưu trữ các biến Gradle toàn cục. Chúng có sẵn trong tất cả các mô-đun qua cú pháp $variableName và được sử dụng để chỉ định phiên bản phụ thuộc, cờ xây dựng và khóa API. Không giống như BuildConfig, gradle.properties chỉ hoạt động ở giai đoạn cấu hình Gradle, không phải tại thời gian chạy của ứng dụng. Do đó, mật khẩu và khóa API được chỉ định trong gradle.properties không hiển thị trong mã đã dịch ngược, vì chúng chỉ được sử dụng để tạo BuildConfig tại thời điểm biên dịch.

groovy
# gradle.properties
SENTRY_DSN=https://key@sentry.io/project
MAPS_API_KEY=AIzaSy...

Để truyền bí mật an toàn trong các dự án Android, nên sử dụng local.properties (bị loại khỏi VCS) hoặc tải giá trị từ biến CI/CD trong build.gradle qua System.getenv(). Điều này đảm bảo rằng các khóa không lọt vào kho lưu trữ. Khi xuất bản lên Google Play Console, hãy đảm bảo rằng tất cả các khóa gỡ lỗi được thay thế bằng phiên bản sản xuất qua các buildTypes hoặc productFlavors khác nhau với các giá trị BuildConfig tương ứng.

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

Có thể sử dụng biến môi trường trong Flutter không?

, Flutter hỗ trợ biến môi trường qua gói flutter_dotenv để truy cập thời gian chạy hoặc qua kênh gốc cho biến nền tảng. Dart cũng có hàm tạo String.fromEnvironment để truyền giá trị tại thời điểm biên dịch qua --dart-define, đây là cách ưa thích cho các dự án Flutter.

Sự khác biệt giữa BuildConfig và gradle.properties là gì?

BuildConfig là một lớp Java với các trường được định kiểu được tạo tại thời điểm biên dịch cho mỗi buildType và flavor. gradle.properties là một tệp văn bản với các cặp khóa-giá trị có sẵn cho tất cả các mô-đun Gradle ở giai đoạn cấu hình xây dựng. BuildConfig hoạt động tại thời gian chạy của ứng dụng, gradle.properties — chỉ trong tập lệnh Gradle.

Làm thế nào để ngăn tệp .env bị rò rỉ vào kho lưu trữ?

Thêm .env vào tệp .gitignore của kho lưu trữ. Trong kho lưu trữ, chỉ cam kết .env.example với các giá trị trống và mô tả của từng biến. Đối với CI/CD, hãy sử dụng bí mật được mã hóa trong cài đặt GitHub Actions, GitLab CI hoặc Bitrise, được che trong nhật ký và không có sẵn để đọc sau khi hoàn tất xây dựng.

Làm thế nào để truyền biến môi trường qua CI/CD?

Hầu hết các hệ thống CI đều hỗ trợ biến môi trường bí mật. Trong GitHub Actions đó là Secrets, trong GitLab CI — CI/CD Variables, trong Bitrise — Secrets. Tại thời điểm xây dựng, chúng được truyền đến tập lệnh xây dựng qua process.env hoặc System.getenv(). Các biến bí mật không hiển thị trong nhật ký xây dựng và không có sẵn trong các nhánh rẽ của kho lưu trữ.

Cờ tính năng qua biến môi trường là gì?

Cờ tính năng là các biến boolean kiểm soát việc bật hoặc tắt chức năng mà không cần biên dịch lại mã. Ví dụ: FEATURE_NEW_PAYMENT=true bật hệ thống thanh toán mới trong staging để kiểm thử. Trong sản xuất, cùng một cờ được đặt thành false cho đến khi backend được triển khai đầy đủ. Điều này cho phép triển khai các thay đổi một cách an toàn theo từng giai đoạn và thu hồi chúng nếu có sự cố.

Tổng kết

  • Biến môi trường tách biệt cấu hình khỏi mã nguồn cho các môi trường phát triển khác nhau
  • Tệp .env với mẫu .env.example — tiêu chuẩn quản lý biến trong nhóm có phân tách môi trường
  • iOS xcconfig kết nối tệp cấu hình với lược đồ Xcode với hỗ trợ kế thừa và tích hợp Info.plist
  • Android BuildConfig tạo các trường được định kiểu từ build.gradle cho mỗi buildType riêng biệt
  • Bí mật CI/CD truyền dữ liệu nhạy cảm tại thời điểm xây dựng mà không lưu trữ chúng trong kho lưu trữ
  • Cờ tính năng qua biến cho phép bật chức năng trong một môi trường cụ thể mà không cần biên dịch lại
  • Bảo mật: khóa được mã hóa trong CI và không lọt vào tệp nhị phân có thể dịch ngược của ứng dụng

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm