wp_remote_get() vs. wp_safe_remote_get()
2015
Über eine kleine Twitter-Diskussion, welche sich an einen Tweet von Pippin anschloß wurde ich nochmals darauf aufmerksam, dass die HTTP API von WordPress auch über „safe“ Funktionen verfügt. Ich hatte irgendwann die letzten Wochen schonmal davon gehört, war der Sache aber zunächst nicht weiter nachgegangen. Es geht um folgendes: Die Funktionen wp_remote_post()
, wp_remote_get()
und wp_remote_head()
verfügen über Geschwister, die allerdings im Codex nicht näher erläutert werden, sondern nur in der Referenz aufgeführt werden:
Nach den vergangenen Diskussionen zum Thema Sicherheit schrillen natürlich die Alarmglocken, wenn plötzlich „safe“-Funktionen auftauchen, von denen man nicht genau weiß, wozu sie dienen. Worin besteht also der Unterschied zwischen wp_remote_post()
und wp_safe_remote_post()
?
Wie die HTTP API von WordPress funktioniert
Klären wir zum Einstieg schnell nochmal, was die HTTP API eigentlich ist und wie sie funktioniert. In wp-includes/http.php finden sich verschiedene Funktionen, mit welchen man HTTP Requests absetzen kann. Alle diese Funktionen basieren letztlich auf der WP_Http()
Klasse, die sich in wp-includes/class-http.php findet.
Mit Hilfe von wp_remote_post()
kann man also relativ einfach einen POST Request absetzen und die erhaltenen Daten weiterverarbeiten. Ein Tutorial, wie man dabei vorgeht hat Tom McFarlin auf Tuts+ veröffentlicht.
Wie unterscheiden sich nun wp_remote_post() und wp_safe_remote_post()?
Liest man sich in den Quellcode ein, so findet sich eigentlich nur ein Unterschied: wp_safe_remote_post()
– und das gilt stellvertretend für alle safe_remote-Funktionen – übergibt im Argumenten-Array den Boolean 'reject_unsafe_urls'
mit true
:
Wie Ihr seht, werden die Argumente dann an die WP_Http()
Klasse übergeben. Zu was führt dort also 'reject_unsafe_urls'
? Wenn dieser Boolean true
ist, wird die URL mit der Funktion wp_http_validate_url()
validiert.
Wozu dient wp_http_validate_url()?
wp_http_validate_url()
hielt mit WordPress 3.5.2 Einzug in den Core. Blickt man in die Beschreibung des Quelltextes, so dient die Funktion wp_http_validate_url()
dazu, URLs für die sichere Verwendung in der HTTP API zu verwenden.
Validate a URL for safe use in the HTTP API.
Wie geht die Funktion vor? Zunächst wird mit Hilfe von wp_kses_bad_protocol()
geprüft, ob das Protokoll der URL gültig ist. Gültige Protokolle sind hierbei ausschließlich HTTP und HTTPS. Ohne den Parameter 'reject_unsafe_urls'
wäre auch SSL ein gültiges Protokoll (s. wp-includes/class-http.php L186). Sollte es sich um kein gültiges Protokoll handeln wird false
zurückgegeben.
Als unsicher gilt eine URL auch dann, wenn diese einen Benutzernamen oder ein Passwort enthält, um sich zu authentifizieren. Folgende URL würde also zurückgewiesen: http://benutzername:passwort@example.com
Enthält der Host eines der folgenden Zeichen, wird die URL zurückgewiesen: :#?[]
Auch externe URLS, welche nicht an die Ports 80, 443 oder 8080 – also die gewöhnlichen HTTP-Ports verweisen, werden zurückgewiesen.
Wenn die URL identisch mit der Home URL ist, der Port allerdings ein anderer als 80, 443 oder 8080 ist, wird die URL zurückgewiesen.
Und das bringt mehr Sicherheit?
Um die Entstehungsgeschichte der safe_remote-Funktionen zu verstehen ist es hilfreich, das Ticket 24646 zu studieren. In der WordPress Version 3.5.2 konzentrierten sich die Core Entwickler darauf, die HTTP API gegen Server Side Request Forgeries abzusichern. Nehmen wir einmal an, die HTTP API würde die Protokolle der URL und ähnliches keiner weiteren Prüfung unterziehen und einfach den angeforderten Request ausführen. Die angefragte URL wäre nun dict://locahost:11211/stat
. Dies würde nun den String „stat
“ an den lokalen Memcached schicken, welches für gewöhnlich den Port 11211 abhört. Da der Aufruf lokal erfolgt greift auch keine Firewall oder ähnliches, um die Abfrage zu verhindern. Um es knapp zu halten: Danger! In 3.5.2 versuchte man deshalb dererlei Angriffe abzuwehren, sprang dabei allerdings über das Ziel hinaus. So konnten überhaupt keine Zugriffe mehr auf die lokale Installation erfolgen. Wenn man beispielsweise über wp_remote_get()
das eigene Feed fetchen wollte, um es darzustellen, war dies nicht mehr möglich.
Deshalb wurden einige Schranken wieder abgebaut (der obige Angriff wird allerdings nach wie vor auch mit wp_remote_get()
unterbunden) und die sicheren remote-Funktionen eingeführt, welche den Standard hielten. Solange man sicher sein kann, dass die URL keinen Angriffsvektor auf das eigene System bilden kann, kann man damit weiterhin auf wp_remote_get()
, wp_remote_post()
und wp_remote_head()
zurückgreifen. Das macht vor allem dann Sinn, wenn man Requests auf den eigenen Host unternehmen möchte.
Kommt die URL hingegen aus einer unsicheren Quelle dienen wp_safe_remote_get()
,wp_safe_remote_post()
und wp_safe_remote_head()
dazu, den Sicherheitslevel noch einmal zu erhöhen. Von nun an kann auf den eigenen Host nur noch begrenzt zugegriffen werden, nämlich unter den oben genannten Bedingungen.
TLTR
Wenn Ihr nicht sicher seid, welche URL ihr letztlich über wp_remote_get() aufruft, ruft sie über wp_safe_remote_post() auf. Das ist schlicht sicherer.
Danke für die kleine Einweisung. Mir waren die safe_remote-Funktionen gar nicht bewusst. Könnte man dann im REST API Kontext als Faustregel sagen, wenn ich meine eigene API verwende sollte ich wp_remote_get verwenden und wenn ich eine fremde API verwende dann lieber wp_safe_remote_get?
Hi Hans,
ja, so könnte man das sagen 🙂
Wobei man natürlich bei der Einbindung von APIs meist relativ sicher sein kann, wie die URL zum Schluss lautet.
Richtig gefährlich sind soweit ich das halt verstehe, bspw. folgende Verwendungen, oder eben ähnliche, wo man nicht weiß, wie zum Schluss die URL aussieht:
$response = wp_remote_get( $_GET['url'] );
(Nicht dass ich das obige Beispiel bei Verwendung von wp_safe_remote_get() empfehlen würde 😛 )