Managed Identity を使ったセキュアなインフラ構築

Tsubasa Yoshino Tsubasa Yoshino
Managed Identity を使ったセキュアなインフラ構築

はじめに

近年、大規模なセキュリティインシデントが各所で散見されており、目下セキュリティの向上は全てのサービス提供者にとって最優先事項です。 セキュリティを向上させるには、攻撃者に対してサービスの攻撃可能面を出来るだけ晒さないことが重要かつ簡潔なセキュリティ向上施策と言えるでしょう。 Azure には、Managed Identity(以下 Managed Id) という仕組みがあり、これにより簡潔に認証や資格情報管理が行えるようになりました。 この記事では、Managed Id 登場以前と以後でどのように変化が起きたのかを説明した後、簡単な Web API を通して Managed Id の利用方法を解説します。

用語の説明

この記事で登場する用語について簡単にまとめます。

  • Managed Identity (Managed Id) : Azure 内でシークレットレス認証を実現するための仕組み。発表当時は、Managed Service Identity (MSI) と呼ばれていましたが、名称が改められ、Managed Identity になりました。
  • 最小特権の原則 : 情報セキュリティの原則の一つ。オブジェクトに対して、必要な権限だけを与えることでセキュリティホールを小さくして、攻撃可能範囲を小さくする原則です。
  • Shared Access Signature(SAS) : ストレージアカウント内のリソースへのアクセスに使うトークンです。IP 制限や有効期限など、接続文字列よりも細かくアクセス制御が行えます。

Managed Id 登場以前

Managed Id 登場以前は、アプリケーションが他の Azure リソースへアクセスする為に、通常ではリソースアクセス用の資格情報が必要でした。 これは、サービスプリンシパル、各リソースの接続文字列、Azure ストレージの SAS 等が該当します。 これらの資格情報は、開発者や管理者が手動で安全に保存し、必要に応じて定期的なローテーションを行う等の作業が必要です。 また、取り扱いの不備により資格情報の漏洩によるセキュリティインシデントのリスクもあります。 セキュリティインシデント以外にも、資格情報の失効によってサービスダウンを招く原因にもなり、これらの管理は運用負荷が上がる原因になります。

ストレージアカウントを例に挙げると、ストレージアカウントは接続文字列と SAS によるアクセスが提供されます。 接続文字列にはフル権限が与えられているので、これが漏洩すると、攻撃者はストレージアカウントに対してデータの作成、読み取り、削除の全てが行えるようになります。 SAS の場合、リソースに対して細かくアクセス制御が出来るため、最小特権の原則に則って権限を付与すれば、資格情報が漏洩した場合でも限定的な被害に抑えることが出来ます。 ですが、SAS できめ細かく制御をする場合、大量の SAS を発行する必要があります。また、トークンのローテーションも必要になってくるため、それらを全て管理するにはかなりの運用コストが必要になります。

資格情報が漏洩した際の検知は非常に難しく、漏洩しないように管理を注意する、定期的に接続文字列をローテーションする対応が大半になるかと思います。 人間が注意を払って運用するには限度があるため、やはり仕組みでセキュリティホールを潰すのが適切です。

Managed Id が無い場合、資格情報をセキュアに管理する為 Key Vaults のようなシークレットマネージャを用意するといった追加のインフラの準備や、アクセスしたいリソースの分だけ資格情報を管理する必要があり、リソースの数が増えていくにつれて徐々に管理コストが高まっていく問題もありました。

また、資格情報が漏洩した場合、資格情報をローテーションする必要がありますが、接続文字列の場合、その接続文字列を利用しているアプリケーション全てで設定を変えて回る必要があり、その点でも非常に多くの作業コストが掛かります。

Managed Id 登場以後

Managed Id を利用すると、アプリケーションから他の Azure リソースへアクセスするための資格情報は、Azure によって完全に管理されます。 資格情報は、どのような権限を持っているアカウントでも閲覧出来ない為、誤ってパブリックリポジトリに公開されるといったことによる漏洩リスクをゼロにすることが出来ます。 Managed Id は、RBAC によってその権限が管理される為、最小特権の原則に則って権限を付与すれば、仮に認証が突破されても、攻撃可能範囲を最小限に抑えることが出来ます。

また、Managed Id を使う場合、Managed Id に対して、アクセスしたいリソースへの権限を付与していく形になるので、アクセスしたいリソースが増えていくにつれて管理する資格情報も増えていくといった問題や、Key Vaults 等の追加のインフラを用意する手間等が最小限に抑えられます。

例えば、複数のストレージアカウント、SQL Database、Redis Cache、Cosmos DB を使用するアプリケーションがある場合、Managed Id を使えば、指定の Managed Id に対してそれぞれのアクセス権限を設定するだけですが、Managed Id を使わない場合は、それぞれの資格情報を管理する必要が出てくるため、段々と管理が煩雑になってきます。

ここでまたストレージアカウントを例に挙げます。 接続文字列は、漏洩するとフル権限が奪われましたが、Managed Id の場合はどうでしょう。 接続文字列を外部に公開するリスクはゼロになりました。 仮に Managed Id が設定された仮想マシンが乗っ取られた場合でも、仮想マシンが利用する Managed Id に特権が付与されていなければ、攻撃者は特定範囲へのアクセスしか行えません。

SAS を使った場合はどうでしょう。 接続文字列と比較して細かく権限を制御できるため、リスクは最小限に抑えられます。 ですが、管理する SAS が増えていくにつれて SAS のローテーションコストやアプリケーション側の改修コストなどが増えていきます。 Managed Id の場合、資格情報のローテーション等は全て Azure に管理され、アプリケーションで資格情報を利用する際も SDK が吸収してくれる為、そういったコストの高まりを最小限に抑えることが出来ます。

これは、オンプレミスや外部のパブリッククラウドとの連携に際しても機能します。 例えば、AWS から Managed Id を経由して Azure 内のリソースにアクセスする場合は、Cognito と Managed Id を連携させることにより、シークレットレスな認証を実現できます。 オンプレミスのリソースとの連携の場合でも、Azure Arc を利用したハイブリッド環境を構築することで、シークレットレス認証を実現できます。

実装例

ここまで Managed Id のメリットを説明してきましたが、ここで実装例を見てみましょう。

簡単な実装例としてミニマムな Web API を作ってみます。

Managed Id を利用しない例

このコードでは、BlobContainerClient のインスタンスを生成する際に接続文字列を利用する実装になっています。 接続文字列は、基本的には環境変数から取得するかと思いますが、ローカルでのテスト目的でハードコードしたままコミットして意図せず漏洩する事故は大いに考えられます。

using Azure.Identity;
using Azure.Storage.Blobs;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.MapGet("/blob", async () =>
{
    var connectionString = "{Your connection string}";
    var contianerName = "{Your container name}";
    var containerClient = new BlobContainerClient(connectionString, contianerName);
    var blob = containerClient.GetBlobClient("sample.json");
    using (var sr = new StreamReader(await blob.OpenReadAsync()))
    {
        var content = await sr.ReadToEndAsync();
        return content;
    }
});

app.Run();

Managed Id を利用した例

このコードでは、センシティブな情報は一切排除されたコードになっています。

このコードでは、BlobContainerClient のインスタンスを生成する際に接続文字列の代わりに DefaultAzureCredential のインスタンスを渡す実装になっています。 また今回は、DefaultAzureCredential の中で、Managed Id、Visual Studio Credential、Azure CLI Credential 以外を利用しないように設定することで、認証を高速に行えるように調整しています。 実際に本番環境などで動かす場合は、Managed Id 以外の認証を利用しない設定すれば、より認証を高速に行うことが出来ます。

このコードには、資格情報が入り込む余地が無いため、万が一にも接続文字列が漏洩することは起こりえません。

using Azure.Identity;
using Azure.Storage.Blobs;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.MapGet("/blob", async () =>
{
    var accountName = "{Your account name}";
    var contianerName = "{Your container name}";
    var containerEndpoint = $"https://{accountName}.blob.core.windows.net/{contianerName}";
    var containerClient = new BlobContainerClient(new Uri(containerEndpoint), new DefaultAzureCredential(new DefaultAzureCredentialOptions
    {
        ExcludeAzureDeveloperCliCredential = true,
        ExcludeAzurePowerShellCredential = true,
        ExcludeEnvironmentCredential = true,
        ExcludeInteractiveBrowserCredential = true,
        ExcludeSharedTokenCacheCredential = true,
        ExcludeVisualStudioCodeCredential = true,
        ExcludeWorkloadIdentityCredential = true,
    }));
    var blob = containerClient.GetBlobClient("sample.json");
    using (var sr = new StreamReader(await blob.OpenReadAsync()))
    {
        var content = await sr.ReadToEndAsync();
        return content;
    }
});

app.Run();

まとめ

Managed Id を利用することによって、リソースアクセスに対する資格情報の漏洩は、完全に防げるようになりました。 少なくとも今後構築するアプリケーションは、可能な限り Managed Id を利用してセキュリティを高められると良いでしょう。 ですが、全てのサービスで Managed Id が使えるわけでは無いため、その点は注意が必要です。

セキュリティインシデントは、注意したら防げるといったものでは無いため、可能な限り仕組みで防ぐ方向に舵切りするべきでしょう。

お問い合わせはこちらから

問い合わせる
TOP