By Brent


2008-09-24 20:20:39 8 Comments

Is there a catchall function somewhere that works well for sanitizing user input for SQL injection and XSS attacks, while still allowing certain types of HTML tags?

19 comments

@troelskn 2008-09-24 22:30:37

It's a common misconception that user input can be filtered. PHP even has a (now deprecated) "feature", called magic-quotes, that builds on this idea. It's nonsense. Forget about filtering (or cleaning, or whatever people call it).

What you should do, to avoid problems, is quite simple: whenever you embed a string within foreign code, you must escape it, according to the rules of that language. For example, if you embed a string in some SQL targeting MySQL, you must escape the string with MySQL's function for this purpose (mysqli_real_escape_string). (Or, in case of databases, using prepared statements are a better approach, when possible.)

Another example is HTML: If you embed strings within HTML markup, you must escape it with htmlspecialchars. This means that every single echo or print statement should use htmlspecialchars.

A third example could be shell commands: If you are going to embed strings (such as arguments) to external commands, and call them with exec, then you must use escapeshellcmd and escapeshellarg.

And so on and so forth ...

The only case where you need to actively filter data, is if you're accepting preformatted input. For example, if you let your users post HTML markup, that you plan to display on the site. However, you should be wise to avoid this at all cost, since no matter how well you filter it, it will always be a potential security hole.

@Bobby Jack 2008-10-20 13:32:42

"This means that every single echo or print statement should use htmlspecialchars" - of course, you mean "every ... statement outputting user input"; htmlspecialchars()-ifying "echo 'Hello, world!';" would be crazy ;)

@Kornel 2009-09-09 21:33:44

There's one case where I think filtering is the right solution: UTF-8. You don't want invalid UTF-8 sequences all over your application (you might get different error recovery depending on code path), and UTF-8 can be filtered (or rejected) easily.

@bobince 2009-11-27 20:22:19

@porneL: Yes, and it can also be worthwhile to filter out control characters other than newline at this point. However given that most PHP apps can't even get the HTML-escaping right yet I'm not going to push the overlong UTF-8 sequence issue (they're only really an issue in IE6 pre-Service-Pack-2 and old Operas).

@Steve Clay 2010-06-06 17:52:58

Although your answer is helpful, HTML can and is successfully filtered for XSS in numerous applications. E.g. Comment systems in blog software such as WordPress.

@David O. 2010-09-28 23:22:04

I realize this is an old question, but as of PHP 5.2.0 PHP has introduced Filters (php.net/manual/en/book.filter.php) and the function filter_var(), which when passed a value and an appropriate filter will either sanitize or validate the supplied user input.

@troelskn 2010-12-08 08:40:14

@david 5.2.0 was out when I made this answer.

@Jens Roland 2011-06-15 21:32:27

Hi Troels -- Thanks for the answer. Most of the frameworks & languages I use every day, including PHP, Javascript and the Apache web server, have failed spectacularly at 'sanitizing' input in the past (magic quotes OMG!), and if they can't get it right, what chance do I have? Currently, I use prepared statements for all SQL I ever write, and for html I use a variant of htmlspecialchars, and frankly I'm still not 100% sure I'm not missing something. Security is HARD.

@troelskn 2011-06-22 07:18:08

@Jens Roland -- You're quite right; Security is hard. Trying to delegate it to a framework is probably not a good strategy. Much better to actually understand what we're dealing with. It sounds like you have the most common bases covered though.

@jbyrd 2011-10-28 22:23:03

But will mysql_real_escape_string properly handle the following: $sub = mysql_real_escape_string("%something"); // still %something mysql_query("SELECT * FROM messages WHERE subject LIKE '{$sub}%'"); ...?

@troelskn 2011-10-29 20:02:08

@jbyrd - no, LIKE uses a specialised regexp language. You will have to escape your input string twice - once for the regexp and once for the mysql string encoding. It's code within code within code.

@Halil Özgür 2012-01-14 15:09:35

And escaping % and the like for SQL is most often overlooked...

@Ijas Ameenudeen 2013-03-16 07:53:31

before you use mysql_real_escape_string , you should be connected to a database.

@Marcel Korpel 2013-06-05 12:46:58

At this moment mysql_real_escape_string is deprecated. It's considered good practice nowadays to use prepared statements to prevent SQL injection. So switch to either MySQLi or PDO.

@Qix 2014-04-22 22:33:35

I haven't touched PHP for a long while; coming back to it and seeing this answer about the deprecation of magic quotes makes me happy.

@jbo5112 2014-04-30 06:37:49

While useful, this doesn't answer the actual question. They wanted to allow some HTML tags in the input. The only advice here on how to do that is to consider not allowing them by using htmlspecialchars. Supporting them may be some sort of customer requirement. I have seen a number of websites that support some HTML markup (e.g. slashdot.org) on input, so I can only assume it's possible.

@Jo Smo 2014-07-14 12:10:28

@troelskn The only case where you need to actively filter data, is if you're accepting preformatted input. Eg. if you let your users post HTML markup, that you plan to display on the site. but if you don't sanitize the input (a $_POST for example), then a user can always input html right? So... Here you are saying that you should sanitize every user input, because a user can enter html code anywhere if you don't sanitize it. Or have i gotten this wrong somehow?

@troelskn 2014-07-15 13:26:19

@tastro No, you don't sanitize when data is input - you sanitize when it's used. E.g. as late as possible. That will give you the best level of security.

@Jo Smo 2014-07-15 16:00:57

@troelskn could you tell me why it will give me the best level of security if i do it as late as possible? Thanks!

@troelskn 2014-07-15 17:33:45

Because you limit the attack surface. If you sanitize early (when input), you have to be certain that there are no other holes in the application where bad data could enter through. Whereas if you do it late, then your output function doesn't have to "trust" that it is given safe data - it simply assumes that everything is unsafe.

@Alan Mattano 2015-05-30 02:20:48

@troelskn can you include "escape" code for each of the examples?

@user1893702 2015-08-06 05:01:37

Question: Would you need to sanitize/validate user input if you were using that input to make a curl post request?

@troelskn 2015-08-06 08:19:56

@sudosoul probably not. Depending on how you pass data to curl, you will need to serialize/encode it properly though.

@mwfearnley 2016-07-01 16:05:31

Should I infer from this answer that (arbitrary) input categorically cannot be filtered? I don't follow.

@tereško 2016-07-23 09:20:58

@troelskn you really need to update this post.

@troelskn 2016-08-03 11:39:19

@tereško how so?

@tereško 2016-08-03 11:50:43

@troelskn I was mostly thinking about the SQL part. As in: use of prepared statements instead of escaping.

@Si8 2018-04-20 18:23:02

I did the following: <input type="hidden" name="my-val" value="<?= htmlspecialchars($_REQUEST['my-val']); ?>"/> and the Url is http://example.com?my-val=<test>this and value is still <test>this in the hidden statement

@BugWhisperer 2019-07-29 00:46:59

so htmlspecialchars() and prepared statements are enough to sanitize user input from input elements that is inserted into the db?!

@BugWhisperer 2019-07-29 00:48:38

@Si8 apparently <?= has been removed from PHP 7+

@SchizoDuckie 2008-09-24 23:12:57

PHP has the new nice filter_input functions now, that for instance liberate you from finding 'the ultimate e-mail regex' now that there is a built-in FILTER_VALIDATE_EMAIL type

My own filter class (uses JavaScript to highlight faulty fields) can be initiated by either an ajax request or normal form post. (see the example below)

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays. 
 *  Note that some of the regexes are for dutch input!
 *  Example:
 * 
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanitize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanitize);
 *                  
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanitize($_POST);
 *      // now do your saving, $_POST has been sanitized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }   
 *  
 * To validate just one element:
 * $validated = new FormValidator()->validate('[email protected]', 'email');
 * 
 * To sanitize just one element:
 * $sanitized = new FormValidator()->sanitize('<b>blah</b>', 'string');
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;


    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanitations = $sanitations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanitizes an array of items according to the $this->sanitations
     * sanitations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanitations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanitize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanitations) === false && !array_key_exists($key, $this->sanitations)) continue;
            $items[$key] = self::sanitizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanitize a single var according to $type.
     * Allows for static calling to allow simple sanitization
     */
    public static function sanitizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }

    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       



}

Of course, keep in mind that you need to do your sql query escaping too depending on what type of db your are using (mysql_real_escape_string() is useless for an sql server for instance). You probably want to handle this automatically at your appropriate application layer like an ORM. Also, as mentioned above: for outputting to html use the other php dedicated functions like htmlspecialchars ;)

For really allowing HTML input with like stripped classes and/or tags depend on one of the dedicated xss validation packages. DO NOT WRITE YOUR OWN REGEXES TO PARSE HTML!

@rjmunro 2011-08-01 14:50:04

This looks like it might be a handy script for validating inputs, but it is completely irrelevant to the question.

@drtechno 2019-05-14 23:50:30

I can see php filter sanitize special special characters come in handy.

like:

    $a=fliter_var($_POST['a'],FILTER_SANITIZE_SPECIAL_CHARS);

However, by stock, I think it could be better, because looking at the c code, it only filters the " ' \ < > & and \0 so I can see this be a good way of sanitizing. However, changing the source code to include these other characters like / { } [ ] . ; ` would strengthen this function on the encode (enc['']) line:

    void php_filter_special_chars(PHP_INPUT_FILTER_PARAM_DECL)
{
unsigned char enc[256] = {0};

php_filter_strip(value, flags);

/* encodes ' " < > & \0 to numerical entities */
enc['\''] = enc['"'] = enc['<'] = enc['>'] = enc['&'] = enc[0] = 1;

/* if strip low is not set, then we encode them as &#xx; */
memset(enc, 1, 32);

if (flags & FILTER_FLAG_ENCODE_HIGH) {
    memset(enc + 127, 1, sizeof(enc) - 127);
}

php_filter_encode_html(value, enc);
}

@Erik Thiart 2018-08-21 19:31:01

Never trust user data.

function clean_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

The trim() function removes whitespace and other predefined characters from both sides of a string.

The stripslashes() function removes backslashes

The htmlspecialchars() function converts some predefined characters to HTML entities.

The predefined characters are:

& (ampersand) becomes &amp;
" (double quote) becomes &quot;
' (single quote) becomes &#039;
< (less than) becomes &lt;
> (greater than) becomes &gt;

@Erik Thiart 2018-10-15 08:05:33

I added a little more information to this practical example.

@Dharman 2019-07-14 18:01:17

What would this protect from? Is this for XSS? Why is it called clean_input then? Why would you want to strip slashes?

@Alejandro Silva 2015-05-27 15:36:31

If you're using PostgreSQL, the input from PHP can be escaped with pg_escape_string()

 $username = pg_escape_string($_POST['username']);

From the documentation (http://php.net/manual/es/function.pg-escape-string.php):

pg_escape_string() escapes a string for querying the database. It returns an escaped string in the PostgreSQL format without quotes.

@cryptic ツ 2015-05-30 08:24:26

pg_escape_literal() is the recommended function to use for PostgreSQL.

@Mark Martin 2018-02-25 06:11:55

Methods for sanitizing user input with PHP:

  • Use Modern Versions of MySQL and PHP.

  • Set charset explicitly:

    • $mysqli->set_charset("utf8");
      manual
    • $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);
      manual
    • $pdo->exec("set names utf8");
      manual
    • $pdo = new PDO(
      "mysql:host=$host;dbname=$db", $user, $pass, 
      array(
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
      )
      );
      manual
    • mysql_set_charset('utf8')
      [deprecated in PHP 5.5.0, removed in PHP 7.0.0].
  • Use secure charsets:

    • Select utf8, latin1, ascii.., dont use vulnerable charsets big5, cp932, gb2312, gbk, sjis.
  • Use spatialized function:

    • MySQLi prepared statements:
      $stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); 
      $param = "' OR 1=1 /*";
      $stmt->bind_param('s', $param);
      $stmt->execute();
    • PDO::quote() - places quotes around the input string (if required) and escapes special characters within the input string, using a quoting style appropriate to the underlying driver:

      $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
      $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection)
      $var = $pdo->quote("' OR 1=1 /*");not only escapes the literal, but also quotes it (in single-quote ' characters) $stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

    • PDO Prepared Statements: vs MySQLi prepared statements supports more database drivers and named parameters:

      $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
      $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection) $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(["' OR 1=1 /*"]);

    • mysql_real_escape_string [deprecated in PHP 5.5.0, removed in PHP 7.0.0].
    • mysqli_real_escape_string Escapes special characters in a string for use in an SQL statement, taking into account the current charset of the connection. But recommended to use Prepared Statements because they are not simply escaped strings, a statement comes up with a complete query execution plan, including which tables and indexes it would use, it is a optimized way.
    • Use single quotes (' ') around your variables inside your query.
  • Check the variable contains what you are expecting for:

    • If you are expecting an integer, use:
      ctype_digit — Check for numeric character(s);
      $value = (int) $value;
      $value = intval($value);
      $var = filter_var('0755', FILTER_VALIDATE_INT, $options);
    • For Strings use:
      is_string() — Find whether the type of a variable is string

      Use Filter Function filter_var() — filters a variable with a specified filter:
      $email = filter_var($email, FILTER_SANITIZE_EMAIL);
      $newstr = filter_var($str, FILTER_SANITIZE_STRING);
      more predefined filters
    • filter_input() — Gets a specific external variable by name and optionally filters it:
      $search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match() — Perform a regular expression match;
    • Write Your own validation function.

@Ondřej Šotek 2015-07-17 17:13:12

Easiest way to avoid mistakes in sanitizing input and escaping data is using PHP framework like Symfony, Nette etc. or part of that framework (templating engine, database layer, ORM).

Templating engine like Twig or Latte has output escaping on by default - you don't have to solve manually if you have properly escaped your output depending on context (HTML or Javascript part of web page).

Framework is automatically sanitizing input and you should't use $_POST, $_GET or $_SESSION variables directly, but through mechanism like routing, session handling etc.

And for database (model) layer there are ORM frameworks like Doctrine or wrappers around PDO like Nette Database.

You can read more about it here - What is a software framework?

@symcbean 2018-02-19 12:12:38

You never sanitize input.

You always sanitize output.

The transforms you apply to data to make it safe for inclusion in an SQL statement are completely different from those you apply for inclusion in HTML are completely different from those you apply for inclusion in Javascript are completely different from those you apply for inclusion in LDIF are completely different from those you apply to inclusion in CSS are completely different from those you apply to inclusion in an Email....

By all means validate input - decide whether you should accept it for further processing or tell the user it is unacceptable. But don't apply any change to representation of the data until it is about to leave PHP land.

A long time ago someone tried to invent a one-size fits all mechanism for escaping data and we ended up with "magic_quotes" which didn't properly escape data for all output targets and resulted in different installation requiring different code to work.

@webaholik 2017-12-17 18:47:15

There's no catchall function, because there are multiple concerns to be addressed.

  1. SQL Injection - Today, generally, every PHP project should be using prepared statements via PHP Data Objects (PDO) as a best practice, preventing an error from a stray quote as well as a full-featured solution against injection. It's also the most flexible & secure way to access your database.

    Check out (The only proper) PDO tutorial for pretty much everything you need to know about PDO. (Sincere thanks to top SO contributor, @YourCommonSense, for this great resource on the subject.)

  2. XSS - Sanitize data on the way in...

    • HTML Purifier has been around a long time and is still actively updated. You can use it to sanitize malicious input, while still allowing a generous & configurable whitelist of tags. Works great with many WYSIWYG editors, but it might be heavy for some use cases.

    • In other instances, where we don't want to accept HTML/Javascript at all, I've found this simple function useful (and has passed multiple audits against XSS):

      /* Prevent XSS input */ function sanitizeXSS () { $_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING); $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST; }

  3. XSS - Sanitize data on the way out... unless you guarantee the data was properly sanitized before you add it to your database, you'll need to sanitize it before displaying it to your user, we can leverage these useful PHP functions:

    • When you call echo or print to display user-supplied values, use htmlspecialchars unless the data was properly sanitized safe and is allowed to display HTML.
    • json_encode is a safe way to provide user-supplied values from PHP to Javascript
  4. Do you call external shell commands using exec() or system() functions, or to the backtick operator? If so, in addition to SQL Injection & XSS you might have an additional concern to address, users running malicious commands on your server. You need to use escapeshellcmd if you'd like to escape the entire command OR escapeshellarg to escape individual arguments.

@vuchkov 2017-06-11 19:05:13

The best BASIC method for sanitizing user input with PHP:


    function sanitizeString($var)
    {
        $var = stripslashes($var);
        $var = strip_tags($var);
        $var = htmlentities($var);
        return $var;
    }

    function sanitizeMySQL($connection, $var)
    {
        $var = $connection->real_escape_string($var);
        $var = sanitizeString($var);
        return $var;
    }

@cryptic ツ 2017-08-05 11:49:23

So you use real_escape_string() to escape special SQL chars, then run sanitizeString() which runs stripslashes() and strips out any escaping that was done. So basically add SQL protection then remove it in the same step. Why are you even using stripslashes? These two functions are a terrible way to sanitize, even for BASIC sanitation.

@Andy Lester 2008-10-09 06:28:05

Do not try to prevent SQL injection by sanitizing input data.

Instead, do not allow data to be used in creating your SQL code. Use Prepared Statements (i.e. using parameters in a template query) that uses bound variables. It is the only way to be guaranteed against SQL injection.

Please see my website http://bobby-tables.com/ for more about preventing SQL injection.

@a coder 2014-11-13 02:49:42

Or visit the official documentation and learn PDO and prepared statements. Tiny learning curve, but if you know SQL pretty well, you'll have no trouble adapting.

@Scott Arciszewski 2015-05-30 02:04:10

For the specific case of SQL Injection, this is the correct answer!

@Basic 2015-08-16 03:01:11

Note that prepared statements don't add any security, parameterised queries do. They just happen to be very easy to use together in PHP.

@Ramon Bakker 2016-02-22 15:50:47

Its not the only guaranteed way. Hex the input and unhex in query will prevent also. Also hex attacks are not possible if you use hexing right.

@Abraham Brookes 2017-01-09 08:34:11

What if you're inputting something specialized, like email addresses or usernames?

@Teson 2018-05-24 19:25:50

If you need dynamic order by PDO doesn't support it so you still need to take height for that with input validation

@user138720 2014-11-17 21:59:15

Just wanted to add that on the subject of output escaping, if you use php DOMDocument to make your html output it will automatically escape in the right context. An attribute (value="") and the inner text of a <span> are not equal. To be safe against XSS read this: OWASP XSS Prevention Cheat Sheet

@Andrew 2012-07-16 10:44:30

What you are describing here is two separate issues:

  1. Sanitizing / filtering of user input data.
  2. Escaping output.

1) User input should always be assumed to be bad.

Using prepared statements, or/and filtering with mysql_real_escape_string is definitely a must. PHP also has filter_input built in which is a good place to start.

2) This is a large topic, and it depends on the context of the data being output. For HTML there are solutions such as htmlpurifier out there. as a rule of thumb, always escape anything you output.

Both issues are far too big to go into in a single post, but there are lots of posts which go into more detail:

Methods PHP output

Safer PHP output

@Daniel Papasian 2008-09-24 20:24:03

No. You can't generically filter data without any context of what it's for. Sometimes you'd want to take a SQL query as input and sometimes you'd want to take HTML as input.

You need to filter input on a whitelist -- ensure that the data matches some specification of what you expect. Then you need to escape it before you use it, depending on the context in which you are using it.

The process of escaping data for SQL - to prevent SQL injection - is very different from the process of escaping data for (X)HTML, to prevent XSS.

@dangel 2012-10-15 08:40:08

PHP 5.2 introduced the filter_var function.

It supports a great deal of SANITIZE, VALIDATE filters.

http://php.net/manual/en/function.filter-var.php

@Hamish Downer 2010-03-08 23:14:13

One trick that can help in the specific circumstance where you have a page like /mypage?id=53 and you use the id in a WHERE clause is to ensure that id definitely is an integer, like so:

if (isset($_GET['id'])) {
  $id = $_GET['id'];
  settype($id, 'integer');
  $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
  # now use the result
}

But of course that only cuts out one specific attack, so read all the other answers. (And yes I know that the code above isn't great, but it shows the specific defence.)

@Duc Tran 2013-07-22 06:58:00

I use $id = intval($id) instead :)

@test 2014-12-22 03:03:50

Casting integer is a good way to ensure only numerical data is inserted.

@vladkras 2016-11-21 14:40:21

$id = (int)$_GET['id'] and $que = sprintf('SELECT ... WHERE id="%d"', $id) is good too

@Peter Bailey 2008-09-24 20:30:01

No, there is not.

First of all, SQL injection is an input filtering problem, and XSS is an output escaping one - so you wouldn't even execute these two operations at the same time in the code lifecycle.

Basic rules of thumb

  • For SQL query, bind parameters (as with PDO) or use a driver-native escaping function for query variables (such as mysql_real_escape_string())
  • Use strip_tags() to filter out unwanted HTML
  • Escape all other output with htmlspecialchars() and be mindful of the 2nd and 3rd parameters here.

@Robert Mark Bram 2012-10-29 01:16:58

So you only use strip_tags() or htmlspecialchars() when you know that the input has HTML that you want to get rid of or escape respectively - you are not using it for any security purpose right? Also, when you do the bind, what does it do for stuff like Bobby Tables? "Robert'); DROP TABLE Students;--" Does it just escape the quotes?

@jbo5112 2014-04-30 14:07:58

If you have user data that will go into a database and later be displayed on web pages, isn't it usually read a lot more than it's written? To me, it makes more sense to filter it once (as input) before you store it, instead of having to filter it every time you display it. Am I missing something or did a bunch of people vote for needless performance overhead in this and the accepted answer?

@Jo Smo 2014-07-14 12:26:02

Best answer for me. It's short and addresses the question well if you ask me. Is it possible to attack PHP somehow via $_POST or $_GET with some injection or is this impossible?

@drtechno 2019-09-18 16:43:38

oh yes, the $post and $get arrays accept all characters, but some of those characters can be used against you if the character is allowed to be enumerated in the posted php page. so if you don't escape encapsulating characters ( like ", ' and ` ) it could open up an attack vector. the ` character is often missed, and can be used to form command line execution hacks. Sanitation will prevent user input hacking, but will not help you with web application firewall hacks.

@jasonbar 2008-09-24 20:29:16

To address the XSS issue, take a look at HTML Purifier. It is fairly configurable and has a decent track record.

As for the SQL injection attacks, make sure you check the user input, and then run it though mysql_real_escape_string(). The function won't defeat all injection attacks, though, so it is important that you check the data before dumping it into your query string.

A better solution is to use prepared statements. The PDO library and mysqli extension support these.

@paan 2008-09-24 22:29:19

there is no "best way" to do something like sanitizing input.. Use some library, html purifier is good. These libraries have been pounded on many times. So it is much more bulletproof than anything ou can come up yourself

@Steve Clay 2010-06-06 18:09:51

@Till 2008-09-24 20:26:36

There is the filter extension (howto-link, manual), which works pretty well with all GPC variables. It's not a magic-do-it-all thing though, you will still have to use it.

Related Questions

Sponsored Content

16 Answered Questions

[SOLVED] Why shouldn't I use mysql_* functions in PHP?

  • 2012-10-12 13:18:39
  • Madara Uchiha
  • 208316 View
  • 2405 Score
  • 16 Answer
  • Tags:   php mysql database

37 Answered Questions

[SOLVED] Deleting an element from an array in PHP

  • 2008-12-15 20:28:55
  • Ben
  • 2377202 View
  • 2316 Score
  • 37 Answer
  • Tags:   php arrays unset

13 Answered Questions

[SOLVED] How does the SQL injection from the "Bobby Tables" XKCD comic work?

18 Answered Questions

[SOLVED] Reference — What does this symbol mean in PHP?

28 Answered Questions

[SOLVED] How can I prevent SQL injection in PHP?

4 Answered Questions

[SOLVED] SQL injection that gets around mysql_real_escape_string()

7 Answered Questions

[SOLVED] How does PHP 'foreach' actually work?

1 Answered Questions

[SOLVED] Sanitizing user inputs with Spring MVC framework

7 Answered Questions

[SOLVED] Sanitising user input using Python

  • 2008-08-19 20:18:31
  • Steve
  • 30842 View
  • 57 Score
  • 7 Answer
  • Tags:   python xss

Sponsored Content