I have some working code to take out the tediousness of binding each variable to its parameter manually in a pdo prepared statement. I loop through the $_POST array and bind the variables to the params dynamically based on the name attributes from the html form.
My question is, is it safe to do this? Am I open to SQL injection?
Here is my code -
if( !empty($_POST) ){
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname;charset=utf8", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("INSERT INTO planes (name, description) VALUES(:name, :description)");
foreach($_POST as $key => &$value){
$key = ':'.$key;
$stmt->bindParam($key, $value);
}
$stmt->execute();
}
catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
$conn = null;
}
Yes, it's safe. If you're using parameterized queries, you won't be vulnerable to injection attacks.
That being said, it seems that you're reinventing the wheel here, which is most often not the right way to do things. However; that's outside the scope of your question.
Also please see this very similar question where the accepted answer has this to say:
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.
Related
So I understand PDO Prepared Statements should protect from SQL injection and ' escapes. But when I attempted the following...
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["id"]))
{
$id = $_POST["id"];
//$id = "2' AND name='Entry2";
$someinfo = "updated";
...DB Stuff...
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $dbpassword);
$stmt = $conn->prepare("UPDATE testdb SET info=:someinfo WHERE id=:id");
$stmt->bindParam(':id', $id);
$stmt->bindParam(':someinfo', $someinfo);
$stmt->execute();
$conn = null;
exit();
}
Then the row with id=2 and name=entry2 would be updated. Now it doesn't seem like this can be used to escape into other SQL queries, and I assume I can take precautions to ensure this kind of escape can't really do damage. But I wanted to be sure that there wasn't some other way to prevent ' escapes making unexpected changes to SQL query parameters. (Worth noting, I tried something similar in SQLi and got pretty much the same result.)
Is there something I'm missing? Or is this just the way Prepared Statements work.
After looking around some more, this behavior was eloquently explained/solved for me here:
https://phpdelusions.net/pdo#comment-277
It turns out it's not escaping the string, but instead truncating input after the integer which just made it appear to escape the string. I was able to confirm this upon modifying the code.
As a prevention against SQL injections, I'm using PDO. I have seen people using both the methods ie: bindValue() and then execute() or just execute(array())
Do both the methods prevent the attack? Since mysql_real_escape_string() is deprecated is there anything else I should consider using here?
Like for $aenrollmentno should I typecast into
$aenrollmentno = (int)($_POST['aenrollmentno']);
Will this be safe enough if I'm not using it in a prepared statement? Any other security measure that I'm missing?
<?php
if(isset($_POST['aenrollmentno']))
{
$aenrollmentno = mysql_real_escape_string($_POST['aenrollmentno']);
}
if(isset($_POST['afirstname']))
{
$afirst_name = mysql_real_escape_string($_POST['afirstname']);
$afirstname = ucfirst(strtolower($afirst_name));
}
//PDO connection
try {
$conn = new PDO('mysql:host=localhost;dbname=practice','root','');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $conn->prepare('INSERT INTO modaltable(afirstname, alastname,aenrollmentno) VALUES (:afirstname,:alastname,:aenrollmentno)');
$stmt->execute(array(
'afirstname' => $afirstname,
'alastname' => $alastname,
'aenrollmentno' => $aenrollmentno,
));
echo "Success!";
}
catch (PDOException $e) {
echo 'ERROR: '. $e->getMessage();
}
?>
execute(array) is just a shortcut for a loop that calls bindValue on each of the array elements. Use whatever suits your program flow best. Both prevent SQL injection.
Rule of thumb: Whatever you pass to prepare should NOT, in any way, depend on user input. You can pass anything you want to execute() - you might get runtime errors, e.g. if you try to put a non-numeric string into a number column - but you won't allow SQL injections.
This question already has answers here:
Are PDO prepared statements sufficient to prevent SQL injection?
(7 answers)
Closed 9 years ago.
I'm fairly new to PDO and wondering if my query below is safe from SQL injection. I'll be using this method throughout the site if so.
// make connection to DB
$db = new PDO('mysql:host='.$dateBaseHost.';dbname='.$dateBaseName, $dateBaseUsername, $dateBasePassword);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//simple query and binding with results
$query = $db->prepare(" SELECT * FROM `profile` WHERE `fullname` = :fullname ");
$search = (isset($_GET['search']) === true) ? $_GET['search'] : '' ; // ? : shorthand for if else
// bind parameters - avoids SQL injection
$query->bindValue(':fullname', $search);
//try... if not catch exception
try {
// run the query
$query->execute();
$rows = $query->fetchAll(PDO::FETCH_ASSOC);
echo '<pre>', print_r($rows, true),'</pre>';
}
catch (PDOException $e){
sendErrorMail($e->getMessage(), $e->getFile(), $e->getLine());
}
Yes - parameterized queries are safe from injection when used in this way.
As long as you use prepared statements properly, you're safe from injection. but as soon as you DIRECTLY insert any external data into a query, even if it's otherwise a prepared statement, e.g.
INSERT INTO $table VALUES (:param)
you're vulnerable - $table can be subverted in this case, even though you're using a prepared statement.
Anyone who tells you simply switching mysql->PDO or mysqli will make you safer is a flat out WRONG. You can be just as vulnerable to injection attacks with either library.
You should also
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
By default it uses emulated mode, which merely does what mysql_real_escape_string does. In some edge cases, you're still vulnerable to SQL injection.
yes, it's fairly safe but whole script could be improved:
if (isset($_GET['search']) {
// make connection to DB
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$dsn = "mysql:host=$dateBaseHost;dbname=$dateBaseName;charset=$dateBaseCharset";
$db = new PDO($dsn, $dateBaseUsername, $dateBasePassword, $opt);
//simple query and binding with results
$query = $db->prepare("SELECT * FROM profile WHERE fullname = ?");
$query->execute(array($_GET['search']));
$rows = $query->fetchAll();
echo '<pre>', print_r($rows, true),'</pre>';
}
you need to set errmode as a connection option
never use try..catch to handle error message. if you want to have a email on every error (which is just crazy), you have to set up my_exception handler() for this.
setting search to empty string doesn't make any sense
connect to PDO should be moved so separate file (not shown)
charset have to be set in DSN
I have many functions like
updateUser($id,$username,$email)
updateMusic($id, $music)
etc...
Is there a generic function to avoid SQL injections ?
I just want to avoid using mysql_real_escape_string for each parameter I have
$username = mysql_real_escape_string($username);
$email= mysql_real_escape_string($email);
$music= mysql_real_escape_string($music);
ALWAYS use prepared statements
Do NOT use mysql driver, use mysqli or PDO
You should use parameterization and let the database driver handle it for you, i.e. with PDO:
$dbh = new PDO('mysql:dbname=testdb;host=127.0.0.1', $user, $password);
$stmt = $dbh->prepare('INSERT INTO REGISTRY (name, value) VALUES (:name, :value)');
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
// insert one row
$name = 'one';
$value = 1;
$stmt->execute();
Code from Bobby-Tables.
you may use,
list($id,$music) = array_map('mysql_real_escape_string',array($id,$music))
but prepared statements rocks
No there isn't, but you can parse all your inputs ( eg. GET and POST ) at beggining of the script
Do I need to use mysql_real_escape_string() on my input (such as $_POST and $_GET) when I use the PDO library?
How do I properly escape user input with PDO?
If you use PDO you can parametize your queries, removing the need to escape any included variables.
See here for a great introductory tutorial for PDO.
Using PDO you can seperate the SQL and passed parameters using prepared statements, this removes the need to escape strings, as because the two are held seperately then combined at execution, the parameters are automatically handled as stings, from the above source:
// where $dbh is your PDO connection
$stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
/*** bind the paramaters ***/
$stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
$stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
/*** execute the prepared statement ***/
$stmt->execute();
Note: sanitization occurs during variable binding ($stmt->bindParam)
Other resources:
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
http://www.phpeveryday.com/articles/PDO-Prepared-Statement-P550.html
http://php.net/manual/en/pdo.prepared-statements.php
The important point when using PDO is:
PDO will only sanitize it for SQL, not for your application.
So yes, for writes, such as INSERT or UPDATE, it’s especially critical to still filter your data first and sanitize it for other things (removal of HTML tags, JavaScript, etc).
<?php
$pdo = new PDO(...);
$stmt = $pdo->prepare('UPDATE users SET name = :name WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filter your data first
$name = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_STRING); // <-- filter your data first
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- Automatically sanitized for SQL by PDO
$stmt->bindParam(':name', $name, PDO::PARAM_STR); // <-- Automatically sanitized for SQL by PDO
$stmt->execute();
Without sanitizing the user input, a hacker could have saved some javascript into your database and then, when output it into your site you would have been exposed to a threat!
http://www.phptherightway.com/#pdo_extension