C++ で環境変数を取得する
この記事では、C++ で環境変数を取得する方法をいくつか説明します。
C++ の環境変数にアクセスするには std::getenv
関数を使用する
getenv
は C 標準ライブラリで実装されている POSIX 準拠の関数であり、C++ のソースファイルから <cstdlib>
ヘッダを用いてインポートすることができます。この関数は文字列を唯一の引数として受け取り、それと等しい環境変数名を検索します。
環境変数は通常、すべて大文字で表現されるが、getenv
では、プログラム環境で定義されていても変数名と一致しないため、引数文字列に小文字を入れないようにする必要があります。この関数は、対応する変数の値が格納されている char*
型を返します。
プログラムがその場所を変更しないようにする必要があるため、getenv
の戻り値を const char*
変数に代入していることに注意してください。
#include <cstdlib>
#include <iostream>
#include <string>
using std::cerr;
using std::cout;
using std::endl;
using std::getenv;
using std::string;
const char *ENV_VAR = "HOME";
int main() {
const char *tmp = getenv("HOME");
string env_var(tmp ? tmp : "");
if (env_var.empty()) {
cerr << "[ERROR] No such variable found!" << endl;
exit(EXIT_FAILURE);
}
cout << "HOME : " << env_var << endl;
exit(EXIT_SUCCESS);
}
出力:
HOME : /home/username
また、getenv
関数から返された値を std::string
コンストラクタに直接渡さないことも重要です。これは、getenv
関数が環境変数の計算に失敗したときにセグメンテーション違反をスローする可能性があるためです。
この問題は、std::char_traits::length()
関数を呼び出す std::string
コンストラクタのコードの実装の中で発生しています。後者の関数は、引数に nullptr
を渡すと未定義の振る舞いをします。このように、環境変数が見つからなかった場合に getenv
が nullptr
を返すことができ、それを string
のコンストラクタに渡すと誤ったコードが生成されてしまうというシナリオがあります。
#include <cstdlib>
#include <iostream>
#include <string>
using std::cerr;
using std::cout;
using std::endl;
using std::getenv;
using std::string;
const char *ENV_VAR = "HOME";
int main() {
// Erroneous
string env_var(getenv("HOME"));
if (env_var.empty()) {
cerr << "[ERROR] No such variable found!" << endl;
exit(EXIT_FAILURE);
}
cout << "HOME : " << env_var << endl;
exit(EXIT_SUCCESS);
}
環境変数の有効な値を検証するためにカスタムチェックルーチンを使用する
環境変数にアクセスする際に最も危険な落とし穴の一つは、取得した値を検証することです。これらの変数は、攻撃者によってプログラムの範囲外で操作される可能性があることに注意してください。したがって、正しく安全なプログラムの実行を保証するために、これらの値の追加のサニタイズが必要です。
これらのチェックルーチンは、ほとんどがプログラムに依存しており、ケースごとに異なる処理を行う必要があります。以下の例では、文字列の値にスペースがあってはならないと仮定し、スペースがある場合には最初のスペース文字の前の部分文字列を抽出するシナリオを示しています。このようにして、システム環境からの不規則な入力値を回避することができます。なお、std::find
アルゴリズムは string
の中の文字を検索するために利用され、指定された char
が見つからなかった場合は string::npos
を返します。
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <string>
using std::cerr;
using std::cout;
using std::endl;
using std::find;
using std::getenv;
using std::string;
const char *ENV_VAR = "HOME";
int main() {
const char *tmp = getenv(ENV_VAR);
string env_var(tmp ? tmp : "");
if (env_var.empty()) {
cerr << "[ERROR] No such variable found!" << endl;
exit(EXIT_FAILURE);
}
// Env Variable Value Sanitization
int pos = env_var.find(' ');
if (pos != string::npos) env_var = env_var.substr(0, pos);
cout << "HOME : " << env_var << endl;
exit(EXIT_SUCCESS);
}