Zugriff mit PHP auf Exchange/Office 365

Lange angekündigtnun Realität: Microsoft hat die Authentifizierung per Passwort für bestimmte Protokolle in Exchange Online ab September 2022 zu Gunsten von oAuth2 abgeschaltet. Die GitHub Issues vieler Libraries und Backup-Scripte verdeutlichen, dass die Umstellung viele Administratoren kalt erwischt hat. Nachfolgend zeigen wir einen Weg, wie man mit Hilfe von oAuth2 per PHP via IMAP weiterhin auf die Inhalte seines Exchange-Postfachs zugreifen kann.


Aus Security-Gesichtspunkten ist der Schritt von Microsoft sehr richtig, aber die Komplexität des programmatischen Zugriffs auf seine E-Mails hat um einiges zugenommen. Nutzt man beispielsweise die weit verbreitete Library barbushin/php-imap, gelang der Zugriff früher wie folgt:

734a82898010e2fcb02c72c3cd9702c2

Das klappt nun so einfach nicht mehr. Um eine Verbindung via oAuth2 herzustellen, besteht die Schwierigkeit zunächst darin, an das Access Token zu kommen. Und hierfür muss man zunächst man zwei Hürden auf sich nehmen.

Azure Active Directory

Die folgenden Schritte registrieren eine neue App im Azure Active Directory:

Login auf https://portal.azure.com
Öffnen des "Azure Active Directory"
"App-Registrierungen" & "Neue Registrierung" auswählen
"Anwendungs-ID (Client)" (=Client-ID) & "Verzeichnis-ID (Mandant)" (=Tenant-ID) kopieren
"API-Berechtigungen" & "Berechtigung hinzufügen"
"Von meiner Organisation verwendete APIs" & "Office 365 Exchange Online"
"Anwendungsberechtigungen" & "IMAP.AccessAsApp"
Administratorzustimmung erteilen
"Zertifikate & Geheimnisse" & "Geheime Clientschlüssel" & "Neuer geheimer Clientschlüssel"
Beschreibung wählen und Gültigkeit festlegen
"Geheime ID" (Client Secret) in Zwischenablage kopieren
Unternehmensanwendungen öffnen
"Objekt-ID" kopieren

PowerShell

Nun schalten wir die App in der Microsoft PowerShell (Administrator-Modus) frei und weisen den einzelnen Postfächern Berechtigungen zu (<TENANTID>, <CLIENTID>, <OBJECTID>, <EMAIL> müssen jeweils ersetzt werden):

Install-Module -Name ExchangeOnlineManagement
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -Organization <TENANTID>

New-ServicePrincipal -AppId <CLIENTID> -ServiceId <OBJECTID>
Add-MailboxPermission -Identity "<EMAIL>" -User <OBJECTID> -AccessRights FullAccess
...

Nachdem man das geschafft, ist der Rest kein Hexenwerk mehr. Da barbushin/php-imap oAuth2 nicht unterstützt, kann man beispielsweise mit der alternativen Library Webklex/php-imap (die obendrein den Vorteil hat, das PHP-Modul IMAP nicht zu benötigen) eine Verbindung herstellen:

734a82898010e2fcb02c72c3cd9702c2

Doch auch Libraries, die offiziell kein oAuth2 unterstützen, kann man mit Hilfe eines Proxies wie simonrob/email-oauth2-proxy lauffähig machen. Nach dem Download und der Installation via python -m pip install -r requirements-no-gui.txt (Python ≥3.6 vorausgesetzt) editiert man die Datei emailproxy.config beispielsweise wie folgt (und ersetzt dieses Mal <TENANTID>, <CLIENTID>, <CLIENTSECRET> und <EMAIL>):

[Server setup]

[IMAP-1993]
local_address = localhost
server_address = outlook.office365.com
server_port = 993

[Account setup]

[<EMAIL>]
token_url = https://login.microsoftonline.com/<TENANTID>/oauth2/v2.0/token
oauth2_scope = https://outlook.office365.com/.default
redirect_uri = http://localhost:8080
client_id = <CLIENTID>
client_secret = <CLIENTSECRET>

Anschließend startet man den Proxy mit python emailproxy.py --no-gui und kann unverschlüsselt nun zur IP localhost auf Port 1993 via regulärem Basic Auth (und beliebig gesetztem Passwort) verbinden. Will man den Proxy beim Systemstart als Dienst im Hintergrund starten, nutzt man beispielsweise systemd:

sudo systemctl edit --force --full emailproxy.service

[Unit]
Description=Email OAuth 2.0 Proxy
[Service]
ExecStart=/usr/bin/python /path/to/emailproxy.py --no-gui
Restart=always
[Install]
WantedBy=multi-user.target

sudo systemctl enable emailproxy.service --now
sudo systemctl status emailproxy.service
sudo systemctl start emailproxy.service

Wird eine verschlüsselte Verbindung vorausgesetzt, ist auch das möglich – hierfür erstellt man zunächst einen privaten Schlüssel sowie ein selbst signiertes Zertifikat:

openssl genrsa -out key.pem 3072
openssl req -new -x509 -key key.pem -out cert.pem -days 360

Anschließend referenziert man in der emailproxy.config diese beiden Dateien:

local_key_path = /path/to/key.pem
local_certificate_path = /path/to/cert.pem
Zurück