php $POST dynamic variable names security concerns - php

I am thinking of making a loop to gather all my $_POST variables and assign them to dynamically named variables.Something like this (not tested)
for($i; $i <= $_POST[].length; $i++){
${$_POST[i]} = $_POST[i]
}
But I am wondering about the security of something like this. This would then create a variable in the system for every bit of post data sent to the page. Can that variable be damaging even if the script I write doesn't reference it? Is this the type of thing I should avoid entirely? I have some pages that send quite a few variables and a script like this would prevent a whole lot of writing, but is it safe enough?

Yes there are possible security risks.
Say you have a variable $is_admin defined earlier in the code that gives someone admin abilities. If someone POSTS to that page with
$_POST['is_admin'] = true;
Then $is_admin is now true. Not good.
What's wrong with using $_POST?

Yes there can be security concerns/problems, for example one could overwrite any local variables which are already set, like database, config values ect.
So something like this should be avoided:
$yourImportantVar = 'Something relies on this';
//User POSTS yourImportantVar=overwritten
foreach ($_POST as $key => $value) {
$$key = $value;
}
echo $yourImportantVar; //overwritten
But if you want to implement a loop to save a chunk of code, you could create an allowed array which you loop over and extract out the value from the $_POST.
foreach (array(
'name',
'address',
'somethingelse',
'ect'
) as $key) {
$$key = isset($_POST[$key]) ? $_POST[$key] : null;
}

This is a very bad idea for security and maintainability. Simplified example why...
<?php
if (someRandomSessionCheck()) {
$isAdminUser = true;
}
if ($isAdminUser) {
// give access to everything
}
?>
Someone could post to the page with a variable "isAdminUser=1" and would have access to everything.
Another reason it is a bad idea is you can't clearly see from the script where your variables are created. This reduces maintainability of the script. What if you now want to run the script but instead need to get the data from somewhere else and not a POST?

Only issue I can think of at the moment is when that overwrites the existing variable in the scope. This can be very unsafe depending on what you do with it. Think about the variable being the URL you are doing a HTTP request to. Or worse, some flag variable which accesses some critical part of your code.
I will post an example that speaks about HTTP request:
<?php
$url = "http://safe/url/to/POSTto";
$var = array("url" => "http://www.mysite.com/url"); //assume this is $_POST
foreach($var as $key => $value){
${$key} = $value;
}
//now upon the HttpRequest, your site can receive the (critical) data which was actually meant for the safe site.
?>
EDIT: #Galen has posted about the flag variable I was talking about, so may be I need not post any example to highlight the problem.

PHP had a feature (using the term loosely) called register_globals. It has since been deprecated (PHP 5.3) and removed (PHP 5.4), but it mirrored the functionality for which you are looking. It performed the same thing as the PHP function extract() does, which sets variables in the current scope with names of the keys and values of the matching array values.
This is most definitely a security risk. Consider the example of a poor check for authentication:
if($is_logged_in) {
// Allow execution of destructive actions
}
If this feature was enabled (or you mimicked it), a malicious user would be able to set the variable $is_logged_in and bypass the login screen. Don't worry about saving typing. If you need to copy and paste a code block like this at the beginning of your files:
$something = $_POST['something'];
$another = $_POST['another'];
$stuff = $_POST['stuff'];
//etc.
Not only is it much more secure, but it doesn't leave developers (who aren't expecting register_globals) puzzled when undeclared variables start being used. Also, the fact that PHP has removed it and there are plenty of arguments against its use should be evidence enough.

<?php
/* Suppose that $var_array is an array returned from
wddx_deserialize */
$size = "large";
$var_array = array("color" => "blue",
"size" => "medium",
"shape" => "sphere");
extract($var_array, EXTR_PREFIX_SAME, "wddx");
echo "$color, $size, $shape, $wddx_size\n";
?>
Please check this. Same thing what you are going do by using loop. may help you

You are basically implementing extract($_POST, EXTR_OVERWRITE) which will overwrite any already existing variables. The manual already warns to use extract in a way you do:
Do not use extract() on untrusted data, like user input (i.e. $_GET, $_FILES, etc.). If you do, for example if you want to run old code that relies on register_globals temporarily, make sure you use one of the non-overwriting extract_type values such as EXTR_SKIP and be aware that you should extract in the same order that's defined in variables_order within the php.ini.
This can result in overwriting essential and sensitive variables, including those that cannot really be modified directly like $_SESSION, $_SERVER, and $GLOBALS:
POST /foo.php HTTP/1.1
Content-Type: application/x-www-urlencoded
_SESSION[user]=admin
This would have the same effect as $_SESSION = array('user'=>'admin').

Related

what does it mean to only allow expected input in php?

I'm reading a book on php security, and on input validation chapter there's a small section that talks about only allowing expected input.
This is the code that they show:
<?php
$expected = array( 'carModel', 'year', 'bodyStyle' );
foreach( $expected AS $key ) {
if ( !empty( $_POST[ $key ] ) ) {
${$key} = $_POST[ $key ];
}
else {
${$key} = NULL;
}
}
?>
I'm kind of confused, there's a small paragraph that explains what the code does. For what I get it assigns a value from the array as a key to $_POST. It also says that the array should be done programatically copied out of the GPC array.
What I don't understand is in what cases should I use this? And what is the GPC array?
GPC = Get Post Cookie, it's referring to variables coming from the user/browser. So instead of using $_GET, $_POST, $_COOKIE you should be cleaning and validating that data into a variable(s) that you can trust and know is clean.
As for the suggestion, what they are doing is limiting what your code has access to. In the above code example, you would probably have a form on the page with elements named carMOdel, year, and bodyStyle. What the code here is doing is restricting your code to ONLY interact with those 3 elements.
This prevents someone from passing extra parameters to your page in an attempt to inject code or some sort of SQL injection attack or any other number of things. In a simple example like this it might not make sense. But there are cases where people are taking form/api requests and running loops over everything in one of GPC arrays and doing stuff with anything in there. I would suggest not doing that to begin with.
This is just 1 of MANY things to protect your server/database. When developing webapps you should take the stance of trusting NOTHING that comes from the user side.
As far as security goes, OWASP is a great source for learning. here is a little cheat sheet that is in the works for php.
The code creates variables from data in the $_POST array. The names of the variables are taken from the keys of the $_POST array. PHP calls this (i.e. naming variables dynamically) variable variables.
This is usually a bad idea, because you do not control, which keys are present in the $_POST array, and thus, which variables are created. The user of your website controls this. A malicious user might name the POST variables in such a way that they overwrite variables that you intended for different purposes.
The book suggests to allow keys in the $_POST array to overwrite variables in a controlled manner. That's what $expected = array('carModel', 'year', 'bodyStyle') is for. This and the following code only creates the variables $carModel, $year and $bodyStyle. If, for example, a user posts current_user_has_admin_rights=1 to you application, a variable $current_user_has_admin_rights with a value of 1 will not be created.
My suggestion is to to stay away from variable variables alltogether and instead access the POST values through the $_POST array only. This makes it clear where the value comes from, an thus makes it easier to spot if such a value is handled in an unsecure manner.
It also says that the array should be done programatically copied out of the GPC array.
This could mean that you shouldn't necessarily write:
$expected = array('carModel', 'year', 'bodyStyle');
Rather write something like:
$expected = getExpectedInput('carForm');
Where the function getExpectInput would query the database for the input that should be in the form 'carForm'. Or perhaps it could be in a file and this function would fetch that content. They basically mean, it's not a good idea to hard-code the variables in the form. Rather write them somewhere so they are easy to change. If you ever made changes to the form and added a variable or changed a variable name, you don't have to change this particular code. This means that this function is usable on different forms. The idea is to write function that that can be re-used. Not those that are dedicated to one thing. OOP.
The getExpectedInput function would probably return an array. You can name this whatever you like or use any method.

Add php function to all POSTs and REQUESTs variables

I have a very old client who is now having issues with security because of the MYSQL Injection. This client does not have enough money to change his PHP database functions to PDO or MYSQLI. Nevertheless, he suggested that he wants a function that prevents mysql injuction. He is fully aware that the function is not perfect. But, he does not have any other temporary way right now. the function that I wrote for him is called safe();. Here comes my question. How can I apply the function to all POSTs and REQUESTs in his site. His site has many files, it will take hours to change. is there anything that I can add in the Header of every file that applies my function to all POSTs and REQUESTs variables?
something that maybe looks like this :
$_POST[*] = safe($_POST[*]);
Of course, the above code does not work. but I hope you get the idea.
You can use array_map, but I doubt it'll be perfect solution:
$final = array_map( "mysql_real_escape_string", $_POST );
In the end $_POST and $_GET are just arrays.
You could do a foreach like
foreach ($_POST as $key => $value) {
safe($value);
}
if they have old php servers etc. So if you have a general file that is included over the whole website and the "normal" functions aren't an option, this could be the back-up plan.
You are describing the infamous Magic Quotes, which are still available if the server is older than PHP/5.4.0 (which I presume is the case).
Please note that they affect all POST data, including that which is not going to be injected in a SQL query.
If you prefer your safe() function, you can simply write a simple script that makes the change and call it via auto_prepend_file.
Possible duplicate of https://stackoverflow.com/questions/15664021/php-escaping-vars-posted-through-var-and-got-by-postvari-with-a-meth
As I was told, there's no universal method, but you can give it a try through foreaching the $_POST array

Clearing _POST array fully

I want clear $_POST array content fully, all examples what I see in internet, looks like this:
if (count($_POST) > 0) {
foreach ($_POST as $k=>$v) {
unset($_POST[$k]);
}
}
Tell please, this variant will be not more better? (Point of view as saving resources)
if (count($_POST) > 0) {
$_POST = array();
}
or not ?
Yes, that is fine. $_POST is just another variable, except it has (super)global scope.
$_POST = array();
...will be quite enough. The loop is useless. It's probably best to keep it as an array rather than unset it, in case other files are attempting to read it and assuming it is an array.
To unset the $_POST variable, redeclare it as an empty array:
$_POST = array();
The solutions so far don't work because the POST data is stored in the headers. A redirect solves this issue according this this post.
How to delete $_POST variable upon pressing 'Refresh' button on browser with PHP?
It may appear to be overly awkward, but you're probably better off unsetting one element at a time rather than the entire $_POST array. Here's why: If you're using object-oriented programming, you may have one class use $_POST['alpha'] and another class use $_POST['beta'], and if you unset the array after first use, it will void its use in other classes. To be safe and not shoot yourself in the foot, just drop in a little method that will unset the elements that you've just used: For example:
private function doUnset()
{
unset($_POST['alpha']);
unset($_POST['gamma']);
unset($_POST['delta']);
unset($_GET['eta']);
unset($_GET['zeta']);
}
Just call the method and unset just those superglobal elements that have been passed to a variable or argument. Then, the other classes that may need a superglobal element can still use them.
However, you are wise to unset the superglobals as soon as they have
been passed to an encapsulated object.
You can use a combination of both unset() and initialization:
unset($_POST);
$_POST = array();
Or in a single statement:
unset($_POST) ? $_POST = array() : $_POST = array();
But what is the reason you want to do this?
To answer "why" someone might use it, I was tempted to use it since I had the $_POST values stored after the page refresh or while going from one page to another. My sense tells me this is not a good practice, but it works nevertheless.

Can I assign a value to a $_POST variable in PHP?

For example, I am using a $_POST variable to insert data into a DB. Just before this query I have a few tests and if they are true I want to adjust that (hidden) $_POST value.
Ex.
if($baby_dragon_eats_children){
$_POST['hidden_value'] = "grapes";
}
Can $_POST['hidden_value'] be assigned a new value and then be passed to another function as $_POST and be able to access the new $_POST['hidden_value']?
Thanks
$_POST['consolidate_answers']
IF you assign a value to $_POST you should document is very clearly as it is not common nor considered "best" practice.
IF you have any extensions of PHP such as Suhosin Patch... it may block such actions..
Handle your own arrays, don't depend on $_POST!
IF need be, make a copy of $_POST and work with that.
You can assign values to $_POST, but if you do so you should document it very clearly with comments both at the point of assignment and the later point of access. Manually manipulating $_POST can break future programmers' (including your own) expectations of what is in the superglobal and where it came from.
There may be other alternatives, like:
$my_post = $_POST;
$my_post['hidden_value'] = 12345;
// In a function:
function func() {
// Emulate a superglobal
echo $GLOBALS['mypost']['hidden_value'];
}
Can $_POST['hidden_value'] be assigned a new value and then be passed to another function as $_POST and be able to access the new $_POST['hidden_value']?
It can in normal PHP.
However, extensions like the Suhosin Patch may block this.
Also, it feels wrong in general. $_POST is intended to contain the raw, incoming POST data - nothing else. It should not be modified IMO.
A better way to go would be to fetch all the data you plan to insert into the database into an array, and do the manipulations in that array instead.
You can, it will work, but don't.
Create a copy of $_POST, perform your tests on that and modify that instead. For example.
$postdata = $_POST;
if ($postdata['somevalue'] != 'someothervalue') $postdata['newvalue'] = 'anothervalue';
// ...
If you manipulate variables auto-created by PHP, it will probably come back to haunt you later on when the variable doesn't hold the data you expect it to.
Find an alternative!
The $_POST variable has a distinct responsibility. Do not abuse its globalism.
I'd say don't abuse globalism either way, even if it means changing the design you have (or have in mind).
But if you choose not to, then consider creating a dedicated global registry just for your needs.
Don't manipulate language built-ins.

PHP, jQuery and .post(): mysql_real_escape_string messes things up

I have been tearing my hair with this one for a while.
index.php:
$(document).ready(function(){
$(".index_login_subm").click(function(){
uid = $("input:username").val();
pwd = $("input:password").val();
$.post("backend/login.php",{
uid:uid,
pwd:pwd
},function(data){
alert("Returned data: " + data);
});
return false;
});
});
login.php:
include("../../settings.php");
echo $uid;
echo $_POST['uid'];
none of the echoes returns anything.
settings.php:
foreach ($_POST as $key => $value) {
$$key = mysql_real_escape_string($value);
}
foreach ($_GET as $key => $value) {
$$key = mysql_real_escape_string($value);
}
The code works well if i comment the settings.php-include out (well, of course the echo $uid won't work), so it must be something the mysql_real_escape_string does. Anyone have any idea what I am doing wrong?
index.php also includes settings.php if that makes any difference.
EDIT: Posts below made me want to clarify; the paths are all correct. settings.php (and some other scripts) are all put outside of the root folder to make them inaccessible to a web user. They are working well when accessed by the scripts.
As described in the PHP manual, mysql_real_escape_string requires a real database connection established with mysql_connect (in the second parameter). If none exists, it tries to create one by simply calling mysql_connect(). If this does not work either, it returns false.
So, you would have to create a database connection first. But before you start doing this: note it is a really really bad idea to simply take every POST and GET variable and spit it into the global namespace.
First, I would avoid the variable variables, they're really not necessary in this context and they make for a moving target if you're not sure what keys are actually reaching that script, and they're bad practice due to the security issues that they create. Instead, I would put the escaped values within settings.php into an array. Then you can simply var_dump() the array after including settings.php, and see what was available.
function extract_requests(){
$res = array();
foreach ($_REQUEST as $key => $value) {
if($key){
$res[$key] = $value;
}
}
return $res;
}
Second, manually call login.php with the correct url string, exactly what you would want the javascript to call for, so that you can be sure that any behavioral problems aren't coming from the login.php script itself.
http://localhost/somewhere/login.php?uid=50
include("../../settings.php");
$input = extract_requests();
var_dump($input);
echo $input['uid'];
echo $_POST['uid'];
Finally, I would check your data that you are sending in post(). I'm not a javascript guru, but
uid:uid,
pwd:pwd
looks like it would cause issues to me, so it'd be good to make sure that your data is being sent in the way that you expect.
$(document).ready(function(){
$(".index_login_subm").click(function(){
uid = $("input:username").val();
pwd = $("input:password").val();
var inputData = {uid:uid, pwd:pwd};
alert(uid+" "+pwd);
// If you have a console.log() available, just use it on inputData here instead.
$.post("backend/login.php",inputData
,function(data){
alert("Returned data: " + data);
});
return false;
});
});
Edit: Based on the mysql_real_escape_string-only-when-a-connection-has-been-initiated thing, I've removed the escaping from the code above, because really, you'll be better off escaping the stuff right before you put it into the sql.
In addition, keep in mind that using javascript for the login WILL come back to bite you, with functionality as basic and necessary as login, you don't want a javascript error to break any ability for a viewer to log in. Here it will do so.
Is there a valid mysql link?
string mysql_real_escape_string ( string $unescaped_string
[, resource $link_identifier ] )
If the link identifier is not specified, the last link opened by mysql_connect() is assumed. If no such link is found, it will try to create one as if mysql_connect() was called with no arguments. If no connection is found or established, an E_WARNING level error is generated.
May be your error reporting setting ignores E_WARNING level errors.
index.php obviously is located in root directory of website, but the script that you are calling is in "backend" catalog.
Try using include_once instead of include and use absolute paths, not relative.
Example:
<?php
include_once($_SERVER['DOCUMENT_ROOT']."/settings.php"); //absolute path to file
echo $uid;
echo $_POST['uid'];
?>
Using absolute paths will make life easier for you if you move files around.

Categories