Security vulnerabilities of using input-generated variables in PHP - php

Given knowledge of the following problematic PHP code, where variables are generated based on the request data (e.g. submitted HTML form fields), what exploits are possible?
I'm aware that the request could be constructed to contain data specifically crafted to overwrite existing variables (although we're blind to what variables exist), and thereby break the application, I'm just struggling to think of ways it could break the application for gain.
<?php
function foo( $data ) {
// Some unknown code may exist here
foreach( $data as $key => $value) {
$$key = $value;
}
// Some other unknown code may exist here
}
if ( ! empty($_POST) ) {
foo( $_POST );
}

<?php
$userid = $_SESSION['uid'];
function foo( $data ) {
// Some unknown code may exist here
//$_POST contains userid field;
foreach( $data as $key => $value) {
$$key = $value;
}
// Some other unknown code may exist here
}
if ( ! empty($_POST) ) {
foo( $_POST );
}
$credit_card_no = $cardDetails->getById($userid);
echo 'your current card on file is: ' . $credit_card_no;

Variables are placeholders in your algorithm, they represent values which get filled in at runtime. In your algorithm you do certain things with these values, and you depend on those values to be in a certain known state to some degree or another. Usually the variables, the placeholders, in your algorithms are fixed and are operated on in a certain order; that is the definition of your algorithm.
we take A
and assign to it B
and then we assign A to C
and then we pass C to the function X
...
Variable variables now are basically allowing you to modify those operations based on other values:
we take A
and assign to it B
AND THEN WE ASSIGN B TO ??? <---
and then we assign A to C
and then we pass C to the function X
...
Well, what variable was B assigned to? How is the rest of your algorithm going to behave from now on? Can you trust your knowledge about the state of any particular variable anymore now?
Those are the problems that variable variables based on user input create. It's not impossible to code carefully enough to avoid this being a problem, but you do have to be extremely careful.

Never trust user generated data. It's very dangerous code because you can easily overwrite the superglobals for example.
Read more about the risks:
http://www.php.net/manual/en/security.globals.php
http://php.net/manual/en/function.extract.php
Better use a whitelist with the keys that you expect:
// no whitelist
$_POST = array('foo' => 'bar', '_GET' => array('foo' => 'bar'), '_SESSION' => array('foo' => 'bar'));
foreach ($_POST as $key => $value) {
$$key = $value;
}
echo $foo;
print_r($_GET);
print_r($_SESSION);
// whitelist
$whitelist = array('foo');
$_POST = array('foo' => 'bar', '_GET' => array('foo' => 'bar'), '_SESSION' => array('foo' => 'bar'));
foreach ($_POST as $key => $value) {
if (in_array($key, $whitelist)) {
$$key = $value;
}
}
echo $foo;
print_r($_GET);
print_r($_SESSION);

Related

How to assign values & keys to a string from an associative array (foreach) [duplicate]

Is there a safe way to auto assign the keys in a posted array? Below are two examples of wrong ways...
foreach( $_POST as $key => $value ) {
$$key = $value;
}
or
extract($_POST)
Is there a better way, or is it best to code:
$foo = $_POST('foo');
$bar = $_POST('bar');
....
for all 50 inputs on my form?
(the posted info will be inserted into a database).
One more cautious way of extracting all input fields at once is:
extract( $_POST, EXTR_OVERWRITE, "form_" );
This way all your input variables will be called $form_foo and $form_bar at least. Avoid doing that in the global scope - not because global is evil, but because nobody ever cleans up there.
However, since mostly you do that in a localized scope, you can as well apply htmlentities if for example you need all fields just for output:
extract(array_map("htmlspecialchars", $_POST), EXTR_OVERWRITE, "form_");
There is not a single reason to do it.
To handle user inputs an array is 100 times better than separate variables
I like an approach where you let dynamic getters and setters in a class do all the work for you. Here's how I would code it.
First, create a bass class to hold data:
class FormParameterHandler {
protected $parameters;
public function __construct($associative_array) {
$this->parameters = array();
foreach($associative_array as $key => $value) {
$this->{$key} = $value;
}
}
public function __get($key) {
$value = null;
if(method_exists($this, "get_$key")) {
$value = $this->{"get_$key"}();
} else {
$value = $this->parameters[$key];
}
return $value;
}
public function __set($key, $value) {
if(method_exists($this, "set_$key")) {
$this->{"set_$key"}($value);
} else {
$this->parameters[$key] = $value;
}
}
}
Next, create a specific class to use for some specific form where there is something special to validate. Use your freedom as a programmer here to implement it any way you want to. And remember, since we're using reflection to look for setter methods, we can write specific setter methods for known problem areas, like e.g. to check for equal passwords in a "register user" form:
class RegisterFormParameterHandler extends FormParameterHandler {
private $passwords_are_equal = null;
public function __construct($register_form_parameters) {
parent::__construct($register_form_parameters);
}
public function has_equal_passwords() {
return $this->passwords_are_equal;
}
public function set_password($password) {
$this->parameters['password'] = $password;
$this->compare_passwords();
}
public function set_password_repeat($password_repeat) {
$this->parameters['password_repeat'] = $password_repeat;
$this->compare_passwords();
}
private function compare_passwords() {
if(isset($this->parameters['password']) && isset($this->parameters['password_repeat'])) {
$this->passwords_are_equal = ($this->parameters['password'] === $this->parameters['password_repeat']);
}
}
}
Finally, use the derived class in a "register user" flow, to easily find out if the two entered passwords match:
$registerFormParameterHandler = new RegisterFormParameterHandler($_POST);
if($registerFormParameterHandler->has_equal_passwords()) {
print "are equal";
//register user
} else {
print "are not equal";
}
You can test this by creating an HTML form that has one input field with the name "password", and another input field with the name "password_repeat".
To access any of the form data, use your form data object variable name, followed by the access operator "dash larger than" -> , followed by the name of the parameter. In the example above, if there was an input field named "user_name", it would be accessed through a call to
$registerFormParameterHandler->user_name
Rr, if you have defined the name of the field you want to get in some other variable, use reflection:
$registerFormParameterHandler->{$settings['form_data_user_name']}
Have fun! :)
A safe way to extract variables into the local scope is not to. You're injecting variables into your local scope, which is a problem however you do it. Even if you limit the variables to only a few select ones that won't clash with other variable names in the scope now, if you start adding elements to your form you may be in trouble later.
Arrays are specifically for holding an unlimited amount of named values without crowding the variable namespace. Use them! You may have to type a little more, but that's par for the course.
While it is best to refer to them with $_POST['variablename'], it is possible to expand only the variables you are expecting.
$expected = array('name', 'telephone', /* etc */);
foreach ($_POST as $key => $value) {
if (!in_array($key, $expected)) {
continue;
}
${$key} = $value;
}
Or, I prefer this:
foreach ($_POST as $key => $value) {
switch ($key) {
case 'name':
case 'telephone':
/* etc. */
${$key} = $value;
break;
default:
break;
}
}
The answer to your question depends on the computer, language, and security knowledge of the programmer. The opening sequence of processing $_POST is kind of like the opening move in a game of chess. Many use foreach loops without realizing that foreach will make a copy of the contents of $_POST the way you have it used (Programming PHP: Chapter 5, p.128-129). Wouldn't it be funny if you caused a buffer overflow simply by using foreach!
One commenter implied that everything should just be worked with inside of the $_POST superglobal. There are some merits to this... However, forgetting cache memory for a moment, access to array values is slower than direct access to a variable.
Since you have fifty (50) controls to validate (with potentially large contents), I might not want to take that array access performance hit more than 50 times (the original access hits). Moreover, if you are concerned about writing secure input validation routines, keeping your dirty laundry (non-validated input) separate from your clean (validated input) laundry is a good idea. That said, you may need a clean array anyway (hence the $_POST advocate's response), but at least you are reducing risk in the process by keeping the hopefully good separate from the potentially bad.
Is there a safe way to auto assign the keys in a posted array?
I might start like this:
Function library for this example.
function all_controls_submitted($controls) {
$postKeys = array_keys($_POST);
foreach($controls as $key) {
if(! array_key_exists($key, $postKeys)) {
return false;
}
}
return true;
}
function all_controls_set($controls) {
foreach($controls as $key) {
if(! isset($_POST[$key])) {
return false;
}
}
return true;
}
if(is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER[REQUEST_URI]) && $_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/contact.php') {
$newForm = true;
} elseif (is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/contact.php') {
$newForm = false;
$controlNames = array('firstName', 'lastName', 'e-mail', 'company', 'subject', 'message', 'captcha');
define('NUM_CONTROLS_EXPECTED', count($controlNames)); //Seven (7)
if(is_array($_POST) && count($_POST) === NUM_CONTROLS_EXPECTED && all_controls_submitted($controlNames) && all_controls_set($controlNames)) {
//Begin input validation
}
} else {
header('location: http://www.nsa.gov');
}
Notice that I prep things with the $controlNames array, therefore I do not have to ask $_POST for the keys. After all, I should know them! :-) The two user-defined functions, all_controls_submitted() and all_controls_set() are two fundamental questions that should be asked before trying to use any values in $_POST (I say, anyway). Admittedly, I do use $_POST in all_controls_submitted(), but only to obtain the names of the controls submitted, not the values.
$postKeys = array_keys($_POST);
However, who is to say that the control names themselves could not be poisonous and in need of input validation?!! Same goes for values in $_SERVER. See, a chess game.
Is there a safe way to auto assign the keys in a posted array? I cannot tell you for certain, but perhaps something like the code above could help? You'd have the keys, at least.
Programming PHP: Chapter 5, p.125
Try this inbuilt method/function filter_input
Reference URL: php.net function filter input

Naming variables automatically from $_POST array.

My PHP script processes some POST variables.
Instead of manually naming each variable
$name = $_POST['name'];
$email = $_POST['account'];
I'd like my script to grab all the variable names from the $_POST array and automatically create variables with those names, e.g. (not code, just illustrating the principle)
foreach ($_POST as $name => $value) {
$[$name] = $value;
}
Is something like that possible?
You can use the extract function for this. But there is a risk, because you cannot know what data is posted, and it will create or overwrite variables in the scope in which you call it, possibly leading to unexpected behaviour.
You can partially counter this, using one of the flags for extract, for instance:
extract($_POST, EXTR_SKIP);
Anyway, make sure to read the two warnings (red block) on the documentation page of this function. And of course, the same warning applies when you do this using your own foreach loop, so answers suggesting that are no more secure.
There is extract function in php:
extract($_POST);
This is a very bad idea because it allows a user to create any variable in your PHP script (within the scope that it this code is used). Take for example if you have a $debugging flag:
$debugging = false;
foreach ($_POST as $name => $value) {
$$name = $value;
}
// some time later, we do a query and output the SQL if debugging
if($debugging){
echo $sql;
}
What if a malicious user submitted an input called debugging with a value of 1? Your debugging flag would be changed and the user could see sensitive debug data.
Try this (which is a bad practice):
foreach ($_POST as $name => $value) {
$$name = $value;
}
You can do this with variable variables as follows:
foreach ($_POST as $name => $value) {
$$name = $value;
}
You can also use the following format if you want to muck about with the variable names some more:
foreach ($_POST as $name => $value) {
${$name.'_1'} = $value;
}
There are comments here saying don't use variable variables - mainly because they are hard as heck to troubleshoot, make it damn hard for others to read your code and will (for the most part) create more headaches than they solve.

Process form fields with foreach loop on posting form?

I'm posting 20 results from a form, to be used on the next page, is there a quick foreach way of posting:
$UserID1 = $_POST['UserID1'];
Where the ID changes each time rather than listing it 20 odd times? I thought a foreach statement, but i'm not too sure where to begin with it?
You can use extract():
extract($_POST);
echo $UserID1; // This now works
This is not considered a good programming practice but will do what you want.
The other answers will do the job just fine, however they introduce security issues.
Code such as:
foreach($_POST as $key => $value){
$$key = $value; //Assign the value to a variable named after $key
}
Allows me to post any form field I wish to your page and alter any variables value, for example, say you have code something like this elsewhere:
if ($validation) { // Do something }
Then I am able to post a form field named validation with a value of 1 and manipulate your code as that would assign a variable called $validation to a value of my choosing.
Using that code to assign variables from all your $_POST values automatically, is the same as using register_globals which was deprecated due to it's blatent security issues. See: http://www.php.net/manual/en/security.globals.php
If you must do this, you must check that the posted values are something that you expect. For example, check for the existance of UserID. This will only allow the creation of variables if they begin with UserID:
foreach($_POST as $key => $value) {
if (substr($key, 0, 6) == 'UserID') {
$$key = $value; //Assign the value to a variable named after $key
}
}
This will check for $_POST['UserID1'] and create $UserID1 and $_POST['UserID2'] and create $UserID2...
foreach($_POST as $key => $value){
$$key = $value; //Assign the value to a variable named after $key
}

PHP - Convert all POST data into SESSION variables

There's got to be a much more elegant way of doing this.
How do I convert all non-empty post data to session variables, without specifying each one line by line? Basically, I want to perform the function below for all instances of X that exist in the POST array.
if (!empty($_POST['X'])) $_SESSION['X']=$_POST['X'];
I was going to do it one by one, but then I figured there must be a much more elegant solution
I would specify a dictionary of POST names that are acceptable.
$accepted = array('foo', 'bar', 'baz');
foreach ( $_POST as $foo=>$bar ) {
if ( in_array( $foo, $accepted ) && !empty($bar) ) {
$_SESSION[$foo] = $bar;
}
}
Or something to that effect. I would not use empty because it treats 0 as empty.
The syntax error, is just because there is a bracket still open. it should be
$vars = array('name', 'age', 'location');
foreach ($vars as $v) {
if (isset($_POST[$v])) {
$_SESSION[$v] = $_POST[$v];
}
}
It should work like that. If you use
if ($_POST[$v])...
it strips down empty data.
Here you go,
if(isset($_POST) {
foreach ($_POST as $key => $val) {
if($val != "Submit")
$_SESSION["$key"] = $val;
}
}
Well the first thing I would suggest is you don't do this. It's a huge potential security hole. Let's say you rely on a session variable of username and/or usertype (very common). Someone can just post over those details. You should be taking a white list approach by only copying approved values from $_POST to $_SESSION ie:
$vars = array('name', 'age', 'location');
foreach ($vars as $v) {
if (isset($_POST[$v]) {
$_SESSION[$v] = $_POST[$v];
}
}
How you define "empty" determines what kind of check you do. The above code uses isset(). You could also do if ($_POST[$v]) ... if you don't want to write empty strings or the number 0.
This lead me to this bit, which is a simplified version of #meder's answer:
<?php
$accepted = array('foo', 'bar', 'baz');
foreach ( $accepted as $name ) {
if ( isset( $_POST[$name] ) ) {
$_SESSION[$name] = $_POST[$name];
}
}
You might also substitute !empty() for isset() above, though be aware of how much farther-reaching empty() is, as #meder pointed out.
$_SESSION["data"] = $POST;
$var = $_SESSION["data"];
Then just use $var as a post variable. For example:
echo $var["email"];
instead of
echo $_POST["email"];
Cheers

Will this foreach copy the values in memory?

Example:
foreach ($veryFatArray as $key => $value) {
Will the foreach assign the value behind the $key by reference to $value, or will $value be a copy of what's stored in the array? And if yes, how could I get an reference only? The array values store pretty big amounts of data so copying them is not really good.
Don't try and second-guess the interpreter is the moral of the story here. $value is actually a copy but an efficient copy. PHP uses pass-by-value (for non-objects) but also uses copy-on-write. What this means is that if you write:
foreach ($bigitems as $bigitem) {
echo $bigitem; // uses original
$bigitem = 'foo'; // item copied and assigned 'foo'
echo $bigitem; // uses copy
}
Just declare your intent: that you want to use the array values. Let PHP sort it out after that. Basically only use references in the loop like this if you intend to change the array. Otherwise you're sending the wrong message to someone else who reads your code.
Section 1 of Copy-on-Write in the PHP Language should tell you everything you ever wanted to know about PHP copy-on-write.
$value will be a copy, but (please someone correct me if I'm wrong here), PHP is actually very smart about pass-by-value type things. It will actually do a pass-by-reference and only copy if you modify the variable. For example:
function foo ($bar) {
echo $bar['x'];
// internally, $bar is a reference to $baz. (virtually) no extra memory used
$bar['y'] = 'Y';
// only now has the array been copied in memory
}
$baz = array('x' => '1', 'y' => '2');
foo($baz);
In your foreach, if you want to modify the original array, you could do this:
// PHP 4
foreach (array_keys($veryFatArray) as $key) {
$value =& $veryFatArray[$key];
// ...
}
// PHP 5
foreach ($veryFatArray as $key => &$value) { }
If you are only reading from $value and not writing to it, then it shouldn't be a problem.

Categories