PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

The Three Ugly Sisters

Note: This article was originally published at Planet PHP on 20 December 2012.
Planet PHP

The Three Ugly Sisters are three classes of attacks which I've tried to highlight in 2012. You might also know them as Cross-Site Scripting, XML Injection, and Insufficient Server-Side Transport Layer Security (or Peerjacking). These three attacks are particularly ugly for PHP programmers, because each, in its own way, has a common advantage for attackersa-aPHP does not defend against them automatically, and they are poorly documented and poorly understood by programmers. This potent mix of default vulnerabilities, programmer ignorance, and poor reference material culminate in the sort of security vulnerabilities that an attacker can find in the wild without trying very hard.

Let's take a brief look at each of them.

Insufficient Server-Side Transport Layer Security (Peerjacking)

The easiest way to fathom the mysteries of Transport Layer Security is to remember that your web app sometimes behaves like a modern browser. It can make GET and POST requests like a browser, over HTTPS. It can act as an intermediary, shuffling a user's personal data to and fro between your server and the server of any third party (including within your own network). It can consume masses of information, store it, and display it back to users, over HTTPS.

Browsers and SSL/TLS have a simple relationship. Browsers implement it correctly, strictly, and monitor their implementation and the CAs they trust very closely. Any failure on their part would be incredibly embarrassing and harmful to their share of the browser market.

Unfortunately, it is far from unusual to find PHP libraries and applications where SSL/TLS protections have been accidentally or even deliberately disabled. Since SSL/TLS is designed to prevent data interception, request manipulation, request replays, and other attacks that are designed to do harm to users, disabling SSL/TLS or being unaware of how to configure it correctly is not an acceptable behavior in PHP. Yet, it remains ludicrously common. As programmers, we also need to be familiar with the prevailing data privacy legislation, privacy ethics, and corporate guidelines which may apply to user data in the jurisdiction or corporate setting we operate within.

Here are two examples of inappropriate HTTPS usage in PHP followed by their correctly configured variants. There is one each for PHP Streams and the cURL extension. The common factor between them is actually very simplea-athey disable two essential checks in SSL/TLS which we can call peer verification and domain matching. Peer verification guarantees the validity of the SSL certificate offered by the contacted server. Domain matching ensures that the offered SSL certificate is for the host or domain name we connected to. If we fail to verify the peer's SSL certificate, then we'd never notice if was a self-signed fake, or if it was signed by an untrusted Certificate Authority. It might also have expired. If we fail to perform domain matching, then the attacker could use any valid SSL certificate for any domain or host (whether one they purchased or stole the private key for), so long as the certificate used is capable of passing Peer Verification. So, you need both Peer Verification and Domain Matching enabled in order to be completely secure.

PHP Streams (The Wrong Way)

$url = 'https://api.twitter.com/1/statuses/public_timeline.json'; $result = file_get_contents($url);

PHP Streams (The Right Way)

$url = 'https://api.twitter.com/1/statuses/public_timeline.json'; $contextOptions = array('ssl' = array('verify_peer' = TRUE, 'cafile' = __DIR__ . '/cacert.pem', 'verify_depth' = 5, 'CN_match' = 'api.twitter.com')); $sslContext = stream_context_create($contextOptions); $result = file_get_contents($url, NULL, $sslContext);

cURL (The Wrong Way)

$url = 'https://api.twitter.com/1/statuses/public_timeline.json'; $req = curl_init($url); curl_setopt($req, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($req, CURLOPT_SSL_VERIFYPEER, FALSE); // Disable Peer Verification curl_setopt($req, CURLOPT_SSL_VERIFYHOST, 0); // Disable Host Matching /** OR **/ curl_setopt($req, CURLOPT_SSL_VERIFYHOST, TRUE); // TRUE = 1 when it should be set to 2! $result = curl_exec($req);

cURL (The Right Way)

$url = 'https://api.twitter.com/1/statuses/public_timeline.json'; $req = curl_init($url); curl_setopt($req, CURLOPT_RETURNTRANSFER, TRUE); $result = curl_exec($req); $error = curl_errno($req); if ($error == CURLE_SSL_PEER_CERTIFICATE || $error == CURLE_SSL_CACERT || $error == 77) { curl_setopt($req, CURLOPT_CAINFO, __DIR__ . '/cert-bundle.crt'); $result = curl_ex

Truncated by Planet PHP, read more at the original (another 8355 bytes)