機密値の保護
外部システムとの統合が増加しているため、AL コードで資格情報やその他の機密テキスト値などの秘密情報を扱うことは珍しいことではありません。 これらの情報がデバッグを通じて明らかにならないように、保護する必要があります。 多くの場合、機密テキスト値はリソース公開ポリシーによってブロックされていましたが、トラブルシューティングの容易さが犠牲になっていました。
資格情報やその他の機密テキスト値の漏洩を防ぐと同時にデバッグの有効化をサポートにするために、変数用の新しい SecretText 型が導入されました。 さらに、システム アプリの一般的なシナリオの一部では、資格情報用の SecretText パラメーターの受け渡しがサポートされます。 パラメーターの型の例として、HttpClient 型や分離ストレージ型などがあります。
SecretText データ型は、通常のデバッグやスナップショット デバッグを行っているときに AL デバッガーを介して機密値が漏洩されるのを防ぐように設計されています。 このデータ型は、API キー、カスタム ライセンス トークンなどのあらゆる種類の資格情報を処理する必要があるアプリケーションで使用することをお勧めします。
取得
資格情報が、プロシージャ スコープやその情報を含んでいる変数で NonDebuggable 属性によって保護されていない場合、AL コード内での情報の有効期間全体にわたって、デバッグ セッションやスナップショットで公開される危険性があります。 この有効期間は、3 つの異なるフェーズに分かれます。
資格情報は、次に示す複数の方法で取得できます。
API キーは、AL HttpClient 経由の呼び出しによって取得され、その後の呼び出しの認証として使用されます。
トークンは、OAUTH2 などの認証プロバイダーとの統合を実装するコントロール アドインによって取得されます。
カスタム開発者が定義したシナリオにより、認証トークンが作成されます。
開発者がテスト目的で誤ってコードに資格情報をハードコーディングし、削除することを忘れた場合。
Text 型または Code 型の任意の値を SecretText 値に割り当てることができます。 トークンが取得され、デバッグ不可能なプロシージャのスコープ内で SecretText 値に変換された場合、そのトークンは有効期間中デバッガーから保護されます。 さらに、AL コンパイラでは、ハード コードされた資格情報は SecretText 型の宛先には直接割り当てることができないことが保証されます。
[NonDebuggable]
procedure RetrieveSessionToken(Credential: SecretText; TargetUri: Text) SessionToken : SecretText
var
Request: HttpRequestMessage;
Response: HttpResponseMessage;
Client: HttpClient;
Headers: HttpHeaders;
Content: HttpContent;
begin
Request.SetRequestUri(TargetUri);
Request.GetHeaders(Headers);
// Compose an authorization header with a secret value
Headers.Add('Authorization', SecretStrSubstNo('Bearer %1', Credential));
Client.Send(Request, Response);
ParseSessionToken(Response, SessionToken);
exit;
end;
[NonDebuggable]
procedure ParseSessionToken(Response: HttpResponseMessage; var SessionToken: SecretText) : Text
begin
// Parse the response
end;
転送
資格情報は AL コードを介して転送され、使用されるポイントに到達します。 転送では次が行われます。
変数への割り当て
プロシージャ/トリガーを呼び出すパラメータとしての使用
関数呼び出しの戻り値になること
SecretText データ型では、デバッグ可能な型へ値が直接割り当てられるのを防ぐことにより、値がデバッグ不可能なままになることが保証されます。 この制約には、バリアント データ型が含まれます。 その結果、NonDebuggable 属性は取得時にのみ必要となり、資格情報の残りの有効期間では省略できます。これは、すべての中間宛先が自動的に保護されるためです。
procedure Assignments()
var
PlainText: Text;
Credential: SecretText;
begin
Credential := PlainText; // Allowed
PlainText := Credential; // Blocked
ConsumePlainText(PlainText); // Allowed
ConsumePlainText(Credential); // Blocked
Credential := ProduceCredential(); //Allowed
PlainText := ProduceCredential(); // Blocked
end;
procedure ConsumePlainText(PlainText: Text)
begin
end;
procedure ProduceCredential(): SecretText
begin
end;
利用
資格情報は、操作の実行で使われるときに利用されます。 一般的な例として、AL HttpClient を経由した外部 Web サービスとの通信があります。この場合、次の手順が必要になることがあります。
資格情報を使用して要求用の認証ヘッダーを作成します。
初回サインイン要求の本文に資格情報を追加します。
要求のパラメーターに API キーを追加します。
AL HttpClient および要求を行う際に必要なすべての中間型は、SecretText データ型を受け入れるメソッドをサポートしているため、値をデバッガーに公開せずに AL ランタイムに直接渡すことができます。
次のコード スニペットは、前述のすべてのシナリオがこれらのメソッドを通じてどのように実装できるかを示しています。
procedure SendAuthenticatedRequestToApi(UriTemplate: Text; BearerToken: SecretText; KeyParameter: SecretText; SecretBody: SecretText)
var
Client: HttpClient;
Headers: HttpHeaders;
SecretHeader: SecretText;
SecretUri: SecretText;
RequestMessage: HttpRequestMessage;
begin
SecretHeader := SecretStrSubstNo('Bearer %1', BearerToken);
RequestMessage.GetHeaders(Headers);
// The header is added and remains hidden when debugging the headers
// of the request message
Headers.Add('Authorization', SecretHeader);
// Headers.Contains('Authorization') - false
// Headers.ContainsSecret('Authorization') - true
// Headers.GetSecretValues must be used to get the values.
// It is not possible to retrieve the header value as a plain text.
SecretUri := SecretStrSubstNo(UriTemplate, KeyParameter);
RequestMessage.SetSecretRequestUri(SecretUri);
// RequestMessage.GetSecretRequestUri can be used to retrieve the request uri.
// It cannot be retrieved by GetRequestUri as a plain text.
RequestMessage.Content.WriteFrom(SecretBody);
// RequestMessage.Content.ReadAs can only read back the body in a SecretText destination
SendMessageAndHandleResponse(Client, RequestMessage);
end;
[NonDebuggable]
procedure SendMessageAndHandleResponse(Client: HttpClient; Request: HttpRequestMessage) CredentialFromResponse: SecretText
var
Response: HttpResponseMessage;
begin
Client.Send(Request, Response);
Response.Content.ReadAs(CredentialFromResponse);
end;
Unwrap メソッド
Unwrap メソッドを使用すると、互換性があるため、SecretText からテキストの宛先に値を抽出できます。 この操作は、OnPrem スコープのアプリケーションを構築する場合にのみ許可されており、このメソッドが NonDebuggable 属性を持つプロシージャ内で呼び出されない場合は警告が生成されます。 .NET の相互運用以外の目的でこのメソッドを使用することは推奨しません。
SecretStrSubstNo メソッド
SecretStrSubstNo メソッドを使用すると、SecretText 型のさまざまな値を組み合わせることができます。それらの値を公開する必要はありません。 このメソッドの動作は、テキスト値に対する StrSubstNo メソッドと同じですが、パラメータと戻り値が SecretText 型であるという重要な違いがあります。
いくつかの例を次のスニペットに示します。
procedure SecretStrSubstNoExamples()
var
First: SecretText;
Second: SecretText;
Result: SecretText;
begin
// Concatenation
Result := SecretStrSubstNo('%1%2', First, Second);
// Build a header value
Result := SecretStrSubstNo('Bearer %1', First);
// Build a comma separated value list
Result := SecretStrSubstNo('%1,%2', First, Second);
end;