Reversed Entropy Can entropy ever be reversed?

21Jun/102

Cookies, cURL and PHP without a jar

cURL is one of the best ways of implementing the client side of a RESTful service with PHP, is also really handy to take some data from other websites and do with it whatever you want.

Taking this data can be a problem when you need to autenticate on the third party website using cookies. Rest supports sending and retrieving them, but all the tutorials I've found use txt files to cache those cookies for being able to send them back to the server.

I don't like this approach, so let's see how to grab and store them on-the-fly using a simple regular expresion. First, let's setup a simple authentication page in PHP.

We're going to do at least two connections to the target server, the first one will send the password to the form and grab the cookie, the second one will call again the same server but this time we will send the cookie along our petitions, so we can navigate through private pages like we were a normal user.

I think that the first file (set.php) don't need commenting, is pretty basic and it only show debug text and sets the cookies/sessions. It would be something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function pre($data){
    echo "<pre>"; print_r($data); echo "&lt;\/pre&gt;";
}
 

session_start();
setcookie("TestCookie1", rand(0, time()));
setcookie("TestCookie2", rand(0, time()), time()+3600);  /* expire in 1 hour */
setcookie("TestCookie3", rand(0, time()), time()+3600, "/~rasmus/", ".example.com", 1);

if(isset($_REQUEST['password']) OR isset($_SESSION['username'])){
    if(isset($_REQUEST['password']) &amp;&amp; $_REQUEST['password'] == "Island"){
        $_SESSION['username'] = "John";
    }

    if(isset($_SESSION['username']) &amp;&amp; isset($_REQUEST['password'])){
        echo "You're logged in as ".$_SESSION['username']." (Private, using cURL with POST)";
    } elseif(isset($_SESSION['username']) &amp;&amp; !isset($_REQUEST['password'])) {
        echo "You're now logged in as ".$_SESSION['username']." (Private, using cURL with Cookies)";
    } else {
        echo "(Public Access - No auth received)";
    }
}

if(isset($_COOKIE['PHPSESSID'])){
    pre("Here are the cookies sent:");
    pre($_COOKIE);
}

The following code is the interesting part, here you can see how to do the cURL connection, grab the cookies and prepare them to be sent back with the next request.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/* A little function to trace the results when
 * needed, is not used on the functionality
 */

function pre($data){
    echo "


<pre>"
; print_r($data); echo "&lt;\/pre&gt;";
}
 

/* We open the first cURL connection against an authentication form
 * using only a valid password,
 */

$webservice = "http://www.reversedentropy.net/scripts/cookie-set.php";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $webservice);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
 curl_setopt($ch, CURLOPT_POSTFIELDS, "password=Island");

 // Execute the request
$request = curl_exec($ch);

/* We need to use regex to catch the cookies from the received
 * header and match them to a variable
 */

preg_match_all("/Set-Cookie: (.*)/", $request, $cookies);

/* Let's show the header to see which cookies did we receive.
 *
 */

pre($request);

/* Now that we have an array with all the cookies we need to parse
 * them in order to be able to send them back in the second request
 * against the server, so first of all we'll create a new variable
 * to store them.
 */

$jar = "";
foreach($cookies[1] as $value){
    /* This is a little tricky, as the cookies may have a path or expire
     * time set that we don't need at all but can interfere with the parse
     * so first we'll catch the cookie name (as $ckey)
     */

    $c = explode("=", $value);
    $ckey = $c[0];

    /* Then we have two possible scenarios: no more parameters apart from the
     * cookie and it's value, and a path, lifetime... If there's a ";" on the
     * string there's another parameter, so we explode again and catch only
     * the cookie value, forgetting about the rest. If there's no ";" we catch
     * directly the value and set it to $cval.
     */

    if($d = explode(";", $c[1])){
        $cval = $d[0];
    } else {
        $cval = $c[1];
    }

    /* The string containing all cookies should be in key=value;key=value[..] format
     * so we'll first of all remove all whitespaces, then urlencode the value and
     * add it to the $jar string that we created before
     */

    $cval = trim(preg_replace('/\s+/', '', $cval));
    $cval = urlencode($cval);

    $jar .= $ckey."=".$cval."; ";
}

/* Now we'll output the cookie string as reference and make another
 * call to the same server, thsi time without posting any password
 * We'll show the response as we want to see the logged message
 */

pre("Cookies inside the virtual jar:");
pre($jar);

By now we got a virtual cookie jar processed, we have all the cookies (the test script is setting a PHPSESSID and three test cookies without any relevant data) and we can store them on a database, on a file or work directly with them on the same script. So let's try it, we are going now to call the same server, but we're not going to send the password, we'll send the cookie.

1
2
3
4
5
6
7
8
9
10
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, false);
curl_setopt($ch, CURLOPT_COOKIE, $jar);
$request = curl_exec($ch);

/* And that's it, we're now logging against this server using cookies
 * on-the-fly, without storing them.  
 */

pre($request);
curl_close($ch);

And that's it, easy, right? You can see a little demo of this script working here, or download a zip containing the example source code.

Filed under: cURL, PHP Leave a comment
Comments (2) Trackbacks (1)
  1. Looks like you have some double encoding in the first section with &&

    Very useful though. I was going to do this myself if you hadn’t already done it.

  2. YEAH! just what I was looking for! Thank you!


Leave a comment

(required)