PDO not filter SQL Injection - php

I have an Application with PHP 5.3.29 and MySQL 5.6.35.
I used SQLQUERY to execute SQL instrucctions, then change to PDO with prepared Statements to avoid SQL-i, but whe i test my app with ZAP 2.6.0, i can confirm that the SQL-I still happens, despite the use of "PDO" and "prepare".
I activated the general log at MySQL and looked for all statements that were executed.
My code is:
function cerrar_sesion($usuario) {
$pdo = new
PDO("mysql:"."host=".DB_SERVIDOR.";"."dbname=".DB_BASEDATOS,DB_USUARIO, DB_CLAVE);
$query = $pdo->prepare('UPDATE ADMIN_USUARIO SET USERID=\' \' WHERE C_USUARIO= :usuario');
$query->bindParam(':usuario',$usuario,PDO::PARAM_INT);
$query->execute();
$pdo = null;
.........
}
Checking the DB log i see the parameter "C_USUARIO" changed, the following 3 lines were extracted from MySQL Log:
227726 Query UPDATE ADMIN_USUARIO SET USERID=' ' WHERE C_USUARIO= '54/2' 227730 Query UPDATE ADMIN_USUARIO SET USERID=' ' WHERE C_USUARIO= '108/2' 227732 Query UPDATE ADMIN_USUARIO SET USERID=' ' WHERE C_USUARIO= '108/2'
Note the values for C_USUARIO should't have "/2", that was injected by ZAP
I expected PDO to prevent the injection, but this wasn't the case, how can i do this using PDO?
Please help me, i´ll apreciate it.

By default, PDO "emulates" prepared statements, by interpolating the bound variables into your SQL query string, and then executing that SQL directly, without using parameters.
PDO does apply correct escaping as it interpolates your variables in the query, so it is safe with respect to SQL injection.
If you want real parameterized queries, disable emulation:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
See http://php.net/manual/en/pdo.setattribute.php for more information.
If you disable emulation, your MySQL query log will show the PREPARE and EXECUTE as separate steps. But MySQL will also log the full query including parameter values. This is also safe, it's just a convenience that MySQL does for the sake of logging, because it's useful to show the query with values. See example in my answer to https://stackoverflow.com/a/210693/20860.

Related

Is it possible to use (with PHP) prepared statements that have been previously declared in the mysql CLI?

In the mysql CLI I have prepared a statement like this:
PREPARE registrarUser FROM 'INSERT INTO Users (Users_name,Email,pass) values (?,?,?)'
In my database the prepared statements have to be done this way,instead of using a php method like this::
$conn->prepare("INSERT INTO Users (Users_name,Email,pass) VALUES (?, ?, ?)");
So I can't use the prepared statement or bind arguments.
I have tried this query which mimics the required statements in mysql CLI
$query = sprintf('
SET #Users_name = "%s";
SET #Email= "%s";
SET #pass = "%s";
EXECUTE registrarUser USING #Users_name, #Email, #pass;',$Users_name,$Email,$pass);
But it returns the following syntax error:
Errormessage: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SET #Email= "eds#gmail.com"; SET #pass = "Thinkshap2"; EXECUTE registrar' at line 2
Does anyone know if there is a way to do it?
Thank you very much in advance;
No, it's not possible. Prepared statements have the session scope. Whenever you open a new connection in PHP, you open a new MySQL session. You can use PREPARE and EXECUTE in PHP, but both operations have to be done using the same session.
In other words, statements created with PREPARE do not persist on the database server. They only exist for the lifetime of the current session.
The reason why you are getting a syntax error in PHP is because you have concatenated multiple SQL statements together. You can't do that by default in PHP due to security considerations. Execute each one separately. For example, this works:
$stmt = $mysqli->query("PREPARE registrarUser FROM 'SELECT ?'");
$stmt = $mysqli->query("EXECUTE registrarUser USING 32");
Warning. Using PREPARE and EXECUTE from PHP defeats the main purpose of prepared statements usage in PHP. The main advantage is that you can separate variables from SQL syntax. You can't do that with PREPARE and EXECUTE. This is why both PDO and mysqli have prepared statements. Use mysqli::prepare() and mysqli_stmt::execute()
It's not possible.
From the MySQL manual
The scope of a prepared statement is the session within which it is created...
A prepared statement created in one session is not available to other sessions.

PHP escape SQL Query algorithm

I'm currently working with PHP 5.4.x and SQL Server 7 and I'm having TONS of issues with the PDO object for the ODBC Driver (Which is the only one that works on Sql Server 7), Statements throw errors everywhere ....
I finally got it working using PDO::query() method, BUT I need to escape the Input .... And PDO::quote IS NOT WORKING, I red the Documentation on php pdo docs about PDO and it says that PDO::quote is Not well implemented on PDO_ODBC, which might explain why im getting errors.
For Example: this
$escapedString = $pdoObject->quote($myQueryString);
returns False, it does not return the escaped string.
That been said,
Do you know a good way to escape input to prevent SQL INJECTION???
PS: Due to driver issues (old tech) I CANNOT Trust in SQL Statements, so is not an option.
Any ideas??
EDIT:
For Example. This does not work
getQueryFromFile is only retrieving a query from a file.
and SqlServerPdo is just a wrapper class I wrote over the PHP PDO so I get the connection as a Singleton
For the Record, the query actually WORKS, it has been tested on the Sql Server Engine
$conn = SqlServerPdo::connect();
$query = SqlServerPdo::getQueryFromFile('STUDENTS_FIND');
$statement = $conn->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_INT);}
$statement->execute();
This throws the error:
text is incompatible with int (SQLExecute[206] at ext\pdo_odbc\odbc_stmt.c:133)
It seems as if the statement is treating the :id param as a text, not as an INT.
bindValue returns the same error

Php PDO query security

I am new to the PDO class, I have been using MYSQLI since just now and I am kind of confused. This question is rather simple but I cannot find the answer in straight text anywhere in the manual. So calling $pdo->query(some query) will automatically escape the query and will not leave any room for potential injections of any kind. Is this true?
NO, this is NOT true.
To avoid any risk of mysql injections you will need either prepared statments or to escape properly your variables (which would involve you to manually escape each variable before submit). I would suggest to use prepared statements because they are way easier to use. Please read this post How can I prevent SQL injection in PHP?. You can either have those with mysqli OR PDO, a nice example of PDO prepared statments, token from stackoverflow
$id = 1;
$stm = $pdo->prepare("SELECT name FROM table WHERE id=?");
$stm->execute(array($id));
$name = $stm->fetchColumn();
You can learn more here about PDO prepared statements. I would also like you to have a look here How can prepared statements protect from SQL injection attacks?
the query function is not safe.
you better use prepare of the PDO object.
e.g.
$sth = $dbh->prepare("select * from mytable where myattr = :attr");
the $sth handler can be used to set the placeholder in your query (e.g. :attr in this example)
you have two choice :
either you use an array directly in the execute function of the handler :
$sth->execute (array ('attr', $myattr));
or the bindParam function of the handler then execute
$sth->bindParam ('attr', $myattr);
$sth->execute();
The method provide a good way of escaping the single quotes in your arguments.
note : also take a loot at Why you Should be using PHP’s PDO for Database Access (net.tutsplus.com)
No, PDO::query is just as vulnerable as mysql_query or any other raw query method.
If you do
$sql = "SELECT foo FROM bar WHERE baz = '$var'";
and $var is
Robert'; DROP TABLE users; --
so the result is
SELECT foo FROM bar WHERE baz = 'Robert'; DROP TABLE users; --'
then no API can help you, because no API can tell the difference between what the query part and what the user value is. This difference is only clear to the API when you use prepared statements or escape special characters in the value properly.
Read The Great Escapism (Or: What You Need To Know To Work With Text Within Text).

What is the PDO equivalent of function mysql_real_escape_string?

I am modifying my code from using mysql_* to PDO. In my code I had mysql_real_escape_string(). What is the equivalent of this in PDO?
Well No, there is none!
Technically there is PDO::quote() but it is rarely ever used and is not the equivalent of mysql_real_escape_string()
That's right! If you are already using PDO the proper way as documented using prepared statements, then it will protect you from MySQL injection.
# Example:
Below is an example of a safe database query using prepared statements (pdo)
try {
// first connect to database with the PDO object.
$db = new \PDO("mysql:host=localhost;dbname=xxx;charset=utf8", "xxx", "xxx", [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
} catch(\PDOException $e){
// if connection fails, show PDO error.
echo "Error connecting to mysql: " . $e->getMessage();
}
And, now assuming the connection is established, you can execute your query like this.
if($_POST && isset($_POST['color'])){
// preparing a statement
$stmt = $db->prepare("SELECT id, name, color FROM Cars WHERE color = ?");
// execute/run the statement.
$stmt->execute(array($_POST['color']));
// fetch the result.
$cars = $stmt->fetchAll(\PDO::FETCH_ASSOC);
var_dump($cars);
}
Now, as you can probably tell, I haven't used anything to escape/sanitize the value of $_POST["color"]. And this code is secure from myql-injection thanks to PDO and the power of prepared statements.
It is worth noting that you should pass a charset=utf8 as attribute, in your DSN as seen above, for security reasons, and always enable
PDO to show errors in the form of exceptions.
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
so errors from you database queries won't reveal sensitive data like your directory structure, database username etc.
Last but not least, there are moments when you should not trust PDO 100%, and will be bound to take some extra measures to prevent sql injection, one of those cases is, if you are using an outdated versions of mysql [ mysql =< 5.3.6 ] as described in this answer
But, using prepared statements as shown above will always be safer, than using any of the functions that start with mysql_
Good reads
PDO Tutorial for MySQL Developers
There is none*! The object of PDO is that you don’t have to escape anything; you just send it as data. For example:
$query = $link->prepare('SELECT * FROM users WHERE username = :name LIMIT 1;');
$query->execute([':name' => $username]); # No need to escape it!
As opposed to:
$safe_username = mysql_real_escape_string($username);
mysql_query("SELECT * FROM users WHERE username = '$safe_username' LIMIT 1;");
* Well, there is one, as Michael Berkowski said! But there are better ways.
$v = '"'.mysql_real_escape_string($v).'"';
is the equivalent of $v = $this->db->quote($v);
be sure you have a PDO instance in $this->db so you can call the pdo method quote()
There is no need of mysql_real_escape_string in PDO.
PDO itself adjust special character in mysql query ,you only need to pass anonymous parameter and bind it run time.like this
Suppose you have user table with attribute name,email and password and you have to insert into this use prepare statement like this
you can pass name as => $name="Rajes'h ";
it should execute there is no need of equivalent of mysql_real_escape_string
$stmt="INSERT into user(name,email,password) VALUES(:name,:email,:password)";
try{
$pstmt=$dbh->prepare($stmt);//$dbh database handler for executing mysql query
$pstmt->bindParam(':name',$name,PDO::PARAM_STR);
$pstmt->bindParam(':email',$email,PDO::PARAM_STR);
$pstmt->bindParam(':password',$password,PDO::PARAM_STR);
$status=$pstmt->execute();
if($status){
//next line of code
}
}catch(PDOException $pdo){
echo $pdo->getMessage();
}
The simplest solution I've found for porting to PDO is the replacement for mysql_real_escape_string() given at https://www.php.net/manual/en/mysqli.real-escape-string.php#121402. This is by no means perfect, but it gets legacy code running with PDO quickly.
#samayo pointed out that PDO::quote() is similar but not equivalent to mysql_real_escape_string(), and I thought it might be preferred to a self-maintained escape function, but because quote() adds quotes around the string it is not a drop in replacement for mysql_real_escape_string(); using it would require more extensive changes.
In response to a lot of people's comments on here, but I can't comment directly yet (not reached 50 points), there ARE ACTUALLY needs to use the $dbh->quote($value) EVEN when using PDO and they are perfectly justifiable reasons...
If you are looping through many records building a "BULK INSERT" command, (I usually restart on 1000 records) due to exploiting InnoDb tables in MySQL/Maria Db. Creating individual insert commands using prepared statements is neat, but highly inefficient when doing bulk tasks!
PDO can't yet deal with dynamic IN(...) structures, so when you are building a list of IN strings from a list of user variables, YOU WILL NEED TO $dbh->quote($value) each value in the list!
So yes, there is a need for $dbh->quote($value) when using PDO and is probably WHY the command is available in the first place.
PS, you still don't need to put quotes around the command, the $dbh->quote($value) command also does that for you.
Out.
If to answer the original question, then this is the PDO equivalent for mysql_real_escape_string:
function my_real_escape_string($value, $connection) {
/*
// this fails on: value="hello'";
return trim ($connection->quote($value), "'");
*/
return substr($connection->quote($value), 1, -1);
}
btw, the mysqli equivalent is:
function my_real_escape_string($value, $connection) {
return mysqli_real_escape_string($connection, $value);
}

Can PHP's PDO be limited to a single query?

PHP's PDO allows multiple querys to be executed at once, either via the query() method or as a prepared statement. Both of the following examples work:
// Two SQL queries
$query = "SELECT * FROM table; DROP table;"
// Execute via query()
$pdo->query($query);
// Execute via prepared statement
$stmt = $pdo->prepare($query);
$stmt->execute();
Is there any way to limit PDO to a single query at a time, much like the mysql_query() function is?
This is a more up-to-date answer to this question.
The old way of preventing multi query execution was to disable emulated prepares, however this was only applicable to the PDO::prepare() method. In newer versions of PHP (>= 5.5.21 and >= 5.6.5), a new constant has been introduced to disable this multi query execution in both PDO::prepare() and PDO::query(). (Constants aren't usually added in patch versions, but this was done due to the severity of a Drupal SQL injection attack brought about by this capability).
The new constant is PDO::MYSQL_ATTR_MULTI_STATEMENTS and must be set on object creation (as the fourth argument to the PDO constructor) - setting it on a pre-existing object with PDO::setAttribute() will not work.
$pdo = new PDO('mysql:host=_;dbname=_', '', '', [PDO::MYSQL_ATTR_MULTI_STATEMENTS => false]);
Mmm, there's a way of achieving this by disabling the emulation of prepared statements in PDO to make it use the native mysql API instead (multi-querying is not supported in server-side prepared statements):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
However, one of the drawbacks of this option is that the query cache is lost.

Categories