Azure Web Appsで、DBへの接続文字列をコードから分離する:Tech TIPS
Webアプリでデータベース(DB)などへの接続文字列をコード中に直接記述することは、割とありがちだ。でも危険なのでコードからは分離した方がよい。Azure Web Appsでの改善方法は?
対象サービス:Microsoft Azure、Web Apps
TIPS「AzureのTraffic ManagerでカスタムドメインやSSL証明書を割り当てる際の手順と注意」に記したように、弊社のマップサービスのバックエンドサーバにはTraffic Managerを利用している。その実装・構築の際、Webアプリのコード側でデータベースの接続文字列の取り扱いを改善する必要があった(もともとの実装が決してよくなかったのだが)。本稿では、その方法について説明したい。
「接続文字列」をコード中に直接記述することの問題点
Webアプリからデータベース(DB)にアクセスする場合、サーバ名やデータベース名、ID/パスワードなどを記述した「接続文字列」をAPIで指定することがよくある。この接続文字列はコードに直接埋め込むのが簡単で手軽なのだが、それには無視できない重要な問題点がある。
- コードが第三者に開示されたり、あるいは漏えいしたりすると、秘匿すべきデータベースへの接続ID/パスワードまで同時に漏れてしまう
- 別のデータベースに接続するとき、いちいちコードを修正する必要がある
要するにセキュリティ面で脆弱(ぜいじゃく)であり、またコードの再利用もしにくいということだ。
Traffic Managerの導入であらわになった問題点
前述のTraffic Managerの場合は、「正しい」データベースが選択されない、という問題が生じた。
筆者は東日本リージョンに構築してあったWeb AppsとAzure SQL Databaseのセットを、そっくり西日本リージョンに複製し、その後もコードやデータがリアルタイムで東日本から西日本へレプリケーションされるように設定した。それをTraffic Managerで束ねることで可用性を高めようとした。
ここで、西日本のWeb Appsが(想定していた)西日本のSQL Databaseではなく、東日本の方に接続してしまうことに気付いた。原因は、西日本にレプリケーションされたWebアプリのコード中にDB接続文字列が直接記述されていて、それが東日本のSQL Databaseを指していたからだ。
このままでは東日本のSQL Databaseがダウンすると、すぐに両リージョンのWeb Appsも応答不能になる。つまりTraffic Manager配下の全サイトがダウンするため、その可用性向上の効果も大幅に薄れてしまう。
この問題はWeb Appsに備わっている接続文字列の設定機能を利用して、コードから接続文字列を分離することであっさりと解決できた。それほど難しくはなかったものの、つまずいたところもあったので、その設定方法を具体的に説明しよう。例示しているのはPHPとAzure SQL Databaseだが、中核となるWeb Appsの仕組みは共通なので、他の言語やデータベースでも参考にはなるだろう。
Azure Web Appsの「アプリケーション設定」で接続文字列を設定する
まずはAzureポータルで対象のWeb Appsに接続文字列を設定する。
Azure Web Appsに接続文字列を設定する
これはAzureポータルでWeb Appsの「アプリケーション設定」画面を開いたところ。
(1)対象のWeb Appsの設定画面を開く。
(2)メニューから[アプリケーション設定]を選ぶ。
(3)ブレードが開いたら下にスクロールして、「接続文字列」というセクションを見つける。
(4)設定済みの接続文字列。
(5)[接続文字列の値の表示]をクリックすると、(4)で隠されている接続文字列があらわになる。
(6)接続文字列に付ける名前を指定する。これは後述する環境変数名の一部になる。
(7)接続文字列の実体を指定する(内容については後述)。
(8)接続先のサーバまたはサービスを選ぶ。これも環境変数名の一部になる(後述)。
(9)Web Appsのデプロイスロット(ステージサイト)ごとに別々の接続文字列を指定するなら、[スロットの設定]にチェックを入れてオンにする。デフォルトのオフのままなら、スロット間で共通の設定として扱われる。
(10)選択した接続文字列を削除するには、これをクリックすると表示される[削除]をクリックする。
(11)設定が済んだら、[保存]をクリックする。数秒から数十秒で「Web アプリ設定が正常に更新されました」という通知が届くはずだ。その後、数秒で実際にWebアプリから、接続文字列が設定された環境変数を参照できるようになる。
設定した接続文字列は、PHPやPython、Java、node.jsでは環境変数で参照できる。一方、.NETアプリケーションの場合は構成ファイルのconnectionStringsセクションに設定される。
環境変数の場合、その名称は前述のアプリケーション設定で指定した<名前>((6))とサービス/サーバの選択肢((8))によって、以下のように決まる。
サービス/サーバ | メニューの選択肢 | 環境変数名 |
---|---|---|
Azure SQL Database | SQL Database | SQLAZURECONNSTR_<名前> |
SQL Server | SQL Server | SQLCONNSTR_<名前> |
MySQL | MySQL | MYSQLCONNSTR_<名前> |
PostgreSQL | PostgreSQL | POSTGRESQLCONNSTR_<名前> |
Azure 通知ハブ | 通知ハブ | NOTIFICATIONHUBCONNSTR_<名前> |
Azure Service Bus | Service Bus | SERVICEBUSCONNSTR_<名前> |
Azure Event Hubs | イベント ハブ | EVENTHUBCONNSTR_<名前> |
Azure Cosmos DB(DocumentDB API) | ドキュメント DB | DOCDBCONNSTR_<名前> |
Azure Redis Cache | Redis Cache | REDISCACHECONNSTR_<名前> |
上記以外のサーバ/サービス | カスタム | CUSTOMCONNSTR_<名前> |
接続文字列の環境変数名の決まり方 前述のアプリケーション設定のうち、(6)の<名前>と、(8)のサービス/サーバの選択によって、Webアプリから参照すべき環境変数名が決まる。 |
接続文字列の内容は、Webアプリのコードや接続先のサーバ/サービスによって異なる。例として、PHPとAzure SQL Databaseの組み合わせに対して筆者が設定している接続文字列を挙げておく。
"sqlsrv:server=<SQL Databaseサーバ名>.database.windows.net;database=<データベース名>;<各種パラメータ>","<ログイン名>","<パスワード>"
Webアプリのコード側で環境変数から接続文字列を取得する
上記の設定が済んだら、実際に設定された環境変数をWebアプリから確認してみよう。PHPであればphpinfo()の出力のうち、「Environment」セクションで環境変数の一覧を確認できる。
phpinfo()の出力から接続文字列の環境変数を確認する
(1)対象のWeb Appsでphpinfo()を実行し、そのHTMLフォーマットでの出力をWebブラウザに表示させたところ。
(2)ページ内を環境変数名「SQLAZURECONNSTR_PDO3」で検索してみる。
(3)「Environment」セクション内で見つかった環境変数。
(4)Azureポータルで設定した接続文字列が(3)に格納されていることが分かる。
後はコードを修正して、決め打ちだった接続文字列を環境変数から取得するように変更する。参考までにPHPでの例を以下に記す。
$envPrefix = "SQLAZURECONNSTR_"; ……Azure Web Apps + Azure SQL Databaseでの環境変数名の接頭語
$envName = $envPrefix + "PDO3"; ……対象の環境変数名
$connStr = getenv($envName); ……環境変数から接続文字列を取得
if (empty($connStr)) {
error_log("環境変数 $envName が見つかりませんでした。");
return false;
}
$conn = explode(",", $connStr); ……配列に分割して保存
if (!$conn || count($conn) !== 3) {
error_log("環境変数 $envName に接続文字列が正しく設定されていませんでした。");
return false;
}
$dsn = trim($conn[0], '"'); ……ダブルクオートを削る
$login = trim($conn[1], '"');
$password = trim($conn[2], '"')
try {
$pdo = new PDO($dsn, $login, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); ……エラー時に例外を発生させる
} catch (PDOException $ex) {
$errMsg = "PDOException: DSN = " . $dsn . ", Line No = " . $ex->getLine() . ", Message = " . $ex->getMessage();
error_log($errMsg);
return false;
}
return $pdo;
これはPHPのPDOを介してSQL Databaseに接続するコードの例。エラー処理は適宜強化していただきたい。
Azureポータルから接続文字列を漏らさないように注意
接続文字列がコードからAzure Web Appsの設定項目に移ったので、以後はWeb Appsの設定項目から外部に漏れないよう、注意を払う必要がある。AzureポータルではIAM(Identify & Access Management)によってポータルを参照できるユーザーを細かく制限できるので、必要に応じて設定することが望ましい。
Copyright© Digital Advantage Corp. All Rights Reserved.