Categories
Development

Auth in bot-proof login form with PHP Curl and JavaScript

Recently I was challenged to make a script that would authenticate through a bot-proof login from and redirect to a logged in page. 

Main concept

The form is not online anymore… Through a web sniffer I’ve found out the form is sent as POST xhr/ajax request with on-fly generated security parameter – hash. See a figure below:post payload

My script authentication concept is put down in the following steps:

  1. Load a form into a browser (PHP cURL with CURLOPT_RETURNTRANSFER => true).
  2. Fill the form out with login/password (on page JavaScript).
  3. Generate security hash based on the form serialized inputs. Append a hash input field (on page JavaScript).
  4. Run on page JavaScript to automatically submit the extended form. We submit the form to the same script as a POST request.
  5. Having been rerun, the script gathers the POST payload and submits it as POST XHR to the target server for authentication.
  6. Then with PHP cURL, we request logged.php file to get the result (test or fail).

Let’s expose each of the steps with some code. Or you might want to jump to the whole code.

Load form into browser

$header=get_web_page('https://auth.gripon.ru/'); 
print_r($header['content']); 
function get_web_page($url){ 
     $options = array( CURLOPT_RETURNTRANSFER => true, // return page 
     CURLOPT_HEADER => true, //return headers in addition to content ); 
     $ch = curl_init( $url ); 
     curl_setopt_array( $ch, $options ); // to save cookie in "cookie jar", namely cookie.txt file $tmpfname = dirname(__FILE__).'/cookie.txt'; 
     curl_setopt($ch, CURLOPT_COOKIEJAR, $tmpfname); 
     curl_setopt($ch, CURLOPT_COOKIEFILE, $tmpfname); 
     $rough_content = curl_exec( $ch ); 
     $header = curl_getinfo( $ch ); curl_close( $ch ); 
     $header_content = substr($rough_content, 0, $header['header_size']); 
     $body_content = trim(str_replace($header_content, '', $rough_content)); 
     $header['content'] = $body_content; 
     return $header; 
}

Fill the form out

We inject JavaScript code to be run with GET parameters (login/password).

echo '<script>$("#login-username").val("' . $_GET['login'] . '"); 
              $("#login-password").val("' . $_GET['password'] . '");		 
     </script>';

Generate security md5 hash

The form’s Javascript file (md5.js) produces hash value out of serialized form upon Login button click:

var ser = $( "#loginform" ).serialize(); var hash=md5(ser);

To load md5.js in our script we included it in the page’s head section. Now we create hash value and append a new hash input field:

<html>
<head> 
  <script src='https://auth.gripon.ru/md5.js'></script>
</head>
<body>
<?php echo '<script> var form = $( "#loginform" ).serialize(); 
              var hash = md5(form); 
              $("#loginform").append(\'<input id="hash" type="text" name="hash" value="\' + hash + \'" >\' ); </script>'; 
?>

Now the form is extended with a hash input.

Auto-submit the extended form

To send the form to our own auth script we change form’s target `action` attribute to `#`. Then we may trigger from submission.

echo '<script>$("#loginform").attr( "action", "#" ); 
              loginform.submit(); 
              console.log("Login form is sent."); </script>';

Submit as POST XHR to the target server and fetch auth result

We simply submit to auth.gripon.ru for authentication. To make it XHR as in the original form we add this:
curl_setopt($ch, CURLOPT_HTTPHEADER, array(...));

if ( isset($_POST['hash']) )
{
     // xhr POST request to post.php
    post_xhr('https://auth.gripon.ru/post.php', $_POST); 
	
    // get logged.php response
    $header = get_web_page('https://auth.gripon.ru/logged.php');    
    print_r($header['content']); 
    exit;
} 

function post_xhr( $url, $post=NULL){
 $options = array(
 CURLOPT_POSTFIELDS => $post ? http_build_query($post) : '',
 CURLOPT_POST => true, 
 CURLOPT_RETURNTRANSFER => true, // return web page 
 );

 $ch = curl_init( $url );
 curl_setopt_array( $ch, $options ); 
 
 $tmpfname = dirname(__FILE__).'/cookie.txt';
 curl_setopt($ch, CURLOPT_COOKIEJAR, $tmpfname);
 curl_setopt($ch, CURLOPT_COOKIEFILE, $tmpfname);

 // to make request xhr
 curl_setopt($ch, CURLOPT_HTTPHEADER, array(
 "Host" => "auth.gripon.ru",
 "User-Agent" => "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
 "Accept" => "application/json, text/javascript, */*; q=0.01",
 "Accept-Language" => "en-us,en;q=0.5",
 "Accept-Encoding" => "gzip, deflate",
 "Accept-Charset" => "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
 "Keep-Alive" => "115",
 "Connection" => "keep-alive",
 "X-Requested-With" => "XMLHttpRequest",
 "Referer" => "https://auth.gripon.ru"
 ));
 curl_exec( $ch );
 curl_close( $ch );
}

The whole code

<?php
if ( isset($_POST['hash']) ) 
{
     // xhr POST request to post.php
    post_xhr('https://auth.gripon.ru/post.php', $_POST); 
	
	// get result from logged.php
	$header = get_web_page('https://auth.gripon.ru/logged.php');    
    print_r($header['content']); 
	exit;
} 
else if (!isset($_GET) OR !isset($_GET['login']) OR !isset($_GET['password']) )
{
    echo 'Missing GET parameters.';
	exit;
}
?>
<!DOCTYPE html>
<html>
<body>
<?php 
// the original auth form load 
$header=get_web_page('https://auth.gripon.ru/');
 
// output form in browser
print_r($header['content']);

function get_web_page($url){
	$options = array(            
		CURLOPT_RETURNTRANSFER => true, // return page
		CURLOPT_HEADER => true, //return headers in addition to content
	);

	$ch = curl_init( $url );
	curl_setopt_array( $ch, $options );

	// to save cookie in "cookie jar", namely cookie.txt file
	$tmpfname = dirname(__FILE__).'/cookie.txt';
	curl_setopt($ch, CURLOPT_COOKIEJAR, $tmpfname);
	curl_setopt($ch, CURLOPT_COOKIEFILE, $tmpfname);
	
	$rough_content = curl_exec( $ch );
	$header  = curl_getinfo( $ch ); 
	curl_close( $ch );

	$header_content = substr($rough_content, 0, $header['header_size']);
	$body_content = trim(str_replace($header_content, '', $rough_content));
	$header['content'] = $body_content;
	return $header;
}

function post_xhr( $url, $post=NULL){
	$options = array(
		CURLOPT_POSTFIELDS => $post ? http_build_query($post) : '',
		CURLOPT_POST => true,            
		CURLOPT_RETURNTRANSFER => true, // return web page 
	);

	$ch = curl_init( $url );
	curl_setopt_array( $ch, $options ); 
	
	$tmpfname = dirname(__FILE__).'/cookie.txt';
	curl_setopt($ch, CURLOPT_COOKIEJAR, $tmpfname);
	curl_setopt($ch, CURLOPT_COOKIEFILE, $tmpfname);

	// to make request xhr
	curl_setopt($ch, CURLOPT_HTTPHEADER, array(
		"Host" => "auth.gripon.ru",
		"User-Agent" => "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
		"Accept" => "application/json, text/javascript, */*; q=0.01",
		"Accept-Language" => "en-us,en;q=0.5",
		"Accept-Encoding" => "gzip, deflate",
		"Accept-Charset" => "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
		"Keep-Alive" => "115",
		"Connection" => "keep-alive",
		"X-Requested-With" => "XMLHttpRequest",
		"Referer" => "https://auth.gripon.ru"
	));
	curl_exec( $ch );
	curl_close( $ch );
}
?>
</body>
<script  src='https://auth.gripon.ru/md5.js'></script>	
<script>
	// fill out from with input values 
	$("#login-username").val("<?php echo $_GET['login']; ?>"); 
	$("#login-password").val("<?php echo$_GET['password']; ?>");
	var form = $( "#loginform" ).serialize(); 
	// create hash value - md5.js
	var hash = md5(form); 
    // append a hash input field
	$("#loginform").append('<input id="hash" type="text" name="hash" value="' + hash + '" >'); 
	// change form's target, `action` attribute
	$("#loginform").attr( "action", "#" ); 
	// extended (with hash) form submition	
	loginform.submit(); 
</script>
</html>

Disclaimer

I admit, it might not be the most optimal way to authenticate through this bot-proof form, yet I think some may get some ideas of how to handle tough logging-in cases in the web scraping. Welcome to suggest some better solutions.

11 replies on “Auth in bot-proof login form with PHP Curl and JavaScript”

Hello
I like to put a login ( test) and password ( test1 ) to see the work , for I am beginner information
thanks in advance

De plus chaque fois que je lance ta commande j’ai “Missing GET parameters.” ce qui est normale car je n’ai pas mis de login ni de pass
Amicalement

thank you for your reply but I work weird.
but I take a sui works program , I modify it to see how it works, the I ‘m stuck , I can not seem to get past the password or login,
thank you in advance for your help

Thank you for your help
For my Part I waiting for me to put in your file
$username = ‘myuser’;
$password = ‘mypass’;
cordialy

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.