コンテンツ セキュリティ ポリシー
コンテンツ セキュリティ ポリシー (CSP) は現在、モデル駆動型とキャンバスでサポートされています Power Apps。 管理者は、CSP ヘッダーを送信するかどうか、およびその内容をある程度制御できます。 設定は 環境 レベルであり、オンにすると 環境 内のすべてのアプリに適用されます。
CSP ヘッダー値の各コンポーネントは、ダウンロード可能なアセットを制御し、Mozilla Developer Network (MDN) で詳細に説明されています。 既定値は次のとおりです:
ディレクティブ | 既定値 | カスタマイズ可能 |
---|---|---|
スクリプトソース | * 'unsafe-inline' 'unsafe-eval' |
いいえ |
ワーカー-src | 'self' blob: |
いいえ |
スタイルソース | * 'unsafe-inline' |
いいえ |
フォントソース | * data: |
いいえ |
フレーム祖先 | 'self' https://*.powerapps.com |
はい |
この結果、既定の CSP は script-src * 'unsafe-inline' 'unsafe-eval'; worker-src 'self' blob:; style-src * 'unsafe-inline'; font-src * data:; frame-ancestors 'self' https://*.powerapps.com;
となります。 マイクロソフトのロードマップでは、現在カスタマイズできないヘッダーを変更できるようになりました。
前提条件
- Dynamics 365 Customer Engagementアプリおよびその他のモデル駆動型アプリの場合、CSPはオンライン環境およびDynamics 365 Customer Engagement (オンプレミスの) バージョン9.1以降を使用している組織でのみ使用できます。
CSP を構成しています
CSPは Power Platform 管理センターから切り替え、または設定することができます。 CSPを有効にすると、ポリシーに違反した場合にシナリオがブロックされる可能性があるため、まず開発/テスト環境で 環境 を有効にすることが重要です。 また、運用開始時での立ち上げを容易にするため、「レポート専用モード」にも対応しています。
CSP を構成するには、Power Platform 管理センター ->環境 ->設定 ->プライバシー + セキュリティに移動します。 次の図は、設定の既定の状態を示しています。
レポート中
「レポートを有効にする」トグルは、モデル駆動型アプリとキャンバスアプリが違反レポートを送信するかどうかを制御します。 これを有効にするには、エンドポイントを指定する必要があります。 違反レポートは、CSP が実施されているかどうかに関係なく、このエンドポイントに送信されます (CSP が実施されていない場合は、レポート専用モードを使用します)。 詳細については、レポート ドキュメント を参照してください。
実施
CSP の実行は、モデル駆動型アプリとキャンバス アプリで独立して制御され、ポリシーのきめ細かい制御を実現します。 モデル駆動型/キャンバス ピボットを使用して、目的のアプリ タイプを変更します。
"コンテンツ セキュリティ ポリシーを強制する" トグルは、特定のアプリの種類に対する既定のポリシーを適用します。 このトグルをオンにすると、この環境でのアプリの動作がポリシーに準拠するように変更されます。 したがって、推奨される有効化フローは次のようになります:
- 開発/テスト環境で適用します。
- 本番環境でレポート専用モードを有効にします。
- 違反が報告されなくなったら、本番環境で適用してください。
ディレクティブを構成する
このセクションでは、ポリシー内の個々のディレクティブを制御できます。 現在、frame-ancestors
のみをカスタマイズできます。
既定のディレクティブのトグルをオンのままにすると、この記事の前半で表示されたテーブルで指定された既定値が使用されます。 トグルをオフにすると、管理者はディレクティブのカスタム値を指定し、それらを既定値に追加できるようになります。 以下の例では、frame-ancestors
にカスタム値を設定しています。 この例では、ディレクティブは frame-ancestors: 'self' https://*.powerapps.com https://www.foo.com https://www.bar.com
に設定されます。つまり、アプリを同じ作成元、https://*.powerapps.com
、https://www.foo.com
と https://www.bar.com
でホストできても、他の作成元ではホストできないことを意味します。 追加ボタンを使用してエントリをリストに追加し、削除アイコンを使用してエントリを削除します。
一般的な構成
Dynamics 365 アプリ を使用した Microsoft Teams の統合では、frame-ancestors
に以下を追加します:
https://teams.microsoft.com/
https://teams.cloud.microsoft/
https://msteamstabintegration.dynamics.com/
Dynamics 365 App for Outlook については、frame-ancestors
に以下を追加します:
- Outlook Web App ホームページのオリジン
https://outlook.office.com
https://outlook.office365.com
Power BI レポートの埋め込み Power Apps の場合、以下を frame-ancestors
に追加します。
https://app.powerbi.com
https://ms-pbi.pbi.microsoft.com
重要な考慮事項
既定ののディレクティブをオフにし、空のリストで保存すると、ディレクティブが完全にオフになり、CSP 応答ヘッダの一部として送信されなくなります。
使用例
CSP の構成例をいくつか見てみましょう:
例 1
この例では、
- レポートはオフになっています。
- モデル駆動型の適用が有効になっています。
frame-ancestors
カスタマイズされており、https://www.foo.com
https://www.bar.com
- キャンバスの適用は無効になっています。
有効なヘッダーは次のとおりです:
- モデル駆動型アプリ:
Content-Security-Policy: script-src * 'unsafe-inline' 'unsafe-eval'; worker-src 'self' blob:; style-src * 'unsafe-inline'; font-src * data:; frame-ancestors https://www.foo.com https://www.bar.com;
- キャンバス アプリ: CSP ヘッダーは送信されません。
例 2
この例では、
- レポートはオンになっています。
- レポート エンドポイントは、
https://www.mysite.com/myreportingendpoint
にセットされています
- レポート エンドポイントは、
- モデル駆動型の適用が有効になっています。
frame-ancestors
デフォルトのまま
- キャンバスの適用は無効になっています。
frame-ancestors
カスタマイズされていますhttps://www.baz.com
有効な CSP 値は次のとおりです:
- モデル駆動型アプリ:
Content-Security-Policy: script-src * 'unsafe-inline' 'unsafe-eval'; worker-src 'self' blob:; style-src * 'unsafe-inline'; font-src * data:; frame-ancestors 'self' https://*.powerapps.com; report-uri https://www.mysite.com/myreportingendpoint;
- キャンバス アプリ:
Content-Security-Policy-Report-Only: script-src * 'unsafe-inline' 'unsafe-eval'; worker-src 'self' blob:; style-src * 'unsafe-inline'; font-src * data:; frame-ancestors https://www.baz.com; report-uri https://www.mysite.com/myreportingendpoint;
組織設定
CSP は、以下の組織の設定を直接変更することで、UI を使用せずに設定することができます:
IsContentSecurityPolicyEnabled は、モデル駆動型アプリでContent-Security-Policyヘッダーが送信されるかどうかを制御します。
ContentSecurityPolicyConfiguration は、frame-ancestors部分の値を制御します (上記のように、設定されていない場合は
'self'
に設定されますContentSecurityPolicyConfiguration
)。 この設定は、以下の構造を持つ JSON オブジェクトで表現されます –{ "Frame-Ancestor": { "sources": [ { "source": "foo" }, { "source": "bar" } ] } }
。 これはscript-src * 'unsafe-inline' 'unsafe-eval'; worker-src 'self' blob:; style-src * 'unsafe-inline'; font-src * data:; frame-ancestors 'foo' 'bar';
に変換されます- (MDN から) HTTP コンテンツ セキュリティー ポリシー (CSP) の frame-ancestors ディレクティブは、
<frame>
、<iframe>
、<object>
、<embed>
、<applet>
を使用してページを埋め込むことができる有効な親を指定します。
- (MDN から) HTTP コンテンツ セキュリティー ポリシー (CSP) の frame-ancestors ディレクティブは、
IsContentSecurityPolicyEnabledForCanvas は、キャンバス アプリでContent-Security-Policyヘッダーが送信されるかどうかを制御します。
ContentSecurityPolicyConfigurationForCanvas は、
ContentSecurityPolicyConfiguration
上記で説明したのと同じプロセスを使用して、キャンバスのポリシーを制御します。ContentSecurityPolicyReportUri は、レポートを使用するかどうかを制御します。 この設定は、モデル駆動型アプリとキャンバスアプリの両方で使用されます。 有効な文字列は、指定されたエンドポイントに違反レポートを送信し、
IsContentSecurityPolicyEnabled
/IsContentSecurityPolicyEnabledForCanvas
がオフになっている場合はレポート専用モードを使用します。 空の文字列はレポートを無効にします。 詳細については、レポート ドキュメント を参照してください。
UI を使用しない CSP の構成
特にオンプレミス構成など Power Platform 管理センターにない環境では、管理者はスクリプトを使用して CSP を構成し、直接設定を変更するケースも想定されます。
UI なしで CSP を有効にする
ステップ:
- 組織エンティティの更新権限を持つユーザー (システム管理者は良い選択です) としてモデル駆動型アプリを使用し、ブラウザの開発ツールを起動します。
- 以下のスクリプトをコンソールに貼り付けて実行します。
- CSPを有効にするには、デフォルトの設定を渡します -
enableFrameAncestors(["'self'"])
- 他のオリジンがアプリを埋め込むことを可能にする例として -
enableFrameAncestors(["*.powerapps.com", "'self'", "abcxyz"])
async function enableFrameAncestors(sources) {
const baseUrl = Xrm.Utility.getGlobalContext().getClientUrl();
if (!Array.isArray(sources) || sources.some(s => typeof s !== 'string')) {
throw new Error('sources must be a string array');
}
const orgResponse = await fetch(`${baseUrl}/api/data/v9.1/organizations`);
if (!orgResponse.ok) throw new Error('Failed to retrieve org info');
const orgs = await orgResponse.json();
const { organizationid, contentsecuritypolicyconfiguration, iscontentsecuritypolicyenabled } = orgs.value[0];
console.log(`Organization Id: ${organizationid}`);
console.log(`CSP Enabled?: ${iscontentsecuritypolicyenabled}`);
console.log(`CSP Config: ${contentsecuritypolicyconfiguration}`);
const orgProperty = prop => `${baseUrl}/api/data/v9.1/organizations(${organizationid})/${prop}`;
console.log('Updating CSP configuration...')
const config = {
'Frame-Ancestor': {
sources: sources.map(source => ({ source })),
},
};
const cspConfigResponse = await fetch(orgProperty('contentsecuritypolicyconfiguration'), {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
value: JSON.stringify(config),
}),
});
if (!cspConfigResponse.ok) {
throw new Error('Failed to update csp configuration');
}
console.log('Successfully updated CSP configuration!')
if (iscontentsecuritypolicyenabled) {
console.log('CSP is already enabled! Skipping update.')
return;
}
console.log('Enabling CSP...')
const cspEnableResponse = await fetch(orgProperty('iscontentsecuritypolicyenabled'), {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
value: true,
}),
});
if (!cspEnableResponse.ok) {
throw new Error('Failed to enable csp');
}
console.log('Successfully enabled CSP!')
}
UI なしで CSP を無効にする
ステップ:
- 組織エンティティの更新権限を持つユーザー (システム管理者は良い選択です) としてモデル駆動型アプリを使用し、ブラウザの開発ツールを起動します。
- 次のスクリプトをコンソールに貼り付けて実行します。
- CSP を無効化する場合はコンソールに貼り付けます:
disableCSP()
async function disableCSP() {
const baseUrl = Xrm.Utility.getGlobalContext().getClientUrl();
const orgResponse = await fetch(`${baseUrl}/api/data/v9.1/organizations`);
if (!orgResponse.ok) throw new Error('Failed to retrieve org info');
const orgs = await orgResponse.json();
const { organizationid, iscontentsecuritypolicyenabled } = orgs.value[0];
console.log(`Organization Id: ${organizationid}`);
console.log(`CSP Enabled?: ${iscontentsecuritypolicyenabled}`);
const orgProperty = prop => `${baseUrl}/api/data/v9.1/organizations(${organizationid})/${prop}`;
if (!iscontentsecuritypolicyenabled) {
console.log('CSP is already disabled! Skipping update.')
return;
}
console.log('Disabling CSP...')
const cspEnableResponse = await fetch(orgProperty('iscontentsecuritypolicyenabled'), {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
value: false,
}),
});
if (!cspEnableResponse.ok) {
throw new Error('Failed to disable csp');
}
console.log('Successfully disabled CSP!')
}