Zabezpieczanie odpowiedzi z Actionable Messages
Table of Contents
W moim poprzednim poście przeprowadziłem Cię przez listę kroków wymaganych do zbudowania, wysłania i obsługi odpowiedzi z Adaptive Cards jako Actionable Messages w Outlooku. Powiem C teraz, jak zabezpieczyć odpowiedź.
Zasadniczo odpowiedź z Adaptive Card wysłanej jako Actionable Message może być łatwo „zhakowana”. W definicji karty definiujemy atrybut „Url”, który informuje formularz, gdzie wysłać dane. Jeśli wiadomość zostanie przekazana do innej osoby w tenancie lub jeśli ktoś stworzy własny formularz, który zasymuluje wysłanie danych do endpointa przygotowanego dla naszej wiadomości, bez dodatkowych zabezpieczeń łyknie wszystko. Dlatego nasze rozwiązanie powinno być przygotowane do zidentyfikowania źródła i zakończenia dalszego wykonywania w przypadku próby oszustwa.
Jeśli karta jest używana do celów korporacyjnych, możesz użyć mechanizmu zwanego „podpisaniem kart”, aby za pomocą klucza prywatnego podpisać payload, a następnie za pomocą klucza publicznego zweryfikować odpowiedź. Mechanizm używa standardów DKIM i SPF: https://docs.microsoft.com/en-us/outlook/actionable-messages/security-requirements#action-authorization-header. Ale co w przypadku, gdy potrzebujemy czegoś prostszego?
Zobacz działające rozwiązanie!
Nagłówek Action-Authorization na ratunek
Każda odpowiedź z Actionable Message zawiera klucz nagłówka Action-Authorization. Wygląda jak zwykły Bearer token, ale nim nie jest. To jest tzw. JSON Web Signature (JWS).
Ważne! Bearer token z Action-Authorization nie może być użyty do autoryzacji zapytania do np. GraphAPI, cele pobrania danych profilu użytkownika. Zawsze zwróci 401, ponieważ nie jest to prawidłowy bearer token usługi AAD.
Zawartość klucza to obiekty JSON zakodowane algorytmem base64. Po rozdzieleniu używając znaku „.” powstaną trzy części (zgodnie ze standardem RFC7515):
- JWS header
- JWS payload
- JWS signature
JWS header
Zdekoduj go używając następującego wyrażenia: decodeBase64(split(replace(triggerOutputs()['headers']?['Action-Authorization'], 'Bearer ', ''), '.')?[0])
{"typ":"JWT","alg":"RS256","kid":"RfKI6h2uhxp6rHwux79UcFh","x5t":"RfKI6h2uhxp6rHwux79UcFh","issloc":"AM0PR10MB","sqid":63734169909382}
Definiuje używany typ JWT (JSON Web Token). Ten dane nie są zbyt przydatne do celów walidacji.
JWS payload
Zdekoduj go używając następującego wyrażenia: decodeBase64(concat(split(replace(triggerOutputs()['headers']?['Action-Authorization'], 'Bearer ', ''), '.')?[1], '=='))
{ "iat":1598827285, "ver":"STI.ExternalAccessToken.V1", "appid":"48af08dc-f6d2-435f-b2a7-069abd99c086", "sub":"WHO SUBMITTED CARD", "appidacr":"2", "acr":"0", "tid":"32443e19-8cfe-4e6b-a3fe-e75c0e09c7a8", "sender":"WHO SENT CARD", "oid":"e4e6b40f-f698-403e-bc8e-252a2d52f22c", "iss":"https://substrate.office.com/sts/", "aud":"https://prod-135.westeurope.logic.azure.com", "exp":1598828185, "nbf":1598827285 }
Zasadniczo wysyłając do użytkownika Actionable Message i czekając na jego odpowiedź, należy gdzieś przechowywać podstawowe informacje, aby porównać je po otrzymaniu odpowiedzi, na przykład w CDS lub SharePoint. Następnie możesz użyć jednej z poniższych wartości do porównania:
- appid – jest to identyfikator aplikacji, która wystawiła token, w przypadku Outlooka zawsze powinien mieć wartość 48af08dc-f6d2-435f-b2a7-069abd99c086.
- sub – to jest e-mail użytkownika, który kliknął, aby przesłać odpowiedź.
- sender – to jest adres e-mail konta używanego do wysłania Actionable Message.
- aud – url instancji Logic Apps, która wykonała akcję wysyłającą wiadomość.
- iat – kiedy payload został podpisany (UNIX timestamp)
- exp – kiedy podpis wygasa (UNIX timestamp)
W tym przypadku możesz porównać, czy wartości appid i sub są zgodne z oczekiwaniami. Więcej w temacie: https://docs.microsoft.com/en-us/outlook/actionable-messages/security-requirements#verifying-that-requests-come-from-microsoft.
CorrelationId i originator w Adaptive Card
To, co można również wykorzystać, a co jest nawet trudniejsze do zhakowania, to użycie atrybutów CorrelationId + originator w Adaptive Cards. Możesz wygenerować swój niestandardowy identyfikator GUID (np. używając wyrażenia guid()
w Power Automate), zapisać go w CDS lub SharePoint, a następnie ustawić jako wartość atrybutu CorrelationId:
{ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.0", "hideOriginalBody": true, "originator": "f6f1d226-d6bb-409e-853e-0747fd61234", "correlationId": "@{guid()}",
Po otrzymaniu odpowiedzi wystarczy porównać wartości atrybutu originator z identyfikatorem guid zarejestrowanego dostawcy oraz wartość CorrelationId z wartością Card-Correlation-Id z nagłówka: triggerOutputs()['headers']?['Card-Correlation-Id']
(źródło: https://docs.microsoft.com/en-us/outlook/actionable-messages/adaptive-card#additional-properties-on-the-adaptivecard-type).
Tokeny Service-specific
Microsoft rekomenduje również dodanie niestandardowych tokenów generowanych do docelowego adresu URL zdefiniowanego w akcjach Adaptive Card, np.: https://contoso.com/approve?requestId=abc&serviceToken=a1b2c3d4e5
Więcej w temacie: https://docs.microsoft.com/en-us/outlook/actionable-messages/security-requirements#verifying-the-identity-of-the-user.
Mam nadzieję, że powyższe kroki pomogą Ci zbudować swoje rozwiązanie oparte o Actionable Messages i Adaptive Cards, a także odpowiednio je zabezpieczyć. Jeśli masz pytania, zadaj je w komentarzach poniżej.