Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

$_SESSION and Google Chrome 1

Status
Not open for further replies.

g0ste

Technical User
Feb 16, 2003
95
AU
The problem:
Hi, I have a 'form' that is submitted via AJAX, and during the submission / validation process it makes a call to another page to check that the captcha is the same as the variable stored in the [tt]$_SESSION[/tt]. If it is, then it returns a string and the sending process continues, as you would expect.

This works perfectly well in Firefox 3.6, IE7 (don't have access to 6 or 8 at the moment) and Safari 4. However, in Opera 10 and Google Chrome 4 it always fails the comparison. So upon failure, I had the php comparison script echo the session value, then have the ajax script alert the [tt]responseText[/tt]and it is completely different to the value on the page. But this alerts the correct value in FF, IE and Safari.

I have googled this problem and not found anything conclusive or useful.

It's been puzzling me for a day now. It all seems logically correct. Can anyone offer any insight into this and why it is not working on Opera 10 and Chrome 4?

Here's the captcha generation:
Code:
session_start();
[green]//i added this to ensure we were getting the same session var each time[/green]
unset($_SESSION['k']);
[green]//set up captcha:[/green]
$random_str = md5(microtime());
$result_str = substr($random_str,6,6); [green]//this var goes into img later on[/green]
[green]//set session var 'k'[/green]
$_SESSION['k'] = $random_str;

...snip - generating img below...

and now the ajax request:
Code:
...snip...
var url = "/inc/vcap.php";
url = url + '?c=' + document.getElementById('incap').value;
url = url + '&sid=' + Math.random();
check_cap.open("GET", url, true);
check_cap.send(null);


and here is the comparison:
Code:
if (isset($_GET['c'])){
	$captcha = $_GET['c'];
	session_start();
	$key = substr($_SESSION['k'],6,6);
	if($captcha == $key){
		echo('1');
	}
	else{
		echo('0');
	}
}

Thanks for any help, you may just be preventing premature baldness!

Dan

________________________________
Top 10 reasons to procrastinate:
1)
 
This could be a safety measure from Google's browser. What happens is this: You have already opened a session by requesting a page with the captcha. That page has a script (yuck! don't check captchas at the client side) that uses the same session. That is, if it is your script. It probably is, but script inserting attacks are so common these days that browsers may very well assume that scripts are not by definition trustworthy or issued by the page maintainer. So Chrome may very well open a new session to prevent any cross-site scripting attack to be able to hijack your session.

In the Ajax request (at the server side), just check if the right session ID was sent. If not, the above might explain it. Maybe a web search like "chrome prevent XSS attack" can give you the answer you were looking for.


+++ Despite being wrong in every important aspect, that is a very good analogy +++
Hex (in Darwin's Watch)
 
a few potential hiccoughs.

1. prevent race conditions by calling session_write_close() before the script ends.
2. the unset() command has no impact whatsoever on the session id. but i guess you know that.
3. are you sure that the value is getting into the incap control correctly?

i assume that your check_cap functions are correctly phrased? you have not posted the code.

for the sake of testing, instead of using a unique pseudo-random key why not use a static text string?
 
Hello! And thank you both for your replies.

@DonQuichote
It is my script. I wrote the script a few months ago. I suppose this is one of the advantages of using those libraries. Anyways, the only difference between the script that currently works and this faulty one is the host. Not sure if that is my coding, but the fundamentals are the same (i.e. the comparison via vcap.php is exactly the same):
Code:
[green]//original comparison that works currently in chrome...[/green]
session_start();
$random_str = md5(microtime());
$result_str = substr($random_str,0,5);
[silver]...snip...[/silver]
$_SESSION['key'] = $result_str;
header("Content-type: image/jpeg");
imagejpeg($img);
imagedestroy($img);

With regards to the XSS you mentioned: this is a massive part of Opera 10 - because if I included the full domain in my ajax send request:
Code:
var url = "[red][URL unfurl="true"]http://www.mysite.com/inc/vcap.php[/URL][/red]";
url = url + '?c=' + document.getElementById('incap').value;
url = url + '&sid=' + Math.random();
Opera goes nuts and throws a huge "Security Violation" error. And when I put in the full domain, chrome doesn't even bother getting so far as to compare the scripts. In fact it chrome does nothing.

So, it has to be addressed by "inc/vcap.php" or "../inc/vcap.php" etc.

However, no checking of captchas is performed client side (so to speak)... The user enters a value into a textbox and sends that to the server, the server compares to the session var and gives a result if it matches or not. So the comparison is done at the server (if this is what you mean, of course!).

@jpadie:
1 - good point. will give this a try asap.
2 - whoops! supposed to be [tt]session_unset();[/tt] (not that this would have much impact on an already 60% working script).
3 - check_cap is just the ajax function name such as [tt]check_cap = new XMLHttpRequest();[/tt] and similar for other browsers etc. [tt]incap[/tt] is working fine. the data being sent that way is by me at the moment, always 100% correct (apparently not on chrome or opera though!).

for the sake of testing, instead of using a unique pseudo-random key why not use a static text string?
yes I have and it works, but when I revert back to a pseudo-random string, it continues with it's bizarre ways.

I am learning to dislike chrome (it's easier to do that than dislike my code)... arf arf!

________________________________
Top 10 reasons to procrastinate:
1)
 
session_unset()? that wouldn't work since the function takes no arguments.

yes I have and it works

now we're getting somewhere.

let's have a closer look at your code, if you will. can you post your complete script here and I will check it?

as an alternative to image based captcha I have posted a solution which I believe is better in most respects. you can find it here
 
@DonQuichote: Even if Chrome was creating a new session, wouldn't that override the existing session if it uses the same session variable?

@jpadie:
just a quick note about your script you linked:

line 105: [tt]if (session_id() [red]=[/red] '') session_start();[/tt]

should be a comparison, not an assignment.

but apart from that, nifty script. not quite what I am trying to achieve, because like I said, I used the script on a website before, but on this one it doesn't work. very strange.

I tried adding [tt]session_write_close()[/tt] upon generation of the captcha image and assigning the session variable, but it had no affect on the script in Opera or Chrome.

captcha creation:
PHP:
session_start();
unset($_SESSION['k']);
[green]//set up captcha:[/green]
$random_str = md5(microtime());
$result_str = substr($random_str,6,6);
[green]//set session var 'k'[/green]
$_SESSION['k'] = $random_str;
[green]//make img[/green]
$img = imagecreatetruecolor(90, 29);
$bg = imagecolorallocate($img, 0, 0, 0);
imagefilledrectangle($img, 0, 0, 90, 35, $bg);
$text_color = imagecolorallocate($img, 0, 255, 198);
[green]//10 lines placed at random coords with random colors.[/green]
for ($i=0;$i<=5;$i++){
	$line_color = imagecolorallocate($img, rand(0,255), rand(0,255), rand(0,255));
	$imgL1 = rand(1,75);
	$imgL2 = rand(1,75);
	$imgL3 = rand(1,75);
	$imgL4 = rand(1,75);
	imageline($img,$imgL1,$imgL2,$imgL3,$imgL4,$line_color);
}
[green]//generating random positions for the string to be placed[/green]
$imgstr_x = rand(0,20);
$imgstr_y = rand(0,12);
imagestring($img, 5, $imgstr_x, $imgstr_y, $result_str, $text_color);
header('Content-type: image/jpeg');
imagejpeg($img, null, 100);
imagedestroy($img);

now the javascript that is called when it reaches the captcha:
JavaScript:
[green]...required fields are filled out, now check captcha...[/green]
else{
	var check_cap;
	try{
		[green]//safari, firefox, opera[/green]
		check_cap = new XMLHttpRequest();
	} catch (e){
		[green]// ie[/green]
		try{
			check_cap = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try{
				check_cap = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e){
				[green]// eek! something went wrong[/green]
				alert("Ajax request failed. Make sure you are connected to the internet.");
				return false;
			}
		}
	}
	check_cap.onreadystatechange = function(){
		if (check_cap.readyState == 4){
			if (check_cap.responseText == "1"){
				document.getElementById('incap').style.borderColor = '#333';
				sendmail();
			}
			else if (check_cap.responseText == "0"){
				errors = errors + 1;
				document.getElementById('incap').style.borderColor = '#800D0D';
				return false;
			}
		}
	}
	var url = "/inc/vcap.php";
	url = url + '?c=' + document.getElementById('incap').value;
	url = url + '&sid=' + Math.random();
	check_cap.open("GET", url, true);
	check_cap.send(null);
}

and finally the comparison:
PHP:
if (isset($_GET['c'])){
    $captcha = $_GET['c'];
    session_start();
    $key = substr($_SESSION['k'],6,6);
    if($captcha == $key){
        echo('1');
    }
    else{
        echo('0');
    }
}

i really don't understand what is going wrong.

________________________________
Top 10 reasons to procrastinate:
1)
 
if you are trying to achieve a captcha then my script will do that. it distinguishes (up to a reasonable degree) human from an automaton. the advantage of my script is that it needs no human interaction to make it work, thus avoiding the 'hassle' of an image or calc based captcha.

i will take a look at your script later on today most probably.
 
@g0ste:
It is my script.
I was not doubting that, but merely suggesting that the browser would not take that for granted. Anyway, I would be really curious to what data is sent to the server. You can check the $_SERVER superglobal. Any membet starting with HTTP_ is a header that is sent by the browser. So if a browser sends an "Accept-language" header, it shows up as $_SERVER['HTTP_ACCEPT_LANGUAGE'].

Even if Chrome was creating a new session, wouldn't that override the existing session if it uses the same session variable?
Technically yes, unless it does not handle cookies at all for javascript-based requests. In that case, new sessions are created but their reference not stored in a clientside cookie.

However, no checking of captchas is performed client side (so to speak)... The user enters a value into a textbox and sends that to the server, the server compares to the session var and gives a result if it matches or not. So the comparison is done at the server (if this is what you mean, of course!).
Not really. The checking takes place on the server, but the decision is made by the client. That is what is wrong. An attacker would simply not send the checking request and send the form data anyway. Better send the form data including the captha and let the server reject the form data (re-sending the filled-out form so the human user has a second chance in case of a typo). When dealing with security issues, never assume that you have any control over the client side code. Client side code should just facilitate the user (like checking for empty fields), not do anything with security.

+++ Despite being wrong in every important aspect, that is a very good analogy +++
Hex (in Darwin's Watch)
 
DonQuichote: I am with you now. I knew what you meant earlier when I said "It's my script." but I failed to clarify that I knew it was not a foreign script being injected (or whatever), because I tried this on a local machine and on the internet with the same results.

Your last point you made makes a lot of sense. I can't believe I didn't think of that before. I'll rewrite it to send all at once and see if that has any bearings on the outcome.

Thanks for your help!

________________________________
Top 10 reasons to procrastinate:
1)
 
I just found the solution to my problem.

I realised when I was testing the Chrome and Opera pages, they were pointed at but the script apparently only works with http://www.mydomain.com ... have you ever heard of this before?

Bizarre. But thanks for the advice folks! I appreciate it greatly.

________________________________
Top 10 reasons to procrastinate:
1)
 
have you ever heard of this before
yes. it's to do with cookies and sub-domains. if you want your cookie to be valid in all browsers in all your subdomains you need to set the domain parameter of the cookie (session_set_cooki_params()). check the manual for instructions.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top