環境変数は、コードを変更せずに動作を設定するために、アプリケーションの起動時に渡される動的な値です。開発、テスト、本番の設定を分離できます。Twelve-Factor App、2025によると、設定はコードではなく環境変数に保存する必要があります。環境変数は、APIキー、バックエンドURL、フィーチャーフラグの安全な管理を保証します。
重要ポイント
環境変数は、オペレーティングシステムのAPIを介してアプリケーションプロセスがアクセスできるキーと値のペアです。プロセスの作成時に渡され、実行中のみ存在します。ソースコードに埋め込まれた設定パラメーターとは異なり、環境変数は値を変更するために再コンパイルを必要としません。これはTwelve-Factor Appの基本原則であり、コードと設定の明確な分離を保証します。
モバイル開発では、環境変数は異なる環境向けの設定の問題を解決します。開発者はローカルサーバーを、テスターはステージングを、ユーザーは本番を使用します。コード内でif-else条件文とともに3つのバックエンドURLを保存する代わりに、開発者はビルド時に環境変数を介して1つのURLを渡します。これによりコードが簡素化され、テスト環境で誤って本番サーバーを使用するリスクが排除されます。
主な利点はセキュリティです。機密データがコードリポジトリに漏れることはありません。APIキー、Firebaseシークレット、バックエンドアクセストークン、証明書はCI/CDを介してビルド環境に直接ロードされます。攻撃者がコードリポジトリにアクセスしても、シークレットはCIシステムの保護されたストレージに保存され、バイナリファイルのビルド段階でのみ渡されるため、見つけることはできません。
モバイルプロジェクトには少なくとも3つの環境があります。開発、ステージング、本番です。各環境には独自の設定セットが必要です:サーバーURL、パッケージ名、署名スキーム、プッシュ通知証明書。環境変数がない場合、開発者はビルドごとに手動で設定を変更する必要があり、エラーの原因になります。テストビルドで本番キーを忘れると、実際のユーザーに通知が送信されたり、有料APIが消費されたりする可能性があります。
環境変数を使用すると、コードを変更せずにバックエンドを切り替えられます。API_BASE_URL変数の値を変更するだけです。フィーチャーフラグはFEATURE_CHAT_ENABLED=trueのような変数で管理され、本番に影響を与えずにステージングで新機能を有効にできます。各環境には独自の.envファイルがあり、ビルド時にロードされます。
class AppConfig {
static final String apiBaseUrl =
const String.fromEnvironment('API_BASE_URL',
defaultValue: 'http://localhost:8080');
}
ハードコードされたキーはモバイルアプリケーションの一般的な脆弱性です。攻撃者はjadxやHopperなどのツールを使用してAPKまたはIPAを逆コンパイルし、バイナリファイルからシークレットを抽出します。難読化でさえ文字列リテラルを保護できません。逆コンパイル後にコード内で簡単に見つかります。環境変数は、CI/CDを介してビルド時にキーを渡すことでこの問題を解決し、ログでマスクされます。
object Config {
val apiKey: String =
System.getenv("API_KEY") ?: throw
IllegalStateException("API_KEY not set")
}
環境変数はビルドパイプラインと統合されます。GitHub Actions、GitLab CI、Bitrise、CircleCIは、ログに表示されないシークレット変数をサポートしています。ビルド時に、CIはブランチまたはタグに応じて適切な値を代入します。developブランチにはステージング、v*タグには本番が使用されます。これによりプロセスが自動化され、人的要因が排除され、各ビルドが正しい設定セットを受け取ることが保証されます。
.envファイルは、KEY=VALUE形式で環境変数を保存する標準的な方法です。リポジトリには含まれず、代わりにすべての変数のテンプレートと空の値を含む.env.exampleが追加されます。各開発者は、チームメンバーの設定に影響を与えずに、ローカル設定を含む独自の.envファイルを作成します。異なる環境には別々のファイルが使用されます:.env.dev、.env.stage、.env.prod。
# .env.example — 開発者向けテンプレート
API_BASE_URL=http://localhost:8080
FEATURE_CHAT_ENABLED=true
SENTRY_DSN=
モバイルプロジェクト向けには、.envファイルを操作するための専門のライブラリがあります:
CI/CDのブランチ設定により、異なる.envファイルを代入できます:テストサーバー用に.env.dev、プレリリース用に.env.stage、アプリストア公開用に.env.prod。シークレットを含むファイルは安全なストレージ(Vault、AWS Secrets Manager)からロードされ、リポジトリには保存されません。これにより、バージョン管理システムが侵害されても、シークレットは保護されたままになります。
iOSエコシステムは、ビルドレベルで変数を管理するためにxcconfigファイルを使用します。これらはXcodeスキームに接続され、DebugおよびRelease設定の値をオーバーライドできます。xcconfigファイルは継承をサポートしており、共通設定を含むベースファイルと、各環境用の固有ファイルを作成できます。
xcconfigファイルはKEY = VALUE形式で変数を保存し、Configuration設定を介してXcodeのビルドスキームに接続されます。xcconfigの変数は$(VARIABLE_NAME)構文を介してInfo.plistで利用可能になり、異なるスキームに異なるバンドル識別子とアプリ名を使用できます。環境をすばやく識別するために、アプリ名にDevまたはStagingの接尾辞が追加されます。
# Config/Dev.xcconfig — 開発設定
API_BASE_URL = http://localhost:3000
BUNDLE_ID_SUFFIX = .dev
APP_DISPLAY_NAME = MyApp Dev
iOSで実行時に変数にアクセスするには、Configuration.swiftファイルを使用し、Bundle.main.object(forInfoDictionaryKey:)を介してInfo.plistから値を読み取ります。このアプローチにより、変数がビルド時に定義され、起動直後にアプリケーションが利用できることが保証されます。値はモジュールの初期化時に1回読み取られ、アプリケーションのライフサイクル全体で高速アクセスするためにキャッシュされます。
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
}
}
AndroidはBuildConfigを介して環境変数をサポートします。これは自動生成されるクラスで、そのフィールドはモジュールのbuild.gradleファイルで定義されます。BuildConfigはコンパイル時にフレーバーとビルドタイプごとに個別に作成されます。これにより、コードで条件演算子を使用せずにデバッグとリリースで異なる値を設定でき、パフォーマンスとセキュリティが向上します。
BuildConfigフィールドは、defaultConfigまたは特定のbuildTypesでbuildConfigFieldを介して設定されます。各環境に個別のbuildTypeまたはproductFlavorが作成されます。これにより、設定の厳格な分離が保証されます。デバッグはローカルサーバー、リリースは本番を使用します。BuildConfigフィールドは静的に型付けされているため、コードでアクセスする際のエラーが排除されます。
// 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ファイルは、グローバルなGradle変数を保存します。これらは$variableName構文を介してすべてのモジュールで利用可能で、依存関係のバージョン、ビルドフラグ、APIキーの指定に使用されます。BuildConfigとは異なり、gradle.propertiesはGradle設定段階でのみ機能し、アプリケーションの実行時には機能しません。したがって、gradle.propertiesで指定されたパスワードとAPIキーは逆コンパイルされたコードで表示されません。コンパイル時にBuildConfigを生成するためだけに使用されるからです。
# gradle.properties
SENTRY_DSN=https://key@sentry.io/project
MAPS_API_KEY=AIzaSy...
Androidプロジェクトでシークレットを安全に転送するには、local.properties(VCSから除外)を使用するか、System.getenv()を介してbuild.gradleのCI/CD変数から値をロードすることをお勧めします。これにより、キーがリポジトリに漏れることがなくなります。Google Play Consoleに公開する際は、すべてのデバッグキーが、対応するBuildConfig値を持つ異なるbuildTypesまたはproductFlavorsを介して本番バージョンに置き換えられていることを確認してください。
よくある質問
はい、Flutterは実行時アクセス用のflutter_dotenvパッケージ、またはプラットフォーム変数用のネイティブチャネルを介して環境変数をサポートしています。Dartには、--dart-defineを介してコンパイル時に値を渡すためのString.fromEnvironmentコンストラクターもあり、Flutterプロジェクトではこちらが推奨される方法です。
BuildConfigは、各buildTypeとflavorのコンパイル時に生成される型付きフィールドを持つJavaクラスです。gradle.propertiesは、ビルド設定段階ですべてのGradleモジュールがアクセスできるキーと値のペアを含むテキストファイルです。BuildConfigはアプリケーションの実行時に機能し、gradle.propertiesはGradleスクリプト内でのみ機能します。
リポジトリの.gitignoreファイルに.envを追加します。リポジトリには、空の値と各変数の説明を含む.env.exampleのみをコミットします。CI/CDには、GitHub Actions、GitLab CI、またはBitriseの設定で暗号化されたシークレットを使用します。これらはログでマスクされ、ビルド完了後に読み取ることはできません。
ほとんどのCIシステムはシークレット環境変数をサポートしています。GitHub ActionsではSecrets、GitLab CIではCI/CD Variables、BitriseではSecretsです。ビルド時に、process.envまたはSystem.getenv()を介してビルドスクリプトに渡されます。シークレット変数はビルドログに表示されず、リポジトリのフォークでは利用できません。
フィーチャーフラグは、コードを再コンパイルせずに機能の有効化または無効化を制御するブール変数です。例:FEATURE_NEW_PAYMENT=trueで、テスト用にステージングで新しい支払いシステムを有効にします。本番では、バックエンドが完全にデプロイされるまで同じフラグはfalseに設定されます。これにより、変更を段階的に安全にロールアウトし、問題が発生した場合にロールバックできます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。