45

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!

SirG
  • 459
  • 1
  • 4
  • 4
  • I know that this question is a bit old, but you can use `map_deep( $_POST, 'sanitize_text_field' );` to recursively sanitize the entire object – JamesS Jan 04 '23 at 13:21

9 Answers9

69

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Alfred
  • 60,935
  • 33
  • 147
  • 186
  • 5
    `FILTER_SANITIZE_STRING` has been deprecated in PHP 8.1 `FILTER_SANITIZE_FULL_SPECIAL_CHARS` is the closest replacement that's still valid. – Aleksandar Apr 06 '22 at 03:54
27

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.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
Russell Dias
  • 70,980
  • 5
  • 54
  • 71
  • 3
    If you are using PHP 7 or higher, the `mysql_real_escape_string()` function has been removed. Use PDO instead. (Posting this for new visitors to the question.) – Kenny Johnson Jun 30 '16 at 03:05
9

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

Dharman
  • 30,962
  • 25
  • 85
  • 135
kieran
  • 2,304
  • 4
  • 28
  • 44
  • But I get `Warning: strip_tags() expects parameter 1 to be string, array given`. I think it doesn't work for 2nd level+ arrays... – Alex Feb 01 '11 at 10:05
  • @Col. Shrapnel: Actually according to php.net array_walk_recursive() - `Any key that holds an array will not be passed to the function.` I was just testing out array_walk_recursive() behavior and its quite different than the solution above, plus array_walk_recursive() seems to be buggy too. – Zubair1 Apr 25 '11 at 20:07
5

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.

Pekka
  • 442,112
  • 142
  • 972
  • 1,088
  • 1
    thanks,i realise this, I am just trying to achieve this without writing lots of repetitive code... – SirG Sep 06 '10 at 10:27
  • @SirG: by al means validate input, but you should only ever apply sanitization transformations to data when it **leaves** PHP - and the method should be appropriate to where the data is going (mysql_real_escape_string(), htmlentities(), urlencode(), base64)_encode(), escapeshellarg(), addslashes()....etc all do **different** things) – symcbean Sep 06 '10 at 11:26
  • @symcbean, thanks for the answer, so would filter_var be suitable to apply just before sending an email via php's mail function ? cheers. – SirG Sep 12 '10 at 11:08
  • Maybe - you can validate an email address with filter_var - but you can't validate the body of an email, subject or additional headers. The MTA should take care of the former but the latter 2 are both targets for header injection which can compromise your application. – symcbean Sep 13 '10 at 10:49
2
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);
Kemo
  • 6,942
  • 3
  • 32
  • 39
1

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

ymakux
  • 3,415
  • 1
  • 34
  • 43
1

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);
colidyre
  • 4,170
  • 12
  • 37
  • 53
Paris Z
  • 19
  • 1
0

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.

OXiGEN
  • 2,041
  • 25
  • 19
-2

Let's say we want to sanitize the $_POST array:

foreach($_POST as $k=>$v) {$_POST[$k] = htmlspecialchars($v);}

This simple. Isn't it?

  • 1
    1) this wouldn't work recursively, a key point in the question. 2) *never* sanitise values on the input end. Always sanitise them on the *output* end, as it is the output (be it to html, database, xml, json, etc) which defines the requirements. The above code runs a serious risk of leaving one open to SQL Injection attacks, for example. – Will Palmer Oct 01 '12 at 09:16
  • The question was not to sanitize for SQL injections. It was to strip the tags. I think it's better to use Prepared Statement for this purpose. The code I wrote dosen't strip the tags, it just rewrites those html special chars as [displayable format](http://webdesign.about.com/library/bl_htmlcodes.htm) format ex.: "&eacute". Of course you can replace htmlspecialchars by strip_tags. Depends on what you want to do! – Marc Tremblay Oct 01 '12 at 18:23
  • Prepared statements do indeed protect against SQL injection, but they are also a form of sanitising at the output, rather than the input. Never sanitise at the input, and certainly never do *both* :). The intended goal is to make things sane for output into HTML, not to break them for every other potential purpose. This code is what `magic_quotes_gpc` would look like if people cared more about XSS attacks than SQL injection. It is bad. Don't do it. Don't do anything similar to it. – Will Palmer Oct 01 '12 at 19:33