Graph und Kennworte
Mit Microsoft Graph können Sie auch Kennwort für Anwender setzen und die aktiven Anmeldeoptionen auslesen. Allerdings kommt dazu ein anderer URL-Pfad und Anmeldeweg zum Einsatz als ich auf Graph und Benutzer beschrieben habe.
Auf der eigenen Seite Graph Token finden sie Details zum Thema AppRegistrierung, Berechtigungen und Token-Generierung. Ich habe hier dennoch den Ablauf komplett beschrieben.
Berechtigungen
Dass Zugriff per Graph mit einem Token erfolgen und Sie für eigene Skripte ein App definieren müssen, sollte ihnen schon klar sein. Was anderes ist es nur, wenn die App von einem Dienstleister kommt und er diese in seinem Tenant definiert und freigegeben hat.
In meinem Beispiel gehe ich auf https://portal.azure.com und führe folgende Schritte durch:
- Anlegen einer generischen App
- Anlegen eines App Secret
Am einfachsten ist ein Kennwort, welches Azure für Sie bereitstellt. - Berechtigungen zuweisen
Die App muss für Microsoft Graph die Berechtigungen bekommen
Die Daten wie AppID, AppSecret etc. muss ich mir natürlich für mein Skript merken.
Eine eigenständige "App" kann diese Funktion aktuell nicht ausführen. Es muss immer ein "Benutzer" mit entsprechenden Anmeldedaten und Berechtigungen sein.
Es geht hier nur um das Setzen eines neuen Kennworts. Die bestehenden Anmeldemethoden können Sie auch als App lesen
- passwordAuthenticationMethod: resetPassword
https://docs.microsoft.com/en-us/graph/api/passwordauthenticationmethod-resetpassword?view=graph-rest-beta&tabs=http - Get passwordAuthenticationMethod
https://docs.microsoft.com/en-us/graph/api/passwordauthenticationmethod-get?view=graph-rest-beta&tabs=http
Delegate Ja, App nein
Als ich diese Seite im Juni 2021 aktualisiert habe, konnten Sie irrtümlich annehmen, das auch eine Application Permission möglich sei. Bei der Zuweisung von Berechtigungen im Azure Portal können Sie bei Microsoft Graph auch bei den "Application permissions" das Recht "UserAuthenticationMethod.ReadWrite.All" auswählen.
Anscheinend blendet Microsoft keine Berechtigungen aus, die in dem Fall nicht wirksam sind. Ein Blick in die Dokumentation zeigt aber deutlich, dass diese Funktion nicht über eine "App" alleine zu erreichen ist.
Der Versuch dennoch mit einer Application Permission das Kennwort eines Benutzers zurück zu setzen scheiter mit folgenden Fehler.
{"error":{"code":"badRequest", "message":"{\"error\":{\"code\":\"BadRequest\", \"message\":\"Upn from claims with value null is not a valid upn.\", \"innerError\":{\"request-id\":\"xxxx"}}}",...
- Upn from claims with value null is not a
valid upn.
https://docs.microsoft.com/en-us/answers/questions/246207/34upn-from-claims-with-value-null-is-not-a-valid-u.html
Token anfordern
Wir können nicht den einfachen Flow einer Application mit AppID/AppSecret oder AppID/AppZertifikat nutzen, sondern müssen einen anderen Prozess aussuchen. Microsoft bietet dazu drei Optionen an:
- Authorization Code
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
Der Weg wird gerne genutzt, wenn z.B. ein Teams Telefon oder ein Skript sich anmelden muss aber der Anwender kein Kennwort eingeben kann oder soll. Er bekomme eine URL mit einem Token, über die er dann dem Telefon oder Skript ein Token zukommen lassen kann - Resource Owner Password Credentials (ROPC)
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc - Implicit Grant
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow
Auf der Seite steht aber auch "With the plans for third party cookies to be removed from browsers, the implicit grant flow is no longer a suitable authentication method."
Ich habe mich für den Weg über ROPC entschieden, da ich hier auch Zugangsdaten übermitteln kann.
$LoginUrl="https://login.microsoftonline.com" # Graph API URLs $ResourceUrl="https://graph.microsoft.com" # Ressource API URLs $AppID="12345678-1234-1234-1234-1234567890ab" $Appkey="xxxxxx" $TenantName="msxfaq.onmicrosoft.com" # Tenant name $username="pwdresetter@carius.onmicrosoft.com" # keine Sorge. den Benutzer gibt es nicht $password="SuperGeheimesKennwort" $authresponse=invoke-restmethod ` -method POST ` -ContentType "application/x-www-form-urlencoded" ` -body "client_id=$($AppID) &scope=https%3A%2F%2Fgraph.microsoft.com%2F.default &client_secret=$($Appkey) &username=$($username) &password=$($password) &grant_type=password" ` -uri "$($LoginUrl)/$($tenantName)/oauth2/v2.0/token"
Offen: Wie gehe ich mit Benutzern und Conditional Access um? Denkbar sind AppKennworte oder das "Dienstkonto" darf generell nur die App nutzen und damit jegliche interaktive Anmeldung unterbunden. Angreifer müssten dann schon die App (Skript/EXE) und die Zugangdaten haben
Password Methoden lesen
Zuerst prüfe ich durch ein "READ", ob der Zugriff mit dem Token schon funktioniert.
$passwordmethodes = Invoke-RestMethod ` -Method GET ` -Headers @{"Authorization" = "Bearer $($accesstoken)"} ` -ContentType "application/json" ` -URI "https://graph.microsoft.com/beta/users/clouduser1%40msxfaq.net/authentication/passwordMethods"
Die Antwort ist im Graph Explorer gut zu sehen:
Dieser Benutzer hat wohl noch nie ein Kennwort bekommen. Ganz glauben kann ich das natürlich nicht, denn auch andere Benutzer. Übrigens können Sie per Graph auch ganz einfach alle Authentifizierungsmethoden eines Anwenders ermitteln:
$passwordmethodes = Invoke-RestMethod ` -Method GET ` -Headers @{"Authorization" = "Bearer $($accesstoken)"} ` -ContentType "application/json" ` -URI "https://graph.microsoft.com/beta/users/clouduser1%40msxfaq.net/authentication/Methods"
So sieht das bei meinem Benutzer aus, der neben dem klassischen Kennwort auch die Microsoft Authenticator App auf einem IPhone11 nutzen kann
Kennwort setzen
Die Beschreibung zum Setzen eines Kennwort " passwordAuthenticationMethod: resetPassword" (https://docs.microsoft.com/en-us/graph/api/passwordauthenticationmethod-resetpassword?view=graph-rest-beta&tabs=http) beschreibt eine URL, in der zweimal eine {id} vorkommt.
POST https://graph.microsoft.com/beta/users/{id | userPrincipalName}/authentication/passwordMethods/{id}/resetPassword Content-type: application/json { "newPassword": "newPassword-value", }
Die erste ID ist dabei die BenutzerID aber die zweite ID ist die Authenticationmethod zu dem Benutzer.
Die GUID der ID ist bei jedem Anwender unterschiedlich!
Wenn ich es daher richtig verstanden habe, muss ich erst mit einem "https://graph.microsoft.com/beta/users/clouduser1%40msxfaq.net/authentication/passwordMethods" mir die ID holen, um dann das Kennwort zu setzen. Das sieht dann so aus:
$passwordmethods = Invoke-RestMethod ` -Method GET ` -Headers @{"Authorization" = "Bearer $($accesstoken)"} ` -ContentType "application/json" ` -URI "https://graph.microsoft.com/beta/users/clouduser1%40msxfaq.net/authentication/passwordMethods" Invoke-RestMethod ` -Method POST ` -Headers @{"Authorization" = "Bearer $($accesstoken)"} ` -ContentType "application/json" ` -URI "https://graph.microsoft.com/beta/users/clouduser1%40msxfaq.net/authentication/passwordMethods/$($passwordmethods.value.id)/resetPassword" ` -body "{""newPassword"": ""Super4geheim!""}"
Eigentlich sollte der Aufruf eine URL zurückliefern, über den der Kennwort Update Flow ermittelbar ist. Es gibt aber sehr wohl Fehlermeldungen
Situation | Meldung |
---|---|
Benutzer nicht gefunden |
"error":{"code":"resourceNotFound", "message":"The specified user could not be found.", "innerError":{"xxxx |
Kennwort zu schwach |
Achtung: Ich habe auch mal "Password" als neues Kennwort versucht. Es wurde nicht gesetzt aber der Invoke-Webrequest hat auch keine Fehlermeldung generiert. |
ADSync Konto mit Password Hash Sync (PHS) | Keine Meldung. Laut Anleitung wird das Kennwort gesetzt und wenn "Password Writeback" aktiviert ist, zum lokalen AD repliziert |
Falsche ID |
{"error":{"code":"methodNotAllowed", "message":"The method is not supported for this URL.", "innerError":{"xxxx |
Kontrolle
Leider habe ich bei keiner der erfolgreichen Änderungen eine deutliche Erfolgsmeldung bekommen. Ein einfacher 200OK reicht mir aber nicht, wenn die oben aufgeführten Fehler auch einen 200 OK liefern. Auch eine erneute Abfrage der passwordMethods liefert keine weiteren Informationen.
$passwordmethods = Invoke-RestMethod ` -Method GET ` -Headers @{"Authorization" = "Bearer $($accesstoken)"} ` -ContentType "application/json" ` -URI "https://graph.microsoft.com/beta/users/clouduser1%40msxfaq.net/authentication/passwordMethods" $passwordmethodes.value | fl id : 28c10230-6103-485e-b985-444c60001490 password : creationDateTime : createdDateTime :
Ich habe hier kein CreationDateTime-Feld bekommen.
Anzeige beim Benutzer
Wenn das Kennwort durch den Administrator geändert wurde, muss er es natürlich dem Benutzer mitteilen. Bei der ersten Anmeldung wird der Benutzer gezwungen, ein neues Kennwort anzugeben.
Sicherheit
Zuerst hatte ich gehofft, dass ich mit einer App alleine die Funktion "Kennwort Reset" bereitstellen könnte. Aber das ist nicht möglich. Es muss immer ein ServiceAccount mit entsprechend privilegierten Berechtigungen genutzt werden. Allerdings können sie mit Conditional Access auch beide Konten miteinander verknüpfen:
Das Dienstkonto darf keine App außer ihre selbst geschriebene App nutzen. Ein Angreifer müsste dann sowohl die Zugangsdaten des Dienstkontos wissen als auch die App-Credentials aus der App haben, um damit etwas anstellen zu können.
Damit ist quasi ein vier Augenprinzip möglich. Der Entwickler der App kann damit alleine sich keine Hintertüren schaffen oder Konten übernehmen. Die Administratoren, die die Zugangsdaten es Dienstkontos wissen, müssen auch über die App verfügen. Wenn Sie noch sicherer sein wollen, dann könnte die Logik als "Azure Function" in der Cloud implementiert werden und die Zugangsdaten im "Azure Key Vault" hinterlegt werden.
Kennwort über Azure Az PowerShell
Weder über das Microsoft Admin Portal ( https://admin.microsoft.com) noch das Azure Portal (https://portal.azure.com) kann ich das Kennwort eines Kontos auf einen Wert setzen. Ich kann es dort nur "zurücksetzen", d.h. Microsoft vergibt ein Startkennwort und fordert den Benutzer beim ersten Anmelden auf, es zu ändern.
Über die AzureAD PowerShell oder Graph kann ich auch ein Kennwort setzen:
# Installiere Module Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force Get-AzADUser ` -UserPrincipalName clouduser@msxfaq ` | Update-AzADUser ` -Password $NewPW.Password ` -ForceChangePasswordNextLogin:$false UserPrincipalName : test1@gsa.lspb.de ObjectType : User UsageLocation : DE GivenName : Test Surname : User AccountEnabled : True MailNickname : test1 Mail : test1@gsa.lspb.de DisplayName : Test User Id : 21b54c02-3293-488d-a727-b347db4d7ab1 Type : Member
- Get-AzADUser
https://docs.microsoft.com/en-us/powershell/module/az.resources/get-azaduser
Update-AzADUser
https://docs.microsoft.com/en-us/powershell/module/az.resources/update-azaduser
Kennwort über AzureAD PowerShell
Hinweis: Von von diesem Modul genutzte API ist zum Sommer 2022 abgekündigt. Ich weiß nicht, ob das Modul angepasst wird oder sie besser die "Azure Az PowerShell" nutzen
Wenn ich ein Kennwort per Graph setze, dann bedeutet dies aber auch, dass der Anwender bei der nächsten Anmeldung zur Änderung aufgefordert wird, Wenn ich ein Kennwort einfach nur setzen will, dann geht das aktuell (Stand Juni 2021) nur per Azure AD PowerShell und "Set-ADUserPassword".
# Optional Modul erst installieren Install-Module AzureAD # Login to Azure AD PowerShell With Admin Account Connect-AzureAD # ObjectID zum Benutzer erhalten $user = Get-AzureADUser -Searchstring Testuser Set-AzureADUserPassword ` -ObjectId $user.ObjectId ` -ForceChangePasswordNextLogin:$false
- Set-AzureADUserPassword
https://docs.microsoft.com/en-us/powershell/module/azuread/set-azureaduserpassword - Azure Active Directory PowerShell for
Graph module
https://docs.microsoft.com/en-us/powershell/module/azuread/?view=azureadps-2.0 - Reset Azure AD User password with a
predefined password
https://www.2azure.nl/2019/06/20/reset-azure-ad-user-password-with-a-predefined-password/ - Resetting Azure AD User Passwords
https://scripting.up-in-the.cloud/security/resetting-azure-ad-user-passwords.html
Interessant ist natürlich der Blick hinter die Kulissen, was hier passiert. Das "Set-AzureADUserPassword"-Commandlet startet genau einen einzelnen HTTP-Request.
Das PowerShell-Modul nutzt eine REST-API, die aber nicht unter "https://graph.microsoft.com" sondern "https://graph.windows.com" wartet. Auch der UserAgent ist interessant
- Swagger Codegen
https://swagger.io/tools/swagger-codegen/
Allerdings ist diese API bereits "Deprecated" und wird Mitte 2022 dann auch entfallen:
Quelle:
https://docs.microsoft.com/en-us/graph/migrate-azure-ad-graph-planning-checklist
Insofern sollten Sie vielleicht nicht allzu viel Zeit investieren, um diese API direkt anzusprechen sondern direkt bei Microsoft Graph nach einer entsprechenden Schnittstelle schauen.
- App migration planning checklist
https://docs.microsoft.com/en-us/graph/migrate-azure-ad-graph-planning-checklist - Request differences between Azure AD
Graph and Microsoft Graph
https://docs.microsoft.com/en-us/graph/migrate-azure-ad-graph-request-differences
Anmelden mit App-Zertifikat
Speziell für die Verwendung mit automatisierten Prozessen ist eine App-Registration und eine Anmeldung per Zertifikat interessant. Auf der Seite MGGraph PowerShell habe ich das mit MGGraph schon beschrieben. Aber auch mit MSAL.PS lässt sich sehr einfach ein Token bekommen.
$ClientID = "<guid der App>" $TenantID = "<gui des Tenant>" $CertificatePath = "cert:\currentuser\my\<thumbprint des Zertifikats>" $Certificate = Get-Item $certificatePath $Token = Get-MsalToken -ClientId $ClientId -TenantId $TenantId -ClientCertificate $Certificate
In der Variable "$Token" steht die Antwort und im Property "$token.Accesstoken" befindet sich wieder das bekannte OAUTH-Token, welches sie auch einfach in https://jwt.io oder https://jwt.ms zur Analyse eingeben können.
- MGGraph PowerShell
-
Use app-only authentication with the
Microsoft Graph PowerShell SDK
https://docs.microsoft.com/en-us/powershell/microsoftgraph/app-only?view=graph-powershell-1.0&tabs=azure-portal - MSAL.PS - Get-MsalToken.ps1
https://www.powershellgallery.com/packages/MSAL.PS/4.2.1.1/Content/Get-MsalToken.ps1 - Microsoft Graph using MSAL with
PowerShell and Delegated Permissions
https://blog.darrenjrobinson.com/microsoft-graph-using-msal-with-powershell-and-delegated-permissions/ - Using Certificate Authentication with
Graph API
https://seanmcavinue.net/2021/07/19/using-certificate-authentication-with-graph-api/
Zwischenstand
Schade, dass eine einfache App keine Kennworte setzen darf. Aber auf der anderen Seite kann ich es auch wieder verstehen, dass so ein sensibler Prozess an erweiterten Berechtigungen hängt. Auf der anderen Seite fände ich eine "verschlossene signierte" App für ein automatisches Provisioning aber immer noch besser als ein Skript, in dem sich ein Benutzerkonto anmeldet um dann mit der App das Kennwort zu ändern.
Auch sonst bin ich etwas irritiert, dass beim Setzen eines schwachen Kennworts einfach ein 200OK kommt aber kein Fehler und die Rückgabe bei der Auflistung von Anmeldedaten kein CreatedDate liefert. Für die Auswertung, wie alt ein Kennwort ist oder wann die letzte Anmeldung durch den Anwender erfolgt ist, müssen weitere APIs herhalten, die ich aber noch nicht verwendet habe.
Weitere Links
- Graph API
- Graph Berechtigungen
- Graph und Benutzer
- MGGraph PowerShell
- passwordAuthenticationMethod: resetPassword
https://docs.microsoft.com/en-us/graph/api/passwordauthenticationmethod-resetpassword?view=graph-rest-beta&tabs=http - passwordAuthenticationMethod resource
type
https://docs.microsoft.com/en-us/graph/api/resources/passwordauthenticationmethod?view=graph-rest-beta - Erste Schritte mit der Microsoft
Graph-API für Authentifizierungsmethoden
https://docs.microsoft.com/de-de/graph/authenticationmethods-get-started - Do we have any microsoft graph api to
change the password of the user giving his
existing password?
https://docs.microsoft.com/en-us/answers/questions/9942/index.html - Unable to reset the password for a disabled account?!
https://www.michev.info/Blog/Post/3298/unable-to-reset-the-password-for-a-disabled-account - Upn from claims with value null is not a
valid upn.
https://docs.microsoft.com/en-us/answers/questions/246207/34upn-from-claims-with-value-null-is-not-a-valid-u.html - Get started with the Microsoft Graph
PowerShell SDK
https://docs.microsoft.com/en-us/graph/powershell/get-started - Get-MgUser
https://docs.microsoft.com/en-us/powershell/module/microsoft.graph.users/get-mguser?view=graph-powershell-beta - Hacking your way around Modern
authentication and the PowerShell modules
for Office 365
https://www.michev.info/Blog/Post/1771/hacking-your-way-around-modern-authentication-and-the-powershell-modules-for-office-365 - PowerShell Basics: How to Create an
Azure AD App Registration
https://techcommunity.microsoft.com/t5/itops-talk-blog/powershell-basics-how-to-create-an-azure-ad-app-registration/ba-p/811570 - https://janbakker.tech/prepopulate-phone-methods-using-a-custom-connector-in-power-automate/
- https://identity-man.eu/2020/07/08/pre-configure-authentication-methods-for-end-users-in-azure-ad/
- https://docs.microsoft.com/de-de/graph/api/authentication-post-phonemethods?view=graph-rest-beta&tabs=http
-
Microsoft Graph REST API - Outlook Calendar
with Powershell
https://blog.icewolf.ch/archive/2020/04/29/microsoft-graph-rest-api-outlook-calendar-with-powershell.aspx -
The Secret to Update User Password Profile
with Microsoft Graph API In Azure AD
Programmatically
https://levelup.gitconnected.com/how-to-reset-or-update-user-passwords-with-microsoft-graph-api-in-azure-ad-c6733c3b0ac3