Help me understand tokens

Notice: This thread is very old.
MBP2013
Member | 3
+
0
-

Hello Nette community.

I've been working with the framework for a little while but first time I'm actually posting here as I'm having a hard time understanding the anti-CSRF tokens. Here's my problem:

I have made a web application using Nette and used the addProtection() function to ensure my contact form would not be easily spammed. Somehow someone did spam me though. The submit times and the randomly generated content makes me think that person was using a bot. I added captcha and it was gone. So that's cool.

Though, I wanted to see how that person did to spam me like he did so I decided to write my own “bot” using CURL. What I did was: connect to the contact form page, grab the token's value, generate random content and send the form with the token I grabbed from the previous request. But everytime I am getting the error (I've left the default message) saying that the token expired.

This is starting to hurt my brain, I don't see how Nette does it to understand it's not a legitimate use of the form. How can the token “expire” when I got it a few seconds ago ? Also, of course, I do save the session ID to a cookie file just like a browser would.

So really I don't understand how it's possible and how that guy managed to bypass when I, as the admin, can't.

Thank you for your input.

Filip Procházka
Moderator | 4668
+
0
-

Are you sure you're carrying the session (there are two cookies) to the second request? Looks like you're doing it somehow wrong.

Last edited by Filip Procházka (2013-11-24 22:35)

MBP2013
Member | 3
+
0
-

Hey Filip.

Yes, I'm pretty sure I am. In my cookie file I can see two cookies (PHPSESSID & nette-browser)

#HttpOnly_xxxxxx	FALSE	/	FALSE	0	PHPSESSID	9q2mz256mcqnbr7b7unmrahng4
#HttpOnly_xxxxxx	FALSE	/	FALSE	0	nette-browser	d8efij8l9o

I know at least the SESSID is being carried to the second request as I appeared as logged in in the server's reply. I'm not sure what “nette-browser” is made for though, when using CURL it changes all the time.
Below both requests:

<?php
		// This is how I make the first request (in order to get the token)
		public function getpage($url = "")
		{

			if (!$url || empty($url)) return false;
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt($ch, CURLOPT_HEADER, 0);
			curl_setopt($ch, CURLOPT_USERAGENT, $this->agent);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($ch, CURLOPT_REFERER, $this->referer);
			curl_setopt($ch, CURLOPT_COOKIEJAR, $this->cookie);
			curl_setopt($ch, CURLOPT_COOKIESESSION, true);
			curl_setopt($ch, CURLOPT_COOKIEFILE, $this->cookie);
			$a = curl_exec($ch);

			$info['status'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
			$info['time'] = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
			$info['lenght'] = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);

			curl_close($ch);

			if ($info['status'] != 200) { die("Connection error: {$info['status']}"); }
			return $a;
		}

		// This is how I send the form
		public function send_form($url, $data)
		{
			if (!$url || empty($url)) return false;
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt($ch, CURLOPT_HEADER, 0);
			curl_setopt($ch, CURLOPT_POST, true);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
			curl_setopt($ch, CURLOPT_USERAGENT, $this->agent);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($ch, CURLOPT_USERAGENT, $this->agent);
			curl_setopt($ch, CURLOPT_COOKIEJAR, $this->cookie);
			curl_setopt($ch, CURLOPT_COOKIESESSION, true);
			curl_setopt($ch, CURLOPT_COOKIEFILE, $this->cookie);

			$a = curl_exec($ch);

			$info['status'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
			$info['time'] = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
			$info['lenght'] = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);

			if ($info['status'] != 200) { die("Connection error: {$info['status']} - ".curl_error($ch)); }

			curl_close($ch);

			return $a;
		}
?>
Jan Tvrdík
Nette guru | 2595
+
0
-

Just to clarify – CSRF protection is not meant to protect you from spam.

You probably don't have the token in $data.

MBP2013
Member | 3
+
0
-

Jan, thank you for your response. I know they are not meant to protect you from spam but they help in most case as they require 2 connections while most spam bots will only handle 1. This is people wanting to spam a CSFR protected form will need to use twice as much bandwidth as they would with a “normal” form and I guess this stops quite a bit of spamming.

And, of course, the token is indeed in the $data array.

Could it maybe have something to do with the nette-browser cookie that keeps on getting updated ?

Last edited by MBP2013 (2013-11-25 12:20)

Filip Procházka
Moderator | 4668
+
0
-

The nette-browser cookie exist only to determine the way how the user had logged out (if he closed the browser, or the session cookie expired, …).