Recently, I switched to PDO and wanted to ask if is as safe as I do it or not?
(I filter the data before with much filter methods provided by php)
QUERY db:
include 'path_to_config_file_with_login_creds_for_db.php';
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
PDO::ATTR_PERSISTENT => false,
);
try {
$pdo = new PDO('mysql:host=' . $database_host . ';dbname=' . $database_name . ';charset=utf8mb4', $database_user, $database_pass, $options);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (PDOException $exception) {
die("some error message");
}
try {
$statement = $pdo->prepare($sql);
$statement->execute($bindings);
$statement->closeCursor();
return $output;
} catch (PDOException $stmEx) {
die("again some error message");
}
UPDATE db:
include 'again_path_to_config_file_where_creds_to_db.php';
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
PDO::ATTR_PERSISTENT => false,
);
try {
$pdo = new PDO('mysql:host=' . $database_host . ';dbname=' . $database_name . ';charset=utf8mb4', $database_user, $database_pass, $options);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (PDOException $exception) {
die("...");
}
try {
$statement = $pdo->prepare($sql);
$statement->execute($bindings);
$statement->closeCursor();
} catch (PDOException $stmEx) {
die("...");
}
Is this way safe or not?
It's all working fine but I want to know it its safe too
From the security point of view it should be safe as long as the query in $sql uses param binding properly. If the $sql variable is build like this then your code won't help you.
//DO NOT TRY THIS AT HOME
$sql = "SELECT * FROM `users` WHERE user='" . $_POST['user'] . "'";
But i can see several other issues with your code.
1) Your try ... catch blocks are useless. When you use PDO::ERRMODE_SILENT the PDO won't raise any exception. Only the $pdo->errorCode or $statement->errorCode properties will be set when error is encountered.
2) You are opening too many connections. I assume that you have the code you've shared in some function that you plan to call like queryDb($sql, $params); That means that every time you will call that function you will create new instance of PDO and open new connection. You might want to move the part that creates PDO instance into the db-config file then use that single instance everytime you are going to create new statement using $pdo->prepare().
3) The use of die. It's common practice to use die or exit when the query goes wrong in examples. But in actual application it would mean the user will see ugly empty page with single sentence saying something went wrong. It's better to throw an exception that will be handled higher in your app. That would let the application to display the page layout with menu and other things even if the requested action failed.
4) You do not set the value to $output variable in your first "Query DB" part of code. Although i'm not sure if you just left it out when copying or if you have it like this in your actual code.
In regards to SQL injection, as long as you parameterize all inputs and white list all dynamic SQL parts, you should be safe.
However, your code has another serious problem. You are silencing errors. PDO::ATTR_ERRMODE should be set to PDO::ERRMODE_EXCEPTION. But, that would leave your code even in a worse state, as you have die all over the place. Don't catch the exceptions unless you have extremely good reason to do so. Read this article https://phpdelusions.net/pdo#errors
Silencing or displaying errors to the user opens up new vectors for exploitation. That is why the best course of action is to leave them alone. In production system you should have the configuration set to never display errors. They will be securely logged on the server.
Related
Hi I have the following code :
try {
$sth = $this->container->db->prepare("select x from table");
$sth->execute();
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
return $result;
} catch (\PDOException $e) {
throw new ServerException("Could not get data");
} catch (\Exception $e) {
return false;
}
using slim 3 with wamp
the problem is when I point to the API(polling every 1 second) I got the following error:
Fatal error: Call to a member function prepare() on boolean in /
2 issues : it throws an error to the client
and it throws an error in php_error.log under wamp and the file becomes bigger
how can I prevent and catch those errors
PDO class:
public function getConnection($dsn, $username, $password) {
$conn = null;
try {
$conn = new PDO($dsn, $username, $password);
//Set common attributes
$conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return $conn;
} catch (PDOException $e) {
return false;
//TODO: flag to disable errors?
throw $e;
}
catch(Exception $e) {
die();
//TODO: flag to disable errors?
throw $e;
}
}
Like many learners, you are taking this matter upside down
You should never actually catch an exception like this.
it throws an error to the client
Disable it for the whole site on the live server. There should be not a single PHP error message shown to the user, no matter if it's PHP exception or a filesystem error. Set display_errors to a negative value and forget this matter for all.
how can I prevent and catch those errors
Again, you should never do anything like this, bluntly catching every error and just dismissing it. It's like using the notorious # operator
it throws an error in php_error.log under wamp and the file becomes bigger
Ok, only this one makes sense. There are two possible solutions:
The best one: configure your mysql server properly so it wouldn't die under such a light load like 1 RPS.
Okay, what you actually want but I still don't recommend as it never pays to sweep the dirt under the rug: catch the exception, then verify if it's one you expect, then do something (i.e. try to reconnect after a short timeout), but re-throw the exception otherwise so you will have an idea when something else would go wrong. For this purpose you should add a condition in the try..catch block that should verify the error, handle it if it's one that you expect or just throw it again otherwise.
Of course, in order to catch a PDOException you have to enable it for PDO.
1) In Slim you should use the container (service factory) to build the PDO object. Example:
$container['db'] = function (Container $container) {
$settings = $container->get('settings');
$host = $settings['db']['host'];
$dbname = $settings['db']['database'];
$username = $settings['db']['username'];
$password = $settings['db']['password'];
$charset = $settings['db']['charset'];
$collate = $settings['db']['collate'];
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES $charset COLLATE $collate"
];
return new PDO($dsn, $username, $password, $options);
};
2) You must set the PDO options into the constructor to make it work. Example:
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
$pdo = new PDO($dsn, $username, $password, $options);
I've been trying to use a sqlite database (php with PDO), but have been running into a problem. Generally the commands work, and everything is fine (including storing files), but for some reason when I run these two commands (which have been simplified), I get the error
SQLSTATE[HY000]: General error: 5 database is locked
I've tried for a while, but have been unable to fix whatever is wrong. The code is below.
Things I've done:
Tried to put sleep(2) between commands
Found out that commenting either of the commands out will cause the error not to happen (which doesn't really help, as both commands must run)
Note that (unlike other problems I saw while looking at similar questions) the database operates correctly in other cases.
$db = new MyDB();
$STH = $db->catchMistakes('SELECT PASSWORD FROM USERS WHERE USERNAME = ?', "test");
$STH->fetchColumn();
$db->catchMistakes("UPDATE ISSUES SET NAME = ? WHERE NUM = ?", ["test", "1"]);
And here's the code for MyDB
public function catchMistakes($cmd, $params = []) {
if (!is_array($params)) {
$params = [$params];
}
try {
$DBH = new PDO("sqlite:" . DB);
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$DBH->beginTransaction();
$query = $DBH->prepare($cmd);
$toReturn = $query->execute($params);
$DBH->commit();
return $query;
}
catch(PDOException $e) {
$DBH->rollback();
$error = $e->getMessage();
exit;
}
}
Sorry if there's a simple fix, I'm pretty new at this. Any help would be greatly appreciated.
You can use closeCursor() method on a PDOStatement object to free the connection to the database so the statement can be executed. You can refer to the PHP manual.
I use this code for check connection to the database(MYSQL).If the database name not available then also it show connect successfully.
if(isset($_REQUEST['submit']))
{
echo $name = $_REQUEST['name'];
echo $pwd = $_REQUEST['password'];
$servername = "localhost";
$username = "root";
$password = "";
try
{
$conn = new PDO("mysql:host = $servername;dbname = pdo",$username,$password);
//echo "Connected successfully";
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "connect successfully";
}
catch(PDOException $e)
{
echo "Connection failed: " . $e->getMessage();
}
}
TL;DR
Because your code is using try-catch blocks you can be sure that as long as no exceptions are thrown the connection was successfully established. Otherwise check the error code stored inside the exception to resolve the type of error.
More detailed
Your code makes use of exceptions, so if an error occurred while a connection was attempted established an exception would be thrown. As far as I know the PDO object throws exceptions by default while it is constructed and falls back to silent mode when to instance is created. So you are assured you will be warned if anything bad happens during construction.
Update 1
How to check the database name is available or not in to the database using PDO?
As said you can utilize exceptions. But instead of echoing the exception message you can inspect the returned error code using the PDOException method getCode(). Using php version 5.6.8 I will get the code 1049. You can then check using the following:
try {
$pdo = new PDO($dsn, $username, $passowrd, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]);
}catch(PDOException $exception) {
if($exception->getCode() == 1049) {
echo 'Database does not exist!';
}
// Continue error handling...
}
Update 2: Alternative
Using the same PHP version as above you can avoid selecting a database in the DSN. With this you can fetch a list of accessible databases for the used MYSQL user. Using this list you can check the existence of one or more databases at the same time and avoid the overhead of an exception halting execution of your script.
/*
* I assume you are on localhost.
*/
$pdo = new PDO('mysql:host=127.0.0.1', $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]);
$databases = $pdo->query('SHOW DATABASES'); // Does not require a prepared statement since it takes no input.
/*
* The following can be done more elegant/efficient.
*/
$exists = false; // Equals TRUE if the database exists.
$check = 'test'; // Enter the database to check for.
foreach($databases->fetchAll(PDO::FETCH_ASSOC) as list($database)) {
if($database == $check) {
$exists = true;
break;
}
}
Bonus
You are setting the error mode after the PDO instance has been constructed. This can also be done with the fourth parameter of PDO's constructer. I think this leads to more readable code.
I would also recommend setting the configuration PDO::ATTR_EMULATE_PREPARES to false. This ensure prepared statements are always used. The documentation can be found here.
$pdo = new PDO($dsn, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]);
Happy coding!
I am thinking, is it safe and OK to use db connection in function to return it anywhere in the project? If I want to reuse this project to build another one, I can change db connection only in this function and I don't need to go all over the script searching and changing connection parameter.
function connect_db(){
$db = new PDO('mysql:host=localhost;dbname=;charset=utf8', '', '');
return $db;
}
Now I can call it anywhere functions.php file is required once, by returning
$db = connect_db();
and then whatever statement follows.
Is it ok and safe?
And how to close connection like this at the end of the page?
$db = NULL;
apparently won't work.
Yes, it is safe and ok to have a single place that creates a connection to your database. I would change your function just a bit though:
<?php
function connect_db(){
try{
return new PDO('mysql:host=localhost;dbname=mydb', 'username', 'password', array(PDO::MYSQL_ATTR_INIT_COMMAND =>'SET NAMES utf8'));
}catch (PDOException $e){
echo '<p>Error connecting to database! ' . $e->getMessage() . '</p>';
die();
}
}
?>
You can read more about the PDO constructor in the documentation.
And to close the PDO connection created this way you do indeed just:
<?php
$pdo = NULL;
?>
Please note that there is really a lot more to be said on this subject. Ideally you would have a class that handles the creation of your PDO object(s). You might want to take a look at this excellent answer from Gordon as a place to start.
I realize this is probably super simple but i just started taking peoples advice and im converting a small program from mysql to PDO as an attempt to learn and switch to PDO.
The script is a script that shows you how to build a shopping cart, so keep in mind its focused on a learning audience like myself. Anyway i converted the old script here:
function db_connect()
{
$connection = mysql_pconnect('localhost', 'database_1', 'password');
if(!$connection)
{
return false;
}
if(!mysql_select_db('database_1'))
{
return false;
}
return $connection;
}
to this which does connect fine:
function db_connect() {
//Hostname
$hostname = 'xxx.com';
//username
$username = 'xxx';
//password
$password = 'xxx';
try {
$connection = new PDO("mysql:host=$hostname;dbname=database_1", $username, $password);
}
catch(PDOException $e){
echo $e->getMessage();
}
}
Now in other parts of the script before accessing the database it does this:
$connection = db_connect();
Now i have 2 questions. First is to help me understand better what is going on.
I understand in the original mysql function we connect to the database, if the connection is unsuccessful or the database doesnt exist it returns false. If it does connect to the database then it returns true.
With that i mind i dont understand this:
$connection = db_connect();
Isnt that just assigning true or false to the $connection variable, if so then whats going on in this part of the code.
$price = 0.00;
$connection = db_connect();
if (is_array($cart))
{
foreach($cart as $id => $qty)
{
$query = "SELECT price
FROM products
WHERE products.id = '$id' ";
$result = mysql_query($query);
if($result)
{
$item_price = mysql_result($result, 0, 'price');
$price += $item_price * $qty;
}
}
}
Instead couldn't i just create an include file with the PDO connection and no function and include that at the top of each page i run scripts on. I just don't understand where the $connection = db_connect comes in.
So the 2nd question if my above suggestion is not the answer is how do i return a boolean value from the connection function to return true or false (If i even need to)
There is one essential difference between old mysql and PDO: both these libraries require a resource variable to connect with. If you take a look at mysql_query() function definition, you will notice the second parameter, represents such a resource.
$connection variable returned by your old function by no means contain boolean value but such a resource variable. Which can be used in every mysql_query call.
But while for mysql ext this resource parameter being optional, and used automatically when not set, with PDO you have to address this resource variable explicitly. Means you cannot just call any PDO function anywhere in the code, but only as a method of existing PDO object. Means you have to make this variable available wherever you need PDO.
Thus, you need not a boolean but PDO object.
Here is the right code for the function:
function db_connect()
{
$dsn = "mysql:host=localhost;dbname=test;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
return new PDO($dsn,'root','', $opt);
}
now you can use it this way
$pdo = db_connect();
but note again - unlike with mysql_query(), you have to always use this $pdo variable for your queries.
Further reading is PDO tag wiki
As you guessed from the context, db_connect() is supposed to return the connection object. Your converted version doesn't return anything, which is a problem.
With the mysql module, you can run queries without using the connection object - this is not the case with PDO. You'll need to use the connection object to run any queries -
$result = $connection->query('SELECT * FROM foo');
First off, let me congratulate you for making the effort to learn PDO over mysql_*. You're ahead of the curve!
Now, a few things to understand:
PDO is OO, meaning the connection to the database is represented by a PDO Object.
Your db_connect() function should return the object that gets created.
Passing in the parameters required by PDO will give you more flexibility!
So what we have is:
function db_connect($dsn, $username, $password)
{
$conn = new PDO($dsn, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //This makes sure that PDO will throw PDOException objects on errors, which makes it much easier enter code hereto debug.
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //This disables emulated prepared statements by PHP, and switches to *true* prepared statements in MySQL.
return $conn; //Returns the connection object so that it may be used from the outside.
}
Now, you may have noticed we aren't checking for PDOExceptions inside of the function! That's because you can't handle the error from inside of the function correctly (becuase you don't know what you would want to do? Would you terminate the page? Redirect to an error message?). So you can only know it when you call the function.
So usage:
try {
$connection = db_connect("mysql:host=$hostname;dbname=database", "user", "pass");
}
catch (PDOException $e) {
echo "Database error! " . $e->getMessage();
}
Further Reading!
The PDO Manual entry - is super easy and super useful. I recommend you read all of it.