Jump to content
xisto Community
iGuest

Quickly Create Form Variables simple form, variable creation, referer check, safe guard variables

Recommended Posts

The reason I wanted to share this is I've seen so many people do this with their forms when using PHP.

 

$username = $_POST['username'];$password = sha1($_POST['password']);$another_var = $_POST['another_var'];

... and so on, just imagine if you had a large number of form inputs, do you really want to create each and every variable name?

 

Why people do this, is probably due to most of the examples I've seen on the web, that does not show an easier and much quicker way of doing it. Though my way might be much easier and quicker, it does introduce security concerns which I've tried to eliminate the most commonly seen problems with the method I'm about to show but since I only created this today, I haven't really had the chance to extensively test every possible flaw that could lurk in it.

 

I should mention I develop on PHP 5, but this should work with PHP 4.3 and could possibly work with PHP 4 but I think some of the things I've done would need to be rewritten to work with it.

 

So first of all the complete PHP page that demonstrates what I want to show, I will break down the PHP code and explain that, the form is simple HTML conforming to XHTML 1.1 standards, but the importance here is the PHP code itself.

 

simple_form.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://forums.xisto.com/no_longer_exists/ https://d https://d xmlns="http://forums.xisto.com/no_longer_exists/ https://4 https://4; xml:lang="en-NZ">	<head>		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />		<title>Quick Form Variable Creation</title>	</head>	<body>		<?php		$allowed_referers = array('yourdomain.com');		if (!empty($_POST['login']) && $_POST['login'] == 'Login')		{			if (!empty($_SERVER['HTTP_REFERER']))			{				$referer = current(array_splice(explode('/',$_SERVER['HTTP_REFERER']),2,1));			}			else			{				exit('<h1>BAD REFERER</h1><p>Oops!</p></body></html>');			}			if (!in_array($referer,$allowed_referers))			{				exit('<h1>BAD REFERER</h1><p>Oops!</p></body></html>');			}			array_pop($_POST);			$created_variables = '';			foreach ($_POST as $name => $value)			{				if (empty($$name))				{					if (!empty($name) || !empty($value))					{						${$name} = $value;						$created_variables .= '<tr><td>$'.$name.'</td><td>'.$value.'</td></tr>';					}				}			}		}		?>		<p><strong>Please Note:</strong> This is just a <em>demonstration</em>, please do not insert <strong>passwords</strong> that you use, as it is displayed back in plain text.</p>		<br />		<p><?php if (!empty($username)) { echo 'Welcome back <strong>'.$username.'</strong>, Thank you for logging in.'; } else { echo 'Hello <strong>Guest</strong>, Please log in.'; } ?></p>		<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">			<fieldset>				<legend>Login Form</legend>				<ul>					<li><label for="username">Username:</label> <input id="username" name="username" type="text" /></li>					<li><label for="password">Password:</label> <input id="password" name="password" type="password" /></li>				</ul>				<input id="login" name="login" value="Login" type="submit" />			</fieldset>		</form>		<?php		if (!empty($created_variables)) { ?>		<table>			<thead>				<tr>					<th>Variable Name</th>					<th>Variable Value</th>				</tr>			</thead>			<tfoot>				<td colspan="2"><em>These were the variables created from the above form.</em></td>			</tfoot>			<tbody>				<?php echo $created_variables; ?>			</tbody>		</table>		<?php } ?>	</body></html>

First of all, I'll explain the form. This is a basic form, that takes user's input and sends the information back to the same page to be processed. The input names of the form become the created variables so that we do not have to manually create them ourselves. With the information gathered from the form, it will alter and add additional information to the page, creating a table after the form, displaying the variables created and the values they were assigned. Remember the password is not encrypted in any way with this form, it is a demonstration and will display it back in plain text, if you're using a password input, be sure to encrypt the variable that it is assigned. In this example, you would do something like the code below after the variable has been created:

 

if (!empty($password)) { $password = sha1($password); }

Now on to the real stuff.

 

$allowed_referers = array('yourdomain.com');

This is to make sure that the form information being received is going to come from the domain you list in this array, as we don't want the form to be submitted from any other place as they could then alter our automatically created variables in a way that could compromise security. Now to make it more secure (just thought of it now), I would create a md5 hash of the form so that I can tell that the page has not been altered at all as it's possible to change the clientside of the page and still have it seem like it came from your site.

 

if (!empty($_POST['login']) && $_POST['login'] == 'Login')		{

This starts the form processing, when the login button is pressed, the posted information gets sent back to the page, and this checks whether login was created and whether the login had the value we made it.

 

if (!empty($_SERVER['HTTP_REFERER']))			{				$referer = current(array_splice(explode('/',$_SERVER['HTTP_REFERER']),2,1));			}			else			{				exit('<h1>BAD REFERER</h1><p>Oops!</p></body></html>');			}			if (!in_array($referer,$allowed_referers))			{				exit('<h1>BAD REFERER</h1><p>Oops!</p></body></html>');			}

This is the referer check, it checks whether our server got the referer, it then grabs the domain part of the referer (I hope) and if it's not set or is not our allowed referers, it exits with a simple BAD REFERER message, which I'm sure you could be more creative about it. This is where PHP 4 could fail, but I'm sure there's alternative ways to grab the referer.

 

array_pop($_POST);

This is to drop the $_POST['login'] from the $_POST, so we don't create a variable for it, but if you do want it, then leave it in. Now I believe this relies on form ordering, so if the submission is not the last, we could not do this, but since we created the form, and made sure it came last, then this should be fine for us.

 

$created_variables = '';

This isn't vitally important, it's just to help this demonstration and give us something we can store information in to display back so we can see what happened.

 

foreach ($_POST as $name => $value)			{				if (empty($$name))				{					if (!empty($name) && !empty($value))					{						${$name} = $value;						$created_variables .= '<tr><td>$'.$name.'</td><td>'.$value.'</td></tr>';					}				}			}

This is what people were probably waiting for, the part that makes variable creating easier, basically we are looping through each $_POST item, using the form name and the value. The first if statement checks that what we are using in the form is empty and not used already, otherwise we could potentially overwrite an internal variable we use as it could affect the behaviour of the program, you could possibly handle this another way by changing the variable so it doesn't conflict but then you would need to figure out how you can remember that change, I don't like variated variables like that, e.g. $password, $password1, $password3 so make sure that your form doesn't use anything that conflicts and this is also an area we have to make sure is secure.

 

Next I did a check if the form name exists and if the value exists, if not, I don't bother with creating a variable, but in your case, you may want to create the empty variables too, and perform your own check on them, incase they are required variables. A quick way you could do this, if you had required variables is to prefix their form name with req_ and then if you encountered this prefix, make sure it's assigned, if not assign a boolean value that the form failed, and tell them what it was that was needed to be filled in and do not process the form any further.

 

The part that automatically creates the variable is the ${$name} = $value; reading this from the inner, {$name} is the form name the brackets convert it to it's name value, e.g. username, the outer $ is for the variable, so we get $username. Simple right? You could also do $$name but it's not the best practise, however I had to use it in the above for checking if the variable had been created, so it's the only time I'm letting it slide.

 

That's pretty much all I should need to explain, the rest just checks the variables are created and displays the information back, there's nothing too drastic about what it's doing. I just want to wrap this up, as I got to get back to doing my work.

 

So hopefully this helps a few people.

 

Cheers,

 

MC

Share this post


Link to post
Share on other sites

Thanks for the tip. That's something I've never came to think about probably because I've rarely had to process massive amounts of form input. Proof that's it's really worth reading these forums. And where you commented that PHP 4 could fail, I don't think it will. I didn't test the code but I didn't notice anything unfamiliar to me and I've always developed in PHP 4.

Share this post


Link to post
Share on other sites

First of all congrats, excellent topic, especially the one related with the variables variable. Now, i just test your code and it have an error because it doesnt shows the table with the submited data when you fill both fields, but dont worry its as simple as add an ! symbol :P.

Simply change this line:

if (empty($$name))
with this one:
if (!empty($$name))
Best regards,

Share this post


Link to post
Share on other sites

Actually TavoxPeru, I did notice that area failing on PHP 4.4.4 (Xisto's version which means the area I commented won't fail, but anything below PHP 4.3 could be of some concern), PHP 5 processes this correctly.What you are suggesting isn't what the code reflects, what you're saying is, if the variable does exist (not empty means has something), then overwrite the already existing variable. This to me seems like register globals is on, or some automatic variable creation has already taken place, not the way I would configure php.ini nor would this be default, I'll try to dig into it more and see why the variables are already being created, as I might write a routine to remove this automation (I know this is what we wanted, but securing this area would be far better and just allowing ourselves to do this automation).For now in PHP 4, just comment out if( empty(${$name}) ) { and the ending bracket } and it'll work as intended but will still overwrite existing variables, unless you think you can post the code to solve this before I get to work on it, then by all means do so.I'll see what I can do, I'm not at home at the moment, and I'm not sure when I'll post the solution, I've also got a bit of code clean up that I did that I want to post too as well as more additions to this code.Now the reason I'm sharing this area, is that I started working on a complete class for form processing, so that the class can handle all aspects of a form, including uploaded files, mailing, searching, registration, you name what the form does, and that is what I want to have in it. I'm also trying to incorporate ajax into the mix too, so that it will seem to process a lot smoother.Cheers,MC

Share this post


Link to post
Share on other sites

The cPanel's phpinfo -PHP 4.4.4- shows that the register_globals is on, so i will test the code locally with PHP5 to verify if it works fine, i guess that this is the problem.Related to the class that you are working on, I think that you have a lot of work to do :P so, in case you need some help, please let me know to colaborate.Best regards,

Share this post


Link to post
Share on other sites

Here's the updated version that works with PHP 4.4.4 and above with register_globals on. I've seperated the HTML and coding, so that it's not all mixed together, it's easier to work with it if it's seperated.

 

Here's the simple form (form.html)

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://forums.xisto.com/no_longer_exists/ xmlns="http://forums.xisto.com/no_longer_exists/; xml:lang="en-NZ">	<head>		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />		<title>Simple Login Form</title>	</head>	<body>		<form action="login.php" method="post">			<fieldset>				<legend>Login Form</legend>				<ol>					<li><label for="username">Username:</label> <input id="username" name="username" type="text" /></li>					<li><label for="password">Password:</label> <input id="password" name="password" type="password" /></li>				</ol>				<input id="login" name="login" type="submit" value="Login Now" />			</fieldset>		</form>	</body></html>

Here's the PHP (login.php):

 

<?phperror_reporting(E_ALL);if ( @ini_get('register_globals') ){	if ( isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS']) )	{		exit('<h1>Sorry</h1><p>Not Allowed!!!</p>');	}	$protected = array('GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES');	$input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());	foreach ( $GLOBALS['input'] as $k => $v )	{		if (!in_array($k, $GLOBALS['protected']) && isset($GLOBALS[$k]) )		{			unset($GLOBALS[$k]);		}	}}$allow = array('yourdomain.com');$referer = '';$created = '';if ( !empty($_POST['login']) && $_POST['login'] == 'Login Now' ){	if ( !empty($_SERVER['HTTP_REFERER']) )	{		$GLOBALS['referer'] = current(array_splice(explode('/', $_SERVER['HTTP_REFERER']), 2, 1));	}	if ( !in_array($GLOBALS['referer'], $GLOBALS['allow']) )	{		exit('<p>Bad Referer</p>');	}	unset($_POST['login']);	foreach ( $_POST as $key => $val )	{		if ( empty($GLOBALS[$key]) )		{			if ( !empty($key) && !empty($val) )			{				$GLOBALS[$key] = $val;				if ( is_numeric($val) )				{					$GLOBALS['created'] .= '<p>$' . $key . ' = ' . $val . ';</p>'."\n";				}				else if ( is_string($val) )				{					$GLOBALS['created'] .= '<p>$' . $key . ' = \'' . $val . '\';</p>'."\n";				}			}		}	}	echo $GLOBALS['created'];}?>

Now I'm using the GLOBALS variable a lot to just show the scope of the variables, that way you should understand that these variables are in the global scope range and not tied into specific functions.

 

The only major difference is that I'm emulating register_globals as off, which is the first instruction for this code, because it has to occur first to eliminate the variables being created. It's always a good idea to initialise your own variables rather than rely on them to be already created and initialised.

 

Now you are probably wondering, what if a variable you want created from your form doesn't get created because it's value was empty, well my method for determining whether a variable is required or not, I would prefix their names with req_ e.g. req_username and then I would create the functions needed to validate these required fields, I have a few validation routines including exact RFC specifications for email addresses as well as my more realistic email address validation routine, however since RFC states what can be valid, it's probably the better choice, my more realistic version is because some of the things they allow, just doesn't seem like what most email providers allow you to have anyways.

 

There's still more I want to show, including how to make sure a form was not tampered with, but that will probably come after the xmas season, so all the best and merry christmas.

 

Cheers,

 

MC

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

Terms of Use | Privacy Policy | Guidelines | We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.