Websupporter

Just another Websupporter site

wp_remote_get() vs. wp_safe_remote_get()

Due to a small Twitter discussion, which started after a tweet of Pippin I realized again, the WordPress HTTP API has “safe” functions. Some days or weeks ago I’ve heard of them already, but I didn’t check on them. The topic is the following: The functions wp_remote_post(), wp_remote_get() and wp_remote_head() do have siblings, which are not documented in the Codex but only in the reference:

After the WordPress community discusses largely the top security, I was quite alarmed. What are these “safe”-functions and what do they do? What is the difference between wp_remote_post() and wp_safe_remote_post()?

How the HTTP API works

Before we start, let’s be clear, what the HTTP API actually does. In wp-includes/http.php several functions are located, with which you can perform HTTP requests. All these functions basically interact with the WP_Http() class, located in wp-includes/class-http.php.

So it is relatively easy to perform a POST request with the function wp_remote_post() and to work with the received data afterwards. Tom McFarlin has published a tutorial how to work with this function on Tuts+.

What is the difference between wp_remote_post() and wp_safe_remote_post()?

If you read the source code, you will find only one difference: wp_safe_remote_post() – and the same applies to all safe_remote functions – extends the argument array with the boolean 'reject_unsafe_urls' set to true:

Embed from pastebin.com

Click the button below to load the content from pastebin.com.


Always allow pastebin.com

As you can see, these arguments are passed to the WP_Http() class. So what does 'reject_unsafe_urls' accomplish there? Once this boolean is true, the URL gets validated by the function wp_http_validate_url().

What does wp_http_validate_url()?

Since WordPress 3.5.2 wp_http_validate_url() can be found in the core. If you read the description in the source, it says.

[The function] validate[s] a URL for safe use in the HTTP API.

How does the function perform this task? As a first step, it checks (by using wp_kses_bad_protocol()), if the URL has a valid protocol. Valid protocols are only HTTP and HTTPS. If 'reject_unsafe_urls' is not true SSL is also perceived as a valid protocol (s. wp-includes/class-http.php L186). If the protocol is not valid the function will return false.

Also a URL is considered to be unsafe, if it contains a username or a password, by which a user tries to authenticate himself. The following URL would be rejected: http://benutzername:passwort@example.com

If the host of the URL contains one of the following symbols, the URL will be rejected: :#?[]

URLs which do resolve to another port than 80, 443 or 8080 – so to speak the usual HTTP posts – will be rejected.

And this is for safety reasons?

To understand the history of the safe_remote functions it helps to study the ticket 24646. In WordPress Version 3.5.2 the core developers focused on securing the HTTP API against Server Side Request Forgeries. Lets just assume, the HTTP API would not check the protocols before firing the request and the requested URL would be dict://localhost:11211/stat. This would send the string “stat” to the locale Memcached, which usually listens to the port 11211. Since the request was locally, no firewall would prevent the server to respond to this request. To be short here: Danger! In 3.5.2 the developers closed this issue, but they overshoot a little bit. The result was, WordPress was not able to access the locale installation with the HTTP API. If you wanted to fetch your own feed using wp_remote_get() the script blocked the resource.

So they had to step back a little bit. Well, the mentioned attack above is not possible, even using wp_remote_get(). But they introduced the safe remote functions. As long as you can be sure the URL can not be used for an attack on the own system, you still can rely on wp_remote_get(), wp_remote_post() and wp_remote_head(). This is useful, if you want to perform HTTP requests, which target your own host.

But if you can not be sure about the URL, you should use wp_safe_remote_get(),wp_safe_remote_post() and wp_safe_remote_head() to raise the level of security. From now on the request is limited to the conditions mentioned above.

TLTR

If you are not sure about the URL which will be passed through wp_remote_get() use wp_safe_remote_post(). It is simply the safe way to do it.

About the author

Seine erste Webseite hat David Remer 1998 in HTML verfasst. Wenig später war er fasziniert von DHTML und JavaScript. Nach jahrelanger Freelancerei arbeitete er zunächst für Inpsyde und ist heute Entwickler bei Automattic. Außerdem hat er das Buch "WordPress für Entwickler" verfasst.

Leave a Reply

Your email address will not be published.