203 Routen: Was die WordPress-REST-API ohne Anmeldung herausgibt

Inhalt
Eine WordPress-Seite bietet zweierlei: Seiten, die ein Browser anzeigt, und eine Schnittstelle, die reine Daten liefert. Diese Schnittstelle ist seit der ersten Minute eingeschaltet, wird aber von den Verantwortlichen der Seite selten angesehen.
Zu finden ist sie unter /wp-json/, und ihre erste Antwort ist eine Liste all dessen, was sie kann.

Wozu die Schnittstelle da ist
Die REST-API gibt es, damit etwas anderes als ein Browser mit einer Seite arbeiten kann: der Block-Editor im Adminbereich, eine App auf dem Telefon, eine andere Seite, die Artikel übernimmt, ein Skript, das nach Plan veröffentlicht.
Die Schnittstelle gehört zu WordPress selbst und ist kein Plugin, und sie antwortet seit 2016 in der Voreinstellung. Eingeschaltet werden muss dafür nichts.
Die Folge, die es zu merken lohnt: Was auf der Seite veröffentlicht ist, ist auch hier veröffentlicht, in einer Form für Maschinen. Keine Kopie und keine Zusammenfassung – derselbe Inhalt, weit leichter einzusammeln.
203 Routen auf einer gewöhnlichen Seite
Das Verzeichnis unter /wp-json/ antwortete an der hier gemessenen Seite mit 203 Routen in neun Namensräumen.
110 davon gehören zu wp/v2, dem Kern: Beiträge, Seiten, Medien, Kommentare, Rubriken, Schlagworte, Blocktypen und die übrige Standardausstattung.
59 gehören einem einzigen Plugin. Das ist mehr als ein Viertel der ganzen Schnittstelle aus einer einzigen Erweiterung, und ungewöhnlich ist es nicht – ein großes Plugin meldet gern für jede eigene Ansicht eine eigene Route an.
Die übrigen 34 verteilen sich auf sieben Namensräume, die meisten davon mit drei bis acht Routen.
Die Liste der Namensräume ist eine Pluginliste
Das Verzeichnis zählt nicht nur Routen. Es benennt die Namensräume, und einen Namensraum sucht sich aus, wer das Plugin geschrieben hat.
In der Antwort auf eine einzige Anfrage ohne Anmeldung steht damit im Klartext, welche Plugins installiert sind: das SEO-Plugin, das Mehrsprachen-Plugin, das Codeblock-Plugin und jeder eigene Code, den die Seite für sich selbst anmeldet.
Ein Leck im Sinne eines Defekts ist das nicht. So muss die Schnittstelle arbeiten, denn ein Programm muss wissen, was es gibt. Es bedeutet aber, dass die Frage „welche Plugins laufen auf dieser Seite” mit einer Anfrage beantwortet ist – und diese Antwort wird regelmäßig benutzt, um nachzuschlagen, welche Versionen bekannte Schwächen haben.

Was ohne Anmeldung antwortet
Das Lesen steht weitgehend offen, was daraus folgt, dass die Inhalte ohnehin öffentlich sind.
Beiträge, Seiten, Kommentare, Rubriken, Schlagworte, Beitragstypen, Medien und die Routen des SEO- und des Mehrsprachen-Plugins antworteten alle mit 200 und echtem Inhalt.
Eine Einzelheit verdient einen Moment. Die Anfrage nach einem einzelnen Beitrag lieferte 23.455 Byte: den vollständigen, fertig gesetzten Artikel, keinen Auszug. Alles Veröffentlichte liegt mit einer Anfrage je Beitrag bereit, sauber in Felder getrennt, ohne das Beiwerk, das eine Seite mitschleppt.
Was eine Anmeldung verlangt, und die bessere Absage
Alles, was die Installation beschreibt statt der Inhalte, verlangte eine Anmeldung: die Einstellungen, die Pluginliste, die Themeliste, die Blocktypen, die Systemdiagnose. Alle antworteten mit 401.
Eine Route antwortete anders, und genau die taugt zum Nachmachen: wp/v2/users lieferte 404.
Der Unterschied ist nicht kosmetisch. Ein 401 oder 403 bestätigt, dass die Route da ist und bewacht wird – und sagt damit, dass dahinter etwas Echtes liegt, für das ein zweiter Versuch lohnt. Ein 404 sagt gar nichts. Die Route war entfernt statt geschützt, und von außen ist das nicht zu unterscheiden von einem WordPress, das sie nie angeboten hat.
Autorennamen sind der übliche Grund für diese Absage. Ein Autorenname ist die Hälfte einer Anmeldung, und eine öffentliche Liste davon ist eine öffentliche Liste gültiger Benutzernamen.
Was sich zu prüfen lohnt
Drei Fragen beantworten sich mit je einer Anfrage.
Was listet /wp-json/ auf? Welche Namensräume tauchen auf, und gehört einer davon zu etwas, das gar nicht mehr benutzt wird? Ein abgeschaltetes Plugin meldet nichts an; ein aktives, aber vergessenes meldet alles an, was es hat.
Was antwortet /wp-json/wp/v2/users? Ein 200 gibt Autorennamen heraus. Ein 401 bestätigt, dass es sie gibt. Ein 404 sagt nichts.
Und was kommt für einen einzelnen Beitrag zurück? Ist die Antwort der ganze Artikel, arbeitet die Schnittstelle genau wie vorgesehen – es lohnt aber zu wissen, dass der Inhalt in dieser Form vorliegt und nicht nur als Seite.
Dieselben zwei Fragen, wie viele Routen das Verzeichnis unter /wp-json/ nennt und was wp/v2/users antwortet, gingen auch an zwölf Installationen; die Ergebnisse von 203 bis 1107 Routen stehen in 1107 Routen ohne Anmeldung: Was zehn WordPress-Installationen unter /wp-json/ herausgeben.
Fragen und Antworten
Genügt es, wp/v2/users zu entfernen, damit keine Benutzernamen mehr nach außen gelangen?
Nein, die Route ist nur die bequemste Liste. In der Voreinstellung verraten zwei weitere Stellen dasselbe:
- Das Autorenarchiv. Bei Autoren mit veröffentlichten Beiträgen leitet eine Adresse wie /?author=1 auf /author/<kürzel>/ weiter, und dieses Kürzel leitet WordPress beim Anlegen eines Benutzers aus dem Anmeldenamen ab. Wer die Nummern der Reihe nach abfragt, erhält im Wesentlichen dieselbe Liste.
- Die oEmbed-Schnittstelle unter /wp-json/oembed/1.0/. Sie liefert zu jedem Beitrag den Anzeigenamen des Autors und die Adresse seines Archivs, samt Kürzel.
Der Anzeigename in Feeds und unter Beiträgen ist unkritisch, solange er nicht dem Anmeldenamen entspricht. Die dauerhafte Lösung liegt deshalb weniger im Verstecken als in einem Anmeldenamen, der nirgends öffentlich auftaucht, und in einer Anmeldung, für die der Name allein nichts nützt, etwa durch ein starkes Passwort und einen zweiten Faktor.
Verrät eine Seite ihre Plugins auch ohne das Verzeichnis unter /wp-json/?
Meist ja. Stylesheets und Skripte eines Plugins liegen unter /wp-content/plugins/ in einem Ordner, der in der Regel nach dem Plugin benannt ist, und tragen oft die Versionsnummer im Parameter ver. Wer das Verzeichnis versteckt, entfernt also eine Quelle, nicht die Information.