💻

サービスプリンシパルのアプリケーション許可による Mail.Send や Mail.Read にアカウント制限を適用する

2022/10/06に公開

この記事について

2022/10/1 より、Exchange Online では基本認証が廃止され、原則、先進認証への対応が必須となります。

https://jpmessaging.github.io/blog/basic-authentication-deprecation-in-exchange-online-september/

先進認証に対応するにあたっては、多要素認証 (MFA) の導入などが必須となり、アプリケーションやバッチ処理などでメール送受信を行う際は、サービスプリンシパル + Microsoft Graph API の利用が検討されると思います。
その際、委任されたアクセス許可 ではなく、アプリケーションの許可 での Mail.SendMail.Read などを設定してしまうと、サービスプリンシパルでテナント配下の全ユーザーのメールボックスを参照したり、好きなユーザーを差出人にしたメール送信を許可することになってしまい、社内の一般ユーザーに付与するには非常にハードルの高い API 権限となっています。

しかし、当該 API の権限付与を許可しないと、従来の業務にも影響が出ることでしょう。そのため、本記事では メールが有効なセキュリティグループ および アプリケーションのアクセス許可ポリシー を利用して、サービスプリンシパルで Mail.Send や Mail.Read などを利用する際、アクセスできるユーザーアカウントに制限を持たせるようにします。

デモ

それでは、実際に API 利用にユーザー制限をかけてみます。

Exchange Online PowerShell v3 のインストール

Exchange Online PowerShell v3 を使用できるようにインストールしておきます。

https://learn.microsoft.com/ja-jp/powershell/exchange/exchange-online-powershell-v2?preserve-view=true&view=exchange-ps

デモ用サービスプリンシパルを作成

まずは、デモ用のサービスプリンシパルを用意します。
サービスプリンシパルの作成方法などは、公式ドキュメントなどを参考にしてください。
証明書またはクライアントシークレットは、後で発行するので、今は作成しなくて OK です。

https://learn.microsoft.com/ja-jp/azure/purview/create-service-principal-azure

このデモでは、サービスプリンシパルで メール送受信(サンプル) というアプリを作成し、Microsoft Graph のアプリケーションの許可で Mail.ReadMail.Send のアクセス許可を付与しています。

デモ用ユーザーを作成

今回のデモで使用するユーザーを作成します。
今回のデモでは、3 ユーザーを作成し、その内の 2 ユーザーがサービスプリンシパル経由でメール送受信を行えるものとします。

UPN サービスプリンシパル経由でのメール送受信
test01@jcdugdev.onmicrosoft.com OK
test02@jcdugdev.onmicrosoft.com OK
test03@jcdugdev.onmicrosoft.com NG

メールが有効なセキュリティグループの作成

Exchange Online PowerShell v3 を使用して、メールが有効なセキュリティグループを作成し、サービスプリンシパルでメールの送受信を行うことを許可するユーザーアカウントをグループメンバーとして追加します。

# 作成したサービスプリンシパルのアプリケーション ID
$registeredAppId = "1e93a5f1-7d79-41a5-99fc-d8a3abdb6335"
# メールが有効なセキュリティグループに追加するメンバーを String[] で指定
[string[]]$membersMailGroup = "test01@jcdugdev.onmicrosoft.com","test02@jcdugdev.onmicrosoft.com"
$dateTime = Get-Date -Format "yyyyMMddHHmmss"
# サービスプリンシパルを作成した Azure AD テナントで使用できるドメインを指定
$domain = "jcdugdev.onmicrosoft.com"
# メールが有効なセキュリティグループのプライマリメールアドレス
$mailAddress = $registeredAppId + "@" + $domain
# GUI で作成するとグループ名がこの形になる
$mailGroupName = $registeredAppId + $dateTime

# Exchange 管理者ロール以上の権限ロールを所有するユーザーアカウントでログイン
Connect-ExchangeOnline
# メールが有効なセキュリティグループを作成
$newDistributionGroupParameter = @{
    Name = $mailGroupName
    DisplayName = $registeredAppId # セキュリティグループの表示名
    # Description = "" # 説明を設定したい場合に使用
    # ManagedBy = "" # セキュリティグループの所有者を設定したい場合に使用
    MemberDepartRestriction = "Closed" # ユーザーの離脱を禁止
    MemberJoinRestriction = "ApprovalRequired" # グループへの参加に所有者の承認を要求
    Members = $membersMailGroup # セキュリティグループのメンバー
    PrimarySmtpAddress = $mailAddress # プライマリメールアドレスを指定
    RequireSenderAuthenticationEnabled = $true # 組織内のユーザーからのメッセージのみを許可
    Type = "Security" # メールが有効なセキュリティグループは Security 指定必須
}
# メールが有効なセキュリティグループを作成
New-DistributionGroup @newDistributionGroupParameter
# 必要に応じて、メールが有効なセキュリティグループの設定を変更
$setDistributionGroupParameter = @{
    Identity = $mailAddress	# 設定変更するセキュリティグループのメールアドレス
    HiddenGroupMembershipEnabled = $true # 共有アドレス帳内での表示を OFF
    MemberDepartRestriction = "Closed" # ユーザーの離脱を禁止
}
Set-DistributionGroup @setDistributionGroupParameter

メールが有効なセキュリティグループを作成したら、Exchange 管理センターにアクセスし、メールが有効なセキュリティグループが作成されたことを確認します。

アプリケーションのアクセス許可ポリシーの作成

作成したサービスプリンシパルメールが有効なセキュリティグループを紐づける形で、アプリケーションのアクセス許可ポリシーを作成します。

# アプリケーションのアクセス許可ポリシーを作成
New-ApplicationAccessPolicy -AppId $registeredAppId `
    -PolicyScopeGroupId $mailAddress `
    -Description "Restrict this app to members of $mailAddress ." `
    -AccessRight RestrictAccess
# 作成したアプリケーションのアクセス許可ポリシーを確認
Get-ApplicationAccessPolicy | select AppId,AccessRight,Identity

アプリケーションのアクセス許可ポリシーの動作確認

アプリケーションのアクセス許可ポリシーを作成したら、実際にポリシーが意図した通りに動作するか確認します。

# テストするユーザー一覧
[string[]]$testers = "test01@jcdugdev.onmicrosoft.com","test02@jcdugdev.onmicrosoft.com","test03@jcdugdev.onmicrosoft.com"
# アプリケーションのアクセス許可ポリシーをテスト
foreach ($upn in $testers) {
	Write-Output "Tester's UPN: ${upn}"
	Test-ApplicationAccessPolicy -Identity $upn -AppId $registeredAppId
}

コマンドを実行すると、以下のような形の結果が返ってきます。
作成したメールが有効なセキュリティグループに所属しているユーザーは許可、所属していないユーザーは拒否となることを確認できます。

Tester's UPN: test01@jcdugdev.onmicrosoft.com

AppId             : 1e93a5f1-7d79-41a5-99fc-d8a3abdb6335
Mailbox           : test01
MailboxId         : 112fbd4d-1abf-41fa-8e33-141ac300668c
MailboxSid        : S-1-5-21-4250393971-3952328016-2926241624-38505906
AccessCheckResult : 許可

Tester's UPN: test02@jcdugdev.onmicrosoft.com
AppId             : 1e93a5f1-7d79-41a5-99fc-d8a3abdb6335
Mailbox           : test02
MailboxId         : 3c0761e6-4417-4066-9360-2abb7ab405cc
MailboxSid        : S-1-5-21-4250393971-3952328016-2926241624-38505907
AccessCheckResult : 許可

Tester's UPN: test03@jcdugdev.onmicrosoft.com
AppId             : 1e93a5f1-7d79-41a5-99fc-d8a3abdb6335
Mailbox           : test03
MailboxId         : 3bc3119b-3a88-4f80-b8ea-343da51d4742
MailboxSid        : S-1-5-21-4250393971-3952328016-2926241624-38505908
AccessCheckResult : 拒否

サービスプリンシパルのシークレットを発行

メールが有効なセキュリティグループアプリケーションのアクセス許可ポリシーを設定できたら、シークレットを払い出して、実際にサービスプリンシパルを利用する準備を完了します。
今回はデモなので、クライアントシークレットを払い出します。

テスト

最後に、実際にサービスプリンシパルのテストを行います。
今回のテスト用スクリプトは、私が過去、Python で作成したものを流用します。

https://github.com/ymasaoka/Sample-MSAL-Python

こちらの sample/servicePrincipal/get_messages_top10.py がメール受信、sample/servicePrincipal/sendmail.py がメール送信のサンプルスクリプトです。

https://www.youtube.com/watch?v=X3E-tP9NxDc

https://www.youtube.com/watch?v=GlM6rtLthsg

作成したメールが有効なセキュリティグループに所属していないユーザーを指定して実行すると、

{'error': {'code': 'ErrorAccessDenied', 'message': 'Access to OData is disabled.'}}

というエラーが返され、Mail.ReadMail.Send を使用できないことを確認できます。

まとめ

Exchange Online を含む、Microsoft 365 まわりの基本認証廃止に伴い、現在動かしているバッチ処理やシステム、ツールなどで、サービスプリンシパルを利用する機会が増えると思います。
ですが、安易にアプリケーションの許可で Mail.Read や Mail.Send を付与してしまうと、証明書やシークレットキーの流出により、意図しない形でメールが送受信されてしまったり、Mail.ReadWrite を付与してしまっていた時には最悪、悪意あるメールボックスの操作などが行われてしまうこともあるかもしれません。
異なるシステムやツールごとに、サービスプリンシパルを分けて作成し、必ずメールが有効なセキュリティグループとアプリケーションのアクセス許可ポリシーを紐づけて、各サービスプリンシパルごとに必要最小限の Microsoft Graph API アクセス許可を付与するようにしましょう。そうすることで、何かあった際のリスクを最小限に抑えられると思います。

Discussion