katie hudson
katie hudson

Reputation: 2893

PHP cURL Log into website and perform action on different page

I am having a little problem with something. I have the following code which uses cURL to log into a website, posting all the required data

<?php

$url = "https://someurl/login.aspx";
$ckfile = tempnam("/tmp", "CURLCOOKIE");
$useragent = $_SERVER['HTTP_USER_AGENT'];

$username = "[email protected]";
$password = "somepassword";

$f = fopen('log.txt', 'w'); 

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_COOKIEJAR, $ckfile);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, $useragent);

$html = curl_exec($ch);

curl_close($ch);

preg_match('~<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="(.*?)" />~', $html, $viewstate);
preg_match('~<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="(.*?)" />~', $html, $eventValidation);

$viewstate = $viewstate[1];
$eventValidation = $eventValidation[1];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
curl_setopt($ch, CURLOPT_COOKIEJAR, $ckfile);
curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_REFERER, $url);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_STDERR, $f);
curl_setopt($ch, CURLOPT_USERAGENT, $useragent);

$postfields = array();
$postfields['__EVENTTARGET'] = "";
$postfields['__EVENTARGUMENT'] = "";
$postfields['__VIEWSTATE'] = $viewstate;
$postfields['__EVENTVALIDATION'] = $eventValidation;
$postfields['btnLogin'] = "Login";
$postfields['txtPassword'] = $password;
$postfields['txtUserName'] = $username;

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
$ret = curl_exec($ch);

So the above code works fine. I do not close curl yet because I need to keep the cookie active. Anyways, once I have logged in using the above, I do

if($ret) {
    curl_setopt($ch, CURLOPT_URL, 'https://someurl.com/financial/quote.aspx?id=12345');
    curl_setopt($ch, CURLOPT_POST, 0);

    $data = curl_exec($ch);
    var_dump($data);
}

I can see from the output that I am now on the correct page. However, in the example above, I do not post anything. The page which I go too has a button on it. When looking at what is posted by this button within Firebug I see this

__EVENTARGUMENT 
__EVENTTARGET   
__EVENTVALIDATION   fsudifhsiudgfiusgdf
__VIEWSTATE 
__VIEWSTATE_GUID    0f26cc24-ef59-4bc7-87c0-141833df148b
ctl00$PageContent$btn2  Accepted

As such, I have tried to replicate the pushing of this button by doing the following

if($ret) {
    curl_setopt($ch, CURLOPT_URL, 'https://someurl.com/financial/quote.aspx?id=12345');
    $postfieldsInner = array();
    $postfieldsInner['__EVENTTARGET'] = "";
    $postfieldsInner['__EVENTARGUMENT'] = "";
    $postfieldsInner['__VIEWSTATE'] = "";
    $postfieldsInner['__VIEWSTATE_GUID'] = $viewstate;
    $postfieldsInner['__EVENTVALIDATION'] = $eventValidation;
    $postfieldsInner['ctl00$PageContent$btn2'] = "Accepted";

    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postfieldsInner);

    $content = curl_exec($ch);

    if (!$content) {
        echo 'An error has occurred: ' . curl_error($ch);
    } else {
        var_dump($content);
    }
}

This time however, the button action does not seem to occur. Am I missing something when making the second request?

Thanks

Upvotes: 0

Views: 1215

Answers (1)

miken32
miken32

Reputation: 42685

So, a couple of things jump out at me:

  • You're assuming that the magic values for __VIEWSTATE and __EVENTVALIDATION won't change. This is unlikely to be the case. You should pull those values again after fetching the data page.

  • You're passing the $viewstate variable as the value for __VIEWSTATE on the initial login, but you leave that blank on the subsequent post and instead pass $viewstate as __VIEWSTATE_GUID. Not sure if this is intentional or not.

  • You're using an array for CURL_POSTFIELDS which may cause problems. The documentation says:

    Passing an array to CURLOPT_POSTFIELDS will encode the data as multipart/form-data, while passing a URL-encoded string will encode the data as application/x-www-form-urlencoded.

  • And, very important, do NOT disable certificate validation, fix your server setup instead.


A few other suggestions, perhaps more a matter of style than substance though.

  • Passing an empty string as CURLOPT_COOKIEFILE will enable session handling without the need to save to a file.

  • You don't need to do curl_close() and curl_init() multiple times in a script; just reuse the existing handle. This saves having to redefine the options and reuse the session cookies.

  • Use curl_setopt_array() for cleaner code.

  • curl_exec() returns false on error, you should check for it explicitly.

Here's how I'd clean up the code:

<?php

$url = "https://someurl/login.aspx";
$ckfile = tempnam("/tmp", "CURLCOOKIE");
$useragent = $_SERVER['HTTP_USER_AGENT'];

$username = "[email protected]";
$password = "somepassword";

$viewstate_pattern = '~<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="(.*?)" />~';
$eventval_pattern = '~<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="(.*?)" />~';

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL            => $url,
    CURLOPT_COOKIEFILE     => "",
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_USERAGENT      => $useragent,
]);

// Getting the login form
$html = curl_exec($ch);

if ($html !== false) {
    preg_match($viewstate_pattern, $html, $viewstate);
    preg_match($evenval_pattern, $html, $eventValidation);
    $viewstate = $viewstate[1];
    $eventValidation = $eventValidation[1];

    $postfields = http_build_query([
        "__EVENTTARGET"=>"",
        "__EVENTARGUMENT"=>"",
        "__VIEWSTATE"=>$viewstate,
        "__EVENTVALIDATION"=>$eventValidation,
        "btnLogin"=>"Login",
        "txtPassword"=>$password,
        "txtUserName"=>$username,
    ]);

    curl_setopt_array($ch, [
        CURLOPT_REFERER=>$url,
        CURLOPT_POST=>true,
        CURLOPT_POSTFIELDS=>$postfields,
    ]);
    // Submitting the login form
    $html = curl_exec($ch);

    if ($html !== false) {
        curl_setopt_array($ch, [
            CURLOPT_URL=>'https://someurl.com/financial/quote.aspx?id=12345',
            CURLOPT_POST=>false,
        ]);

        // Getting the data page
        $html = curl_exec($ch);
        if ($html !== false) {
            preg_match($viewstate_pattern, $html, $viewstate);
            preg_match($evenval_pattern, $html, $eventValidation);
            $viewstate = $viewstate[1];
            $eventValidation = $eventValidation[1];

            $postfieldsInner = http_build_query([
                "__EVENTTARGET"=>"",
                "__EVENTARGUMENT"=>"",
        // Should this be empty?
                "__VIEWSTATE"=>"",
                "__VIEWSTATE_GUID"=>$viewstate,
                "__EVENTVALIDATION"=>$eventValidation,
                'ctl00$PageContent$btn2'=>"Accepted",
            ]);

            curl_setopt_array($ch, [
                CURLOPT_POST=>true,
                CURLOPT_POSTFIELDS=>$postfieldsInner,
            ]);
            // Posting the data page
            $html = curl_exec($ch);

            if ($html === false) {
                echo 'An error has occurred: ' . curl_error($ch);
            } else {
                var_dump($html);
            }
        } else {
            // Error getting the data page
        }
    } else {
        // Error submitting the login page
    }
} else {
    // Error getting the login page
}
curl_close();

Upvotes: 1

Related Questions