My boss set up a script to process forms through PHP and email the results. We are required to use this processor. The problem with the processor is this line of code:
foreach ($_POST as $k => $v){$$k = strip_tags($v);}
This would be fine if all values sent were just strings, but I am trying to process some checkboxes which are passed as arrays. From what I understand, the strip_tags function only works with strings. It processes everything and sends the results via email as it should, but it throws a notice every time it tries to process a series of checkboxes.
Notice: array to string conversion...
The process still works, I just get ugly notices all over the place. In order to temporarily fix the problem, I removed the strip_tags function, resulting in this:
foreach ($_POST as $k => $v){$$k = $v;}
Everything now functions properly and I get no warnings, errors or notices. However, after pointing this out to my boss he wants me to revert back to the original code and then give each checkbox its own unique name, instead of giving them all the same name with different values. I could do that, but I know that is not the proper way to process a series of checkboxes. Plus it creates all sorts of headaches. My boss simply does not understand how to work with arrays, so he comes up with stupid work-arounds like this every time he encounters one. He also claims that this is some sort of spam protection to stop people from adding recipients to our forms. I may not be an expert in PHP, but I'm pretty sure that statement is false.
So what can I do to fix this issue? I know I should be converting the checkbox arrays to strings first, then use the strip_tags function on the resulting strings, but I am still fairly new to PHP and don't entirely understand what that line of code is doing to begin with. Can anybody help at least point me in the right direction?
Point out to your boss that:
<input type="checkbox" name="valid_recipient[]" value="1" /> fred#example.com
<input type="checkbox" name="valid_recipient[]" value="x" /> nasty#badperson.com
and
<input type="checkbox" name="valid_recipient1" value="1" /> fred#example.com
<input type="checkbox" name="valid_recipient2" value="x" /> nasty#badperson.com
are the same thing, whether they get passed as an array of checkbox values, or individual checkbox/value pairs. Either way, Mr. Nasty just injected something into your checkbox list.
As well, what's a malicious user from setting
<input type="hidden" name="_POST" value="haha I wiped your post array!" />
into the form. Your PHB's handy dandy "makes us completely secure" processor has just happily nuked the $_POST array, all while being "completely secure"
Passing checkboxes as an array is kush:
<input type="checkbox" name="myarray[key1]" value="hello world" />
<input type="checkbox" name="myarray[key2]" value="well hello again" />
Will create a resultant $_POST like:
Array
(
[myarray] => Array
(
[key1] => hello world
[key2] => well hello again
)
)
While this is awesome it also means that your bosses code is doubly insecure:
First, not having the strip_tags in there makes you vulnerable to XSS attacks.
Second, naively trusting $_POST variable names and exporting them into the global namespace is a recipe for disaster. (There's a reason why register_globals is a thing of the past).
For instance, let's say you track the username of who is logged in using a simple $_SESSION variable. Something like this:
if ($_SESSION['logged_in_user'] == 'admin') {
// do administrator things
}
Well, by accepting and exporting $_POST variables and attacker could modify an HTML form element like this:
<input type="checkbox" name="myarray[key1]" value="hello world" />
And twiddle it into something like this (using Firebug or Chrome):
<input type="checkbox" name="_SESSION[logged_in_user]" value="admin" />
Tad-ah! Any anonymous user can gain administrator access to the web site.
Here is a simple script for your consideration:
<pre>
<?php
session_start();
$_SESSION['logged_in_user'] = 'pygorex1';
print_r($_SESSION);
foreach ($_POST as $k => $v) {
$$k = $v;
}
?>
</pre>
<form method="post">
<input type="checkbox" name="myarray[key1]" value="hello world" />
<input type="checkbox" name="_SESSION[logged_in_user]" value="admin" />
<input type="submit" value="go go gadget" />
</form>
<pre>
<?php
print_r($_SESSION);
print_r($myarray);
session_write_close();
if ($_SESSION['logged_in_user'] == 'admin') {
echo("OWNED\n");
}
?>
</pre>
Related
Is there a way to allow user to edit a php code securely, for example this is a basic php code to echo
Hello World! onto the page.
The idea is not to allow full coding changes just things like the array or they could edit a date in mktime things like that. I thought there maybe a way to echo form input fields into a php code which will then display the results.
How could i go about allowing a user to edit the code changing (Hello World!) to something else and then click submit to display there edit.
<?php
echo "Hello World!";
?>
or another example would be how can the user edit the words in the array
<?php
$words = array("the ", "quick ", "brown ", "fox ",
"jumped ", "over ", "the ", "lazy ", "dog ");
shuffle($words);
foreach ($words as $word) {
echo $word;
};
unset($word);
?>
I presume that i would have to create a form which gets the php code and somehow get it to display the edited results?
<form name="form" method ="get" action="a.php">
<input type="text" id="edit" name="edit" size="30" />
<input type="submit" value="Submit" >
</form>
For anyone that is viewing this and would like to know what you can create using a form and php see here Form that edits php script
What you are trying to accomplish is what variables are for.
Taking this example:
echo "Hello World!";
You could change that to
echo $_POST["data"];
and in your html
<form type='post'>
<input type='text' name='data'/>
<input type='submit'/>
</form>
See it in action
Eval should be avoided at all costs, there is a very narrow set of problems where using eval is a sane solution.
You want people to run arbitrary PHP code, but not all arbitrary PHP code. Tough thing to get right.
First off do not just eval() form data. Only bad* can come of this.
<form method="POST">
<textarea name="php"></textarea>
<button type="submit">Run</button>
</form>
<pre>
<?= eval($_POST['php']) ?>
</pre>
One option that comes to mind is to use https://github.com/nikic/PHP-Parser.
Basically, the parser does nothing more than turn some PHP code into an abstract syntax tree. ("nothing more" is kind of sarcastic here as PHP has a ... uhm, let's just say "not nice" ... grammar, which makes parsing PHP very hard.)
You can then walk the AST and remove suspect expressions, reconstitute the tree to code, and then call eval() on it.
Outside of that, configuring a sandbox environment would be critical here, as nothing is foolproof. That way, when someone inevitably bricks the box, you can recover it.
php.ini configuration changes can make for a "safer" environment to execute arbitrary code by imposing restrictions. disable_functions and disable_classes can help limit the possible abuse. Setting a low memory_limit will prevent help reduce excessive resource slurping.
* Unless this is a social experiment to see how long it takes for someone to turn your machine into pudding
in THEORY you can do something like this, but PLEASE PLEASE PLEASE don't do it because it is extremely UNSECURE
<?php
if (isset($_REQUEST['do_eval'])){
eval($_REQUEST['to_eval']);
}
?>
<form action="eval.php">
<textarea name="to_eval" rows="20" cols="80"><?php if (isset($_REQUEST['eval'])) print($_REQUEST['eval']); ?></textarea>
<br />
<input type="submit" name="do_eval" value="Submit" />
</form>
if I get you right, then eval function is what you need (http://php.net/manual/en/function.eval.php)
Evaluates the given code as PHP.
Although it is very dangerous as a user can execute a destructive code or output some private data.
<?
if(isset($_POST['submit'])
{
eval($_POST['code']);
}
else
{
?>
<form method="POST">
<textarea name="code"></textarea>
<input type="submit" value="Submit"></form>
</form>
<?
}
This sounds extremely dangerous to me; since PHP code runs on the server, you are basically letting anyone and everyone tell your server what code to run, and telling it to run harmful code would be very easy. Unfortunately, I can't think of a trivial way to sanitize this type of input.
Having said that... you can have a form that submits the user's code to a page that can write that code into a .php file on your server, then redirects to the newly created .php file. But, again, I would not advise you to do this sort of thing.
I think I understand what you're trying to accomplish. The actual task I believe will require a large amount of javascript in association with your PHP.
So, let's run it down theoretically.
Let's say this is your start code:
$array = array('one', 'two', 'three');
var_dump($array);
Ok - so now you want to define that the user can modify the array. Your HTML now looks something like that code above - all escaped of course.
However, you put form element around the escaped content, and put each array element as an input field.
So, you'll end up with something like this: (Note this is HTML not PHP)
<form action="self.php">
<div>$array = array(</div>
<span>'<input name="arrayValue[]">, +</span>
<div>);<br>var_dump($array);</div>
<input type="submit" value="Process this code">
</form>
Now, you'll need to write some javascript that watches for the class 'addAnother' to be clicked. If so, it goes up to its parent element and clones it (see - that's the span) and adds it after the parent. This way you'll have another whole line that is that span - with another input.
If you style the inputs to look nice, you can make it look like the user is typing inline.
Once the submit is pressed, the values are sent to the PHP. Then, the PHP will create a new array from all of $_POST['arrayValue'];
Your actual code will do this:
$array = $_POST['arrayValue'];
var_dump($array);
And then, you'll rerender the HTML again.
I know this is all 'theory' - there's a bit more code to actually be written.
I honestly would re-think if you really want to take on this task - this is a LOT of work to do it in an interactive, secure way. Perhaps there are other ways to accomplish your core task. Best of luck!
Which is the most secure way to send an array through POST?
foreach ($id as $array)
{
<input type="hidden" name="prova[]" value="<?php echo $array; ?>"/>
}
<input type="submit" name="submit"/>
or using implode() to create a single variable, pass the variable and then use explode() to get back the values into a new array?
Edit If you are asking about security, see my addendum at the bottom Edit
PHP has a serialize function provided for this specific purpose. Pass it an array, and it will give you a string representation of it. When you want to convert it back to an array, you just use the unserialize function.
$data = array('one'=>1, 'two'=>2, 'three'=>33);
$dataString = serialize($data);
//send elsewhere
$data = unserialize($dataString);
This is often used by lazy coders to save data to a database. Not recommended, but works as a quick/dirty solution.
Addendum
I was under the impression that you were looking for a way to send the data reliably, not "securely". No matter how you pass the data, if it is going through the users system, you cannot trust it at all. Generally, you should store it somewhere on the server & use a credential (cookie, session, password, etc) to look it up.
http://php.net/manual/en/reserved.variables.post.php
The first comment answers this.
<form ....>
<input name="person[0][first_name]" value="john" />
<input name="person[0][last_name]" value="smith" />
...
<input name="person[1][first_name]" value="jane" />
<input name="person[1][last_name]" value="jones" />
</form>
<?php
var_dump($_POST['person']);
array (
0 => array('first_name'=>'john','last_name'=>'smith'),
1 => array('first_name'=>'jane','last_name'=>'jones'),
)
?>
The name tag can work as an array.
You could put it in the session:
session_start();
$_SESSION['array_name'] = $array_name;
Or if you want to send it via a form you can serialize it:
<input type='hidden' name='input_name' value="<?php echo htmlentities(serialize($array_name)); ?>" />
$passed_array = unserialize($_POST['input_name']);
Note that to work with serialized arrays, you need to use POST as the form's transmission method, as GET has a size limit somewhere around 1024 characters.
I'd use sessions wherever possible.
There are two things to consider: users can modify forms, and you need to secure against Cross Site Scripting (XSS).
XSS
XSS is when a user enters HTML into their input. For example, what if a user submitted this value?:
" /><script type="text/javascript" src="http://example.com/malice.js"></script><input value="
This would be written into your form like so:
<input type="hidden" name="prova[]" value="" /><script type="text/javascript" src="http://example.com/malice.js"></script><input value=""/>
The best way to protect against this is to use htmlspecialchars() to secure your input. This encodes characters such as < into <. For example:
<input type="hidden" name="prova[]" value="<?php echo htmlspecialchars($array); ?>"/>
You can read more about XSS here: https://www.owasp.org/index.php/XSS
Form Modification
If I were on your site, I could use Chrome's developer tools or Firebug to modify the HTML of your page. Depending on what your form does, this could be used maliciously.
I could, for example, add extra values to your array, or values that don't belong in the array. If this were a file system manager, then I could add files that don't exist or files that contain sensitive information (e.g.: replace myfile.jpg with ../index.php or ../db-connect.php).
In short, you always need to check your inputs later to make sure that they make sense, and only use safe inputs in forms. A File ID (a number) is safe, because you can check to see if the number exists, then extract the filename from a database (this assumes that your database contains validated input). A File Name isn't safe, for the reasons described above. You must either re-validate the filename or else I could change it to anything.
Why are you sending it through a post if you already have it on the server (PHP) side?
Why not just save the array to s $_SESSION variable so you can use it when the form gets submitted, that might make it more "secure" since then the client cannot change the variables by editing the source.
It all depends on what you really want to do.
I'm working on a script that essentially has tell-a-friend functionality, but I'm trying to figure out the best way to capture and process the data.
For example:
Do I display 5 pairs of fields asking for name and email, each pair being $friend_name1/$friend_email1, $friend_name2/$friend_email2, etc, which allows you to email 5 friends at a time?
Do I display one pair and use JavaScript to allow the user to keep adding more friends using the variable naming convention as #1?
Do I display them as suggested in #1 or #2, but then submit an array e.g. $friend_email[]/$friend_name[], etc.
What's the best way to capture the data?
And then what's the best way to process the data?
If you get an array, such as in #3, do you then loop through each $friend_name $_POST? Do you store it in another array? How do you ensure that the correct name/email combination stays together, if for example, the user didn't add a "name" for the third friend?
How do most people do it so they can remain flexible during the capturing and precise during the processing? I'm really looking for logic here, although sample code is much appreciated.
One of the things I'm tracking is who is referring who. For example, if A refers B and B buys something, A will benefit. So accuracy and security are very important to me.
Thanks,
Ryan
I would recommend the array. You could do javascript or otherwise, but make sure that the processing end can see how many referrals there are. To ensure the correct e-mail and name stay together, try something like
<input type="text" name="friend_name[0]" />
<input type="text" name="friend_email[0]" />
<input type="text" name="friend_name[1]" />
<input type="text" name="friend_email[1]" />
and so on. From there, your PHP script will simply match the array indices in the $_POST var. For instance, if this form was submitted (as above) with some arbitrary data, it would format like this (this is the print_r($_POST); data)
Array
(
[friend_name] => Array
(
[0] => test_name
[1] => test_name2
)
[friend_email] => Array
(
[0] => email#something.com
[1] => email#two.com
)
)
An idea on looping through these (if you implemented javascript with a potential for an "infinite" number of referrals) would be to use the count(); function. Implementation would look something like this:
for($i=0;$i<count($_POST['friend_name']);++$i)
{
if($_POST['friend_email'][$i] == NULL) continue; // Ignore NULL e-mails
// Process data blah
}
Or, if those names where entered out of order, PHP's foreach() feature is very nice and can accomplish the same thing (probably more efficiently for this type of error checking)
foreach($_POST['friend_name'] as $key => $val)
{
if($_POST['friend_email'][$key] == NULL) continue; // Ignore NULL e-mails
// Process $val and $_POST['friend_email'][$key]
}
Hope this helps! Good luck!
Dennis M.
These are UI questions, and the answer is whatever UI you think works best for your site. You might have a design where a fixed number of fields fits better, or you might have a demographic where the "add another field" button is too confusing. Either way the code that processes the form doesn't have to be related to how you present the UI.
Regarding making sure your fields stay associated if you name them as an array, specify the indexes yourself rather than using open brackets, and you won't have that problem.
<input type="text" name="name[0]" value="First Friend's Name" />
<input type="text" name="email[0]" value="First Friend's E-mail" />
<input type="text" name="name[1]" value="Second Friend's Name" />
<input type="text" name="email[1]" value="Second Friend's E-mail" />
Then in your code,
foreach ($_POST['email'] as $index => $value) {
$name = $POST['name'][$index];
$email = $_POST['email'][$index];
}
Within the loop, $name and $email will be from the same set of fields in the form, though they can still be blank.
What would be the best way to intercept multiple fields via PHP? Currently I have a login.php file which is pretty simple:
<form method="POST" action="auth.php">
Code:<br />
<input type="text" name="code" />
<input type="submit" id="submit" value="Submit" />
<br />
Pass:<br />
<input type="text" name="pass" />
<input type="submit" id="submit" value="Submit" />
</form>
Then in the auth.php I get the value via the POST:
$value = $_POST['code'];
The problem with this is that I would have a quite amount of fields, and every field would have a submit button assigned. Then I would need a if condition for every field name avaible, which I don't want to. What would be the best way to handle this?
Thanks
Just use a single submit button. There's no reason to have more than one here.
If you have multiple related fields you can use array naming:
Primary email: <input type="text" name="email[]" >
Additional email: <input type="text" name="email[]">
and access from php using
$emails = $_REQUEST['email'];
However, you should not use arrays like this for unrelated parameters just because you're too lazy to use multiple field names. If you do you're just writing terrible, unmaintainable code.
There's a couple of ways you can simplify this problem.
Here's one approach:
$fields = array('field1','field2','field3','field4','field5'); // Add all your field names to an array
$data = array();
foreach ($fields as $field) {
if (isset($_POST[$field])) {
$data[$field] = $_POST[$field];
// If you wanted it assigned to a local variable instead,
// you could do it like this, although this pattern is
// generally frowned upon:
${$field} = $_POST[$field];
}
}
The danger of assigning it as a local variable is that it can overwrite a variable that already exists. It could potentially overwrite something crucial to the rest of the application. However, because the field names are explicitly defined in the array, you do at least maintain control over this.
You would definitely NOT want to iterate through the $_POST array and assign each array key to a local variable -- doing so would leave you wide open for hackers.
You have a bad problem with HTML.
In HTML you don't need more than one submit button.
A submit does not submit a field, but a whole form.
You don't even need it, as most user-agents will submit a form if you press Enter on a textfield.
Anyway, your fields will have an attribute name, unique within the form, and shall have an id attribute unique within the document.
When they are sent, all data can be accessed in PHP as an array.
The superglobal $_POST in your case, or $_GET if that method was used to submit it.
There, you do your magic.
Assuming there are 5 inputs in web form
<input name='the_same[]' value='different' />
<input name='the_same[]' value='different' />
<input name='the_same[]' value='different' />
<input name='the_same[]' value='different' />
<input name='the_same[]' value='different' />
When server side receive the post data, i use a foreach to accept data, say
$the_same = new array();
foreach($_POST['the_same'] as $data)
$the_same[] = $data;
Will the order of data saved in server side be the same to it in web form? and cross browsers, it could be a criteria all browsers follow.
Well, the W3C recommendation on HTML forms does say:
The control names/values are listed in
the order they appear in the document.
Still, I'd consider it a bit risky to have your app depend critically on that detail.
PHP already handles converting POSTed/GETed variables into arrays when you put [] after the name. Do that instead of getting it wrong yourself.
Better way to do in html:
<input name='the_same[]' value='different' />
Then in server:
$the_same = new array();
foreach($_POST['the_same'] as $data) // or $_GET if you prefer
$the_same[] = $data;
In this way no variable will be overwrite.
if you want to have it in an order, you may use the dynamic variables or simply access the array explicitly
the_same1
the_same2
the_same3
since you know the names anyway, you can access them easily
$the_same = array();
for($i=1; ; $i++){
$tmp =$_REQUEST["the_same".$i]
if( empty($tmp) ){
// no more stuff
break;
}
$the_same[] = $tmp;
}
If you change the name of your input to the_same[] - $_REQUEST['the_same'] will become an array of those values, first to last in element order (all current browsers I believe).
You can also specify a specific order if you need, or even use string keys. For instance, an <input name='the_same[apple][2]'/> would become $_REQUEST['the_same']['apple'][2]
Without using the [] on the input names, PHP will only see the last value. The other values will be 'overwritten' by the later value when the $_REQUEST/$_GET/$_POST arrays are constructed.
An example of using that to your advantage could be with a checkbox, as the HTML checkbox only submits a value when checked, you may want to submit a "not checked" value somtime:
<input type='hidden' name='check' value='not checked' />
<input type='checkbox' name='check' value='checked' />
Most likely yes, but you should not assume this. It depends on your browser how the inputs are being send, and aditionally PHP does not guarantee that a foreach loop iterates in the same order as the elements were added.
It is a bad practise to give your inputs the same name.
You could append an index after each name value (even with javascript if you want), and then read this in PHP to be sure the order is maintained.