If have a problem on my site that users can post empty messages if they use space.
Code:
if (isset($_POST['submit'])) {
// check for empty fields
if (empty($_POST['headline']) || empty($_POST['text']) ||
empty($_POST['forum_id'])) {
header("Refresh: 2; url=/add-thread");
die('You must fill out every field.');
}
// No errors? Save.
else {
$headline = mysql_real_escape_string($_POST['headline']);
$text = mysql_real_escape_string($_POST['text']);
mysql_query("INSERT INTO threads (headline, text, date, forum_id, user_id)
VALUES ('$headline', '$text', NOW(), '$_POST[forum_id]', '$user[id]')");
header("Location: /thread/".mysql_insert_id()."");
}
}
How can I fix this?
trim() the text inputs. You can do that easily like this:
// get input vars and trim space
$callback = array('filter' => FILTER_CALLBACK, 'options' => 'trim');
$fields = filter_input_array(INPUT_POST, array(
'headline' => $callback,
'text' => $callback,
'forum_id' => $callback,
));
// check for empty fields by counting how many are set
if ( count($fields) != count(array_filter($fields)) ) {
// something was unset
}
The empty function checks for variables that meet a set criteria, from the manual
Returns FALSE if var has a non-empty and non-zero value.
The following things are considered to be empty:
"" (an empty string)
0 (0 as an integer)
"0" (0 as a string)
NULL
FALSE
array() (an empty array)
var $var; (a variable declared, but without a value in a class)
Your $_POST fields actually contain something like this
" ";
This isn't and empty string, but a string that's filled with whitespace characters.
Before using empty(), trim() the white-space from your POSTed values
$trimmed_post = array();
foreach($_POST as $key=>$value){
$trimmed_post[$key] = $value;
}
if(!empty($trimmed_post['headline'])){
//...
}
You don't need to put the new values into a new array, but I'm not a big fan of changing what's in the auto-generated superglobals.
One final note, you can't do something like this
if(!empty(trim($_POST['headline']))){
//...
}
because the empty function expects to be passed an actual variable. You could do something like this instead
if('' != trim($_POST['headline'])){
//...
}
This is probably the best approach to take. You reduce the number of functions that you need to call, users can post entries with a value of 0, and the code is more explicit about what it does. Another form you'll see is
if(trim($_POST['headline'])){
}
This works because PHP evaluates an empty string ('') as false, and a non empty string as true. I tend to avoid this form because I've found a lot of PHP bugs crop up around misunderstandings on how the equality operators get boolean values out of certain types. Being explicit helps reduce occurrences of these types of bugs.
Just a quick note: You're injecting the value of $_POST['forum_id'] into the SQL; that's not a good idea, since a user could manipulate that value as desired, even if it is coming from a hidden field. It would be wise to escape the value, or at least pass it through intval() and ensure it's an integer (assuming integral post identifiers).
I agree that trimming is the way to go. Here's a much easier way to go about it:
$_POST = array_map('trim', $_POST);
Try
if (!empty($_POST['headline']) && !empty($_POST['text']) &&
!empty($_POST['forum_id']))
For the logic.
You'd have to switch it around though.
UPDATE to clarify:
if (isset($_POST['submit']) && !empty($_POST['headline']) &&
!empty($_POST['text']) && !empty($_POST['forum_id'])) {
$headline = mysql_real_escape_string($_POST['headline']);
$text = mysql_real_escape_string($_POST['text']);
mysql_query("INSERT INTO threads (headline, text, date, forum_id, user_id)
VALUES ('$headline', '$text', NOW(), '$_POST[forum_id]', '$user[id]')");
header("Location: /thread/".mysql_insert_id()."");
}
else
{
header("Refresh: 2; url=/add-thread");
die('You must fill out every field.');
}
}
I trim every $_GET & $_POST variable as soon as the app starts. try something like this:
function trimArray(&$array) {
if (empty($array)) {
return;
}
foreach ($array as $k => $v) {
if (! is_array($v)) {
$array[$k] = trim($v);
} else {
trimArray($v);
}
}
}
if (! empty($_GET)) {
trimArray($_GET);
}
if (! empty($_POST)) {
trimArray($_POST);
}
Right after checking for a submission:
foreach ( $_POST as $key => &$value ) $value = trim($value);
edit in response to asker's comment:
Odd that it didn't work as above. Here was my exercise to confirm it would.
tbramble#wayfarer:~$ php -a
Interactive shell
php > $arr = array('one',' two', ' three ');
php > print_r($arr);
Array
(
[0] => one
[1] => two
[2] => three
)
php > foreach ( $arr as $key => &$value ) $value = trim($value);
php > print_r($arr);
Array
(
[0] => one
[1] => two
[2] => three
)
Must have something to do with working on a superglobal instead of a normal array.
Related
Im trying to load content via request and include pages, problem is that even if I put localhost/?login or ?logout , it doesn't work, shows that there is no request with name $k and priting $container .= twice (??). I never had a problems with array but I didn't try it as a website request.
$actions = array(
"login" => "source/login.php",
"logout" => "source/logout.php"
);
foreach($actions as $k => $v){
if (!empty($_REQUEST[$k])) {
include($v);
break;
}
else
{
$container .= "?";
}
}
That your punishment for using empty. :-) Lots of values are considered empty by PHP.
Since login is just a key, but you didn't specify a value, it is considered 'empty' even though it exists in the array.
Use array_key_exists for a stricter check.
foreach($actions as $k => $v){
if (array_key_exists($k, $_REQUEST)) {
include($v);
break;
}
else
{
$container .= "?";
}
}
Many people use isset for this, which is slightly better than empty, but I prefer array_key_exists since it does exactly what you expect it to do and nothing else.
The following things are considered to be empty according to the docs:
"" (an empty string)
0 (0 as an integer)
0.0 (0 as a float)
"0" (0 as a string)
NULL
FALSE
array() (an empty array)
$var; (a variable declared, but without a value)
So you can see why empty can give you a lot of false positives when testing for it. :-)
another way you could do it was have your links be setup like this:
foo.com/bar.php?action=login
then have your php code do
$action = $_REQUEST['action'];
if ($action == "" || array_key_exists($action,$actions) )
{
//set to default value...
}
include($actions[$action]);
would this achieve your goal?
Currently I am checking if the $_FILES array is empty to unset like so:
if ($var === 'AP' && empty($_FILES['field_1']['name'])) {
unset($_FILES['field_1']);
}
if ($var === 'AP' && empty($_FILES['field_2']['name'])) {
unset($_FILES['field_2']);
}
if ($var === 'AP' && empty($_FILES['field_1']['name']) && empty($_FILES['field_2']['name'])) {
unset($_FILES['field_1']);
unset($_FILES['field_2']);
}
This is for image uploads, it works when there are a small amount of images that can be uploaded. Now I'm going to have 15 upload fields with them all being optional. Is there a better way to unset those arrays that are empty without doing conditionals like above for each and every possible combination?
Instead of unsetting the ones that are empty, why not set the ones that are set?
(This is good practice, since you may want to perform some validation action on the ones that are set)
$clean = array();
foreach($_FILE as $fileKey => $fileValue){
if($fileValue) {
$clean[$fileKey] = $fileValue;
}
// if you really want to unset it :
else{
unset($_FILE[$fileKey]);
}
}
for ($x = 0; $x < 15; $x++) {
if ($var === 'AP' && empty($_FILES[$x]['name'])) {
unset($_FILES[$x]);
}
}
Should do the trick. (Could use a check to make sure elements are set and some cleanup, but you get the idea)
It will actually be $_FILES['name'][0] not $_FILES[0]['name'] ,etc so just remove the empties:
$_FILES['name'] = array_filter($_FILES['name']);
Then just use $_FILES['name'] as the array to loop over and the keys will still be the same as the other arrays.
You can use array_filter to remove empty elements:
$emptyRemoved = array_filter($linksArray);
If you have (int) 0 in your array, you may use the following:
$emptyRemoved = remove_empty($linksArray);
function remove_empty($array) {
return array_filter($array, '_remove_empty_internal');
}
function _remove_empty_internal($value) {
return !empty($value) || $value === 0;
}
EDIT: Maybe your elements are not empty per say but contain one or more spaces... You can use the following before using array_filter
$trimmedArray = array_map('trim', $linksArray);
This question already has answers here:
Sanitizing user's data in GET by PHP [duplicate]
(5 answers)
What are the best PHP input sanitizing functions? [duplicate]
(14 answers)
Closed 7 months ago.
I have a form with a lot of variables which is then sending an email, rather than sanitizing each $_POST value with filter_var($_POST['var'], FILTER_SANITIZE_STRING); I was after a more simple piece of code. I came up with the below, which seems to work as I believe the default action is FILTER_SANITIZE_STRING, but I was just wondering what peoples opinions are, and if this is not good practice, perhaps you could tell me why? The $_POST values are then individually embedded into new variables, so I would only be using array_map just at the start to sanitize everything...
$_POST = array_map('filter_var', $_POST);
Thank you for your replies, to give you a little more information, basically:
I have 20-30 input fields in a form which are being captured,
the data is then displayed to the user to check their input,
variables are then sanitized,
the user is then sent an email
and then finally the details are entered into a db.
currently I am sanitizing using the above array_map function, as well as FILTER_SANITIZE_EMAIL on the email address before sending an email and then escaping the input using mysql_real_escape_string() before the insert into the db. Without getting into prepared statements etc.. do you think I should be doing anything additionally? thanks again!
Just use filter_input_array() from the filter extension.
/* prevent XSS. */
$_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
$_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
This will sanitize your $_GET and $_POST.
Depends what its being used for.
If you are inserting it into the database then mysql_real_escape_string() for quoted strings and type casting for numbers would be the way to go - well ideally prepared statements, but thats an entirely different matter.
If you plan on outputting the data onto the webpage then I would recommend something like htmlspecialchars()
If you plan on using the user input as a shell argument, then you would use escapeshellarg()
Moving onto your question about sending emails. Well, the following should suffice:
filter_var($_POST['message'], FILTER_SANITIZE_STRING);
All this does is basically strip tags and encode special characters.
You can use strip_tags() with array_map()
<?php
$a = array(
'title' => 'Title',
'data' => array(
'hdr' => 'Header',
'bdy' => 'Body'
),
'foo' => array(1, 23, 65)
);
$b = array_map("strip_tags", $a);
print_r($b);
?>
Update for 2D array:
function array_map_r( $func, $arr )
{
$newArr = array();
foreach( $arr as $key => $value )
{
$newArr[ $key ] = ( is_array( $value ) ? array_map_r( $func, $value ) : ( is_array($func) ? call_user_func_array($func, $value) : $func( $value ) ) );
}
return $newArr;
}
Usage:
$a = array(
'title' => 'Title',
'data' => array(
'hdr' => 'Header',
'bdy' => 'Body'
),
'foo' => array(1, 23, 65)
);
$ar =array_map_r('strip_tags', $a);
print_r($ar);
Note I found this just by searching the comments for Dimension
There is no correct way to do blanket sanitation. What sanitation method you need depends on what is done to the data.
Sanitize the data directly before it is used.
function strip($string, $allowed_tags = NULL)
{
if (is_array($string))
{
foreach ($string as $k => $v)
{
$string[$k] = strip($v, $allowed_tags);
}
return $string;
}
return strip_tags($string, $allowed_tags);
}
Just an example of a recursive function, for stripping tags in this case.
$arr = strip($arr);
This is what I use in all my projects:
function util_array_trim(array &$array, $filter = false)
{
array_walk_recursive($array, function (&$value) use ($filter) {
$value = trim($value);
if ($filter) {
$value = filter_var($value, FILTER_SANITIZE_STRING);
}
});
return $array;
}
It allows to trim and sanitize a nested array of posted data
This looks ok, but please comment if it can be improved or has any misgivings:
$_GET =filter_var_array($_GET);
$_POST=filter_var_array($_POST);
To apply specific filters on multiple fields, use a switch statement.
$post = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
foreach($post as $k => $v) {
switch ($k) {
case 'int_1':
case 'int_2':
$post[$k] = filter_var($v, FILTER_SANITIZE_NUMBER_INT) * 1;
break;
case 'float_1':
case 'float_2':
$post[$k] = filter_var($v, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) * 1;
break;
default:
break;
}
}
Note: My IDE (NetBeans) warns about using global $_POST anywhere as a security violation, so I've just gotten into the habit of using a local $post variable instead. If you choose not to do the blanket string sanitation first, FILTER_SANITIZE_STRING could be used for the default: case.
Let's say we want to sanitize the $_POST array:
foreach($_POST as $k=>$v) {$_POST[$k] = htmlspecialchars($v);}
This simple. Isn't it?
A couple questions here..
What is the following syntax?
What do all the pieces mean?
(( ! is_array($data)) ? $data : '')
How is it used in the function at the end?
function form_input($data = '', $value = '', $extra = '')
{
$defaults = array('type' => 'text', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value);
return "<input "._parse_form_attributes($data, $defaults).$extra." />";
}
Thankyou. Blake
Ok let me try as far as I understand you question.
What is the following syntax?
It is a function definition.
What do all the pieces mean?
This is the ternary operator. It means: If $data is not an array (!is_array($data)), return $data (? $data) otherwise return an empty string (: '')
It is a shorthand for if-else.
How is it used in the function at the end?
Not sure what you mean here. A function _parse_form_attributes($data, $defaults) is called witch seems to return a string.
If it in your question refers to $defaults, then it is just an array that gets build and that contains the following values:
Array (
'type' => 'text',
'name' => $data, // or empty string if $data is an array,
'value' => $value
);
It is used to build an input element, that will look like this:
<input type="text"
name="(value of $data or empty)"
value="(value of $value)"
(value of $extra)
/>
Ok this seems to generate the HTML for a form input with ome useful default values.... To answer your Qs in order:
It's a function that takes optional parameters. You could call eg form_input('SomeData', 'SomeValue');
It creates the HTML for the element and includes whatever information you give it, using defaults if no value is provided
A difficult one to answer... In short you're defining a new function. If you provide parameters, they will be used to create a form element. If you omit them, defaults will be used - eg type will default to text for output like <input type="text" [blah]>
I'm not quite sure what you mean here - Append as key-value pairs eg <input type="text" key="Value" [blah]> ? If this is the case, I would personally lose the $extra variable and have an array of key->value pairs instead. Failing that, you need to et $extra as appropriate eg key1="value1" key2="value2"
It's an inline expression of:
function form_input($data = '', $value = '', $extra = '')
{
if (is_string($data)) { // or better: is_scalar()
// leave as-is
}
else {
$data = '';
}
...
(Slightly variated.)
It basically ensures that the $data parameter is never an array. Maybe it was allowed to be an array in previous versions of the function, and this is safety code to ensure correct operation.
Note that type enforcement is another approach (often overkill, but sometimes advisable):
function xy($data, $value, $z) {
assert( ! is_array($data) );
assert( is_string($value) );
1) It returns an input field;
2) Pardon?
3) Function takes three values. $value is the default value of the input field (the one you find in the textbox); $data will be the name of the form if it isn't an array, otherwise the name will be blank; $extra is a string containing other stuff that will go in the form. Please refer to the function _parse_form_attributes;
4) link
By the way, I sincerely hope that _parse_form_attributes() does something amazing, since that looks like an incredibly verbose function and codeigniter has some handy built-in form generation functions.
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