Sensible Werte schützen
Da es immer mehr Integrationen in externe Systeme gibt, ist es im AL-Code nicht unüblich, mit Geheimnissen wie Anmeldeinformationen und anderen sensiblen Textwerten zu arbeiten. Da es notwendig ist, diese vor der Aufdeckung durch Debuggen zu schützen. Letzteres wurde häufig durch Richtlinien zur Ressourcengefährdung auf Kosten einer einfachen Problembehandlung blockiert.
Führen wir einen neuen Typ SecretText für Variablen ein, um das Debuggen zu ermöglichen und gleichzeitig Anmeldeinformationen und andere vertrauliche Textwerte vor der Offenlegung zu schützen. Zudem erhalten einige der gängigen Szenarios in der System-App Unterstützung für die Übergabe von SecretText-Parametern für Anmeldeinformationen. Beispielsweise die Typen „HttpClient“ und „Isolated Storage“
Datentyp „SecretText“ dient dazu, vertrauliche Werte vor der Offenlegung durch den AL-Debugger beim regulären oder Debuggen von Momentaufnahmen zu schützen. Die Verwendung wird für Anwendungen empfohlen, die jegliche Art von Anmeldeinformationen wie API-Schlüssel, angepasste Lizenztokens oder Ähnliches verarbeiten müssen.
Abruf
Wenn eine Anmeldeinformation nicht durch das Attribut NonDebuggable in einem Prozedurbereich oder in der Variablen, in der sie enthalten ist, geschützt ist, gibt es die Gefahr, dass sie in einer Debugging-Sitzung oder einer Momentaufnahme seine gesamte Lebensdauer im AL-Code offengelegt. Dieses Leben kann in drei verschiedene Phasen aufgeteilt werden.
Ein Leistungsnachweis kann auf verschiedene Arten abgerufen werden:
Ein API-Schlüssel wird durch einen Aufruf über den AL HttpClient abgerufen und dann als Authentifizierung für weitere Aufrufe genutzt.
Ein Token wird über ein Steuerelement-Add-In abgerufen, das eine Integration mit einem Authentifizierungsanbieter wie OAUTH2 implementiert.
Ein benutzerdefiniertes, vom Entwickler festgelegtes Szenario erstellt ein Authentifizierungstoken.
Ein Entwickler codiert versehentlich zu Testzwecken eine Anmeldeinformation fest in den Code und vergisst dabei, diese zu entfernen.
Jeder Wert vom Typ Text oder Code kann einem Wert SecretText zugewiesen werden. Wenn die Token abgerufen und dann im Rahmen eines nicht debuggbaren Verfahrens in einen SecretText-Wert konvertiert werden, sind sie während ihrer Lebensdauer vor dem Debugger geschützt. Zudem garantiert der AL-Compiler, dass ein fest codierter Berechtigungsnachweis nicht direkt einem Ziel vom Typ SecretText zugewiesen werden kann.
[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;
Transit
Ein Leistungsnachweis durchläuft den AL-Code, um die Punkte zu erreichen, an denen sie verwendet wird. Der Transit beinhaltet:
Variablen zuweisen
Als Parameter verwenden, um eine Prozedur/einen Trigger aufzurufen
Werden Sie zum Rückgabewert eines Funktionsaufrufs
Der SecretText-Datentyp garantiert, dass der Wert nicht debuggbar bleibt, indem er alle Zuweisungen von sich selbst zu einem debuggbaren Typ verhindert. Diese Einschränkung umfasst den Variant-Datentyp. Als Ergebnis ist das NonDebuggable-Attribut nur während des Abrufs erforderlich und kann für den Rest der Lebensdauer einer Anmeldeinformation weggelassen werden, da alle Zwischenziele automatisch geschützt sind.
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;
Verbrauch
Der Leistungsnachweis wird verbraucht, wenn sie zum Ausführen eines Vorgangs verwendet werden. Ein häufiges Beispiel ist die Kommunikation mit einem externen Webdienst über den AL HttpClient, bei der möglicherweise folgende Schritte erforderlich sind:
Erstellen eines Authentifizierungsheaders für die Anfrage mit den Anmeldeinformationen.
Hinzufügen der Anmeldeinformationen zum Hauptteil einer Anfrage zur ersten Anmeldung.
Hinzufügen eines API-Schlüssels zu den Parametern einer Anfrage.
Der AL HttpClient und alle Zwischentypen, die zum Erstellen einer Anforderung erforderlich sind, unterstützen eine Methode, die den SecretText-Datentyp akzeptiert, sodass die Werte direkt an die AL-Laufzeit übergeben werden können, ohne für den Debugger offengelegt zu werden.
Der folgende Codeausschnitt zeigt, wie alle vorher genannten Szenarien mit diesen Methoden implementiert werden können.
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;
Die Unwrap-Methode
Mit der Unwrap-Methode kann der Wert aus Kompatibilitätsgründen aus einem SecretText an ein Textziel extrahiert werden. Es ist nur beim Erstellen von Anwendungen für den Bereich OnPrem zulässig und seine Verwendung erzeugt eine Warnung, es sei denn, es wird in einem Verfahren mit dem NonDebuggable-Attribut aufgerufen. Es wird nicht empfohlen, diese Methode für andere Zwecke als die .NET-Interoperabilität zu nutzen.
Die SecretStrSubstNo-Methode
Die SecretStrSubstNo-Methode ermöglicht die Zusammenstellung verschiedener Werte vom Typ SecretText, ohne die entsprechenden Werte preiszugeben. Ihr Verhalten ist identisch mit dem der StrSubstNo-Methode für Textwerte, mit dem wichtigen Unterschied, dass ihre Parameter und ihr Rückgabewert dem Typ SecretText angehören.
Einige Beispiele werden im folgenden Ausschnitt demonstriert:
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;