How to Prevent Code Injection Attacks in PHP

How to prevent code injection attacks in PHP?

  • mysql_real_escape_string used when insert into database
  • htmlentities() used when outputting data into webpage
  • htmlspecialchars() used when?
  • strip_tags() used when?
  • addslashes() used when?

htmlspecialchars() used when?

htmlspecialchars is roughly the same as htmlentities. The difference: character encodings.

Both encode control characters like <, >, & and so on used for opening tags etc. htmlentities also encode chars from other languages like umlauts, euro-symbols and such. If your websites are UTF, use htmlspecialchars(), otherwise use htmlentities().

strip_tags() used when?

htmlspecialchars / entities encode the special chars, so they're displayed but not interpreted. strip_tags REMOVES them.

In practice, it depends on what you need to do.

An example: you've coded a forum, and give users a text field so they can post stuff. Malicious ones just try:

pictures of <a href="javascript:void(window.setInterval(function () {window.open('http://evil.example');}, 1000));">kittens</a> here

If you don't do anything, the link will be displayed and a victim that clicks on the link gets lots of pop-ups.

If you htmlentity/htmlspecialchar your output, the text will be there as-is. If you strip_tag it, it simply removes the tags and displays it:

pictures of kittens here

Sometimes you may want a mixture, leave some tags in there, like <b> (strip_tags can leave certain tags in there). This is unsafe too, so better use some full blown library against XSS.

addslashes

To quote an old version of the PHP manual:

Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).

An example use of addslashes() is when you're entering data into a database. For example, to insert the name O'reilly into a database, you will need to escape it. It's highly recommeneded to use DBMS specific escape function (e.g. mysqli_real_escape_string() for MySQL or pg_escape_string() for PostgreSQL), but if the DBMS you're using does't have an escape function and the DBMS uses \ to escape special chars, you can use this function.

The current version is worded differently.

Best way to avoid code injection in PHP

Use a whitelist and make sure the page is in the whitelist:

  $whitelist = array('home', 'page');

if (in_array($_GET['page'], $whitelist)) {
include($_GET['page'].'.php');
} else {
include('home.php');
}

How can I prevent SQL injection in PHP?

The correct way to avoid SQL injection attacks, no matter which database you use, is to separate the data from SQL, so that data stays data and will never be interpreted as commands by the SQL parser. It is possible to create an SQL statement with correctly formatted data parts, but if you don't fully understand the details, you should always use prepared statements and parameterized queries. These are SQL statements that are sent to and parsed by the database server separately from any parameters. This way it is impossible for an attacker to inject malicious SQL.

You basically have two options to achieve this:

  1. Using PDO (for any supported database driver):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    $stmt->execute([ 'name' => $name ]);

    foreach ($stmt as $row) {
    // Do something with $row
    }
  2. Using MySQLi (for MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    $stmt->execute();

    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
    // Do something with $row
    }

If you're connecting to a database other than MySQL, there is a driver-specific second option that you can refer to (for example, pg_prepare() and pg_execute() for PostgreSQL). PDO is the universal option.



Correctly setting up the connection

PDO

Note that when using PDO to access a MySQL database real prepared statements are not used by default. To fix this you have to disable the emulation of prepared statements. An example of creating a connection using PDO is:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4', 'user', 'password');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

In the above example, the error mode isn't strictly necessary, but it is advised to add it. This way PDO will inform you of all MySQL errors by means of throwing the PDOException.

What is mandatory, however, is the first setAttribute() line, which tells PDO to disable emulated prepared statements and use real prepared statements. This makes sure the statement and the values aren't parsed by PHP before sending it to the MySQL server (giving a possible attacker no chance to inject malicious SQL).

Although you can set the charset in the options of the constructor, it's important to note that 'older' versions of PHP (before 5.3.6) silently ignored the charset parameter in the DSN.

Mysqli

For mysqli we have to follow the same routine:

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // error reporting
$dbConnection = new mysqli('127.0.0.1', 'username', 'password', 'test');
$dbConnection->set_charset('utf8mb4'); // charset


Explanation

The SQL statement you pass to prepare is parsed and compiled by the database server. By specifying parameters (either a ? or a named parameter like :name in the example above) you tell the database engine where you want to filter on. Then when you call execute, the prepared statement is combined with the parameter values you specify.

The important thing here is that the parameter values are combined with the compiled statement, not an SQL string. SQL injection works by tricking the script into including malicious strings when it creates SQL to send to the database. So by sending the actual SQL separately from the parameters, you limit the risk of ending up with something you didn't intend.

Any parameters you send when using a prepared statement will just be treated as strings (although the database engine may do some optimization so parameters may end up as numbers too, of course). In the example above, if the $name variable contains 'Sarah'; DELETE FROM employees the result would simply be a search for the string "'Sarah'; DELETE FROM employees", and you will not end up with an empty table.

Another benefit of using prepared statements is that if you execute the same statement many times in the same session it will only be parsed and compiled once, giving you some speed gains.

Oh, and since you asked about how to do it for an insert, here's an example (using PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute([ 'column' => $unsafeValue ]);


Can prepared statements be used for dynamic queries?

While you can still use prepared statements for the query parameters, the structure of the dynamic query itself cannot be parametrized and certain query features cannot be parametrized.

For these specific scenarios, the best thing to do is use a whitelist filter that restricts the possible values.

// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}

Preventing JavaScript Injections in a PHP Web Application

A good first step is applying the methods listed in the question Gert G linked. This covers in detail the variety of functions that can be used in different situations to cleanse input, including mysql_real_escape_string, htmlentities(), htmlspecialchars(), strip_tags() and addslashes()

A better way, whenever possible, is to avoid inserting user input directly into your database. Employ whitelist input validation: in any situation where you only have a limited range of options, choose from hard-coded values for for insertion, rather than taking the input from any client-side facing form. Basically, this means having only certain values that you accept, instead of trying to eliminate/counter evil/mal-formed/malicious input.

For example:
If you have a form with a drop down for items, do not take use the input from this dropdown for insertion. Remember that a malicious client can edit the information sent with the form's submission, even if you think they only have limited options. Instead, have the drop down refer to an index in an array in your server-side code. Then use that array to choose what to insert. This way, even if an attacker tries to send you malicious code, it never actually hits your database.

Obviously, this doesn't work for free-form applications like forums or blogs. For those, you have to fall back on the "first step" techniques. Still, there are a wide range of options that can be improved via whitelist input validation.

You can also use parameterized queries (aka prepared statements with bind variables) for your sql interactions wherever possible. This will tell your database server that all input is simply a value, so it mitigates a lot of the potential problems from injection attacks. In many situations, this can even cover free-form applications.

Prevent SQL injection attack in PHP

SQLite3::exec is for executing a query string, not a prepared statement. You need to use SQLite3Stmt::execute instead. Change:

if (!$db->exec ($sql)) {
throw new Exception($db->lastErrorMsg());
}

to

if (!$sql->execute()) {
throw new Exception($db->lastErrorMsg());
}

Note you can't echo $sql as it is an object, not a simple type. If you want to look at what a SQLite3Stmt object looks like, you would need to print_r($sql) or var_dump($sql).

Is this code sufficient to protect me from SQL injection attacks, and PHP injection attacks?

The general rule is to deny everything and allow through only valid characters, rather than removing what you consider to be invalid.
The most important aspect is what you do with these string afterwards. If you have a line later saying:
tsql = "SELECT * FROM Users WHERE Username='" . $username . "' AND "
then this is the primary area of risk, although mysql_real_escape_string should avoid this.

By using a libraries or features that allow passing of parameters to the database there can never be any sql injection, as the database parameters can't be interpreted into TSQL, leaving only PHP/Javascript injection as a possibility.
Basically, look at the bind_param functions as the only true protection.

Whenever displaying data on-screen, consider something like htmlspecialchars() to convert it to HTML. There's no point in storing something escaped if you need it un-escaped later, and raw data in the database poses no risk as long as you always consider it raw.

In summary, the code you list may or may not reduce injection, but there are too many combinations to exclude every possibility, including aspects such as a user using single quotes (you're only replacing double quotes).
All user input data is potentially dangerous. Feel free to store it raw, but whenever USING it make sure your operations are protected using one of the above options.

My PHP is a bit rusty now, but exactly the same rules apply to SQL Server, Oracle, .NET, Java any any other database/language.



Related Topics



Leave a reply



Submit