I have a simple scenario: a user connects to "get.php?city=london", the server then searches the MySQL database for the data matching that request and outputs it.
I am using the following code to do this:
<?php
$con = mysql_connect( 'localhost', 'user', 'pass') or die('Could not connect to mysql server.' );
mysql_select_db('mydb', $con) or die('Could not select database.');
if(!isset($city)) return;
$c=$_GET["city"];
$q = "select * FROM cities WHERE city = '{$c}'";
$re = mysql_query($q);
... etc
Is it secure to simply insert the GET variable into the query like this? Will this make me vulnerable to a MySQL injection? Is there a more secure way to do this?
Regards
UPDATE:
would that be more secure?:
if(!isset($city)) return;
$city=$_GET["city"];
$q = "SELECT row1, row2 WHERE city= ?;";
$mysqli = new mysqli('localhost', 'root', 'pass', 'dbname');
$stmt = $mysqli->stmt_init();
if($stmt->prepare($q)){
$stmt->bind_param("s", mysql_real_escape_string($city));
$stmt->execute();
$stmt->bind_result($row1, $row2);
while ($stmt->fetch()) {
//do stuff with the data in the $row array
}
$stmt->close();
}
It's not secure at all, GET requests can be faked by simply changing the web address.
Trusting user input is dangerous. Someone could change the value of the city parameter, inject SQL into your query, and gain access to your data. You should be using prepared statements. That means that you need to use either the mysqli extension or the PDO extension.
I recommend using the PDO extension, mainly because it allows you to switch databases in the future without having to re-write all the code.
No they are not in mysql_* functions. You need to sensitize them using mysql_real_escape_string.
Better to use PDO with bindParams and the PDO library will do it for you.
php.net/pdo_mysql
http://in3.php.net/manual/en/pdostatement.bindparam.php
If you use mysql_real_escape_string user couldn't pass a string to mysql injection. On that purpose, for cities, I think there are not problem to use GET. However if you use personal user information I would use $_POST
Related
I've learned PHP from a book and its told me to use PDO objects or sql statements (I'm not sure if that's the right terminology, I apologize if it's not).
When I look up sql stuff, a lot of the times I see stuff like this:
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysql_select_db('database_name')) {
die('Could not select database: ' . mysql_error());
}
$result = mysql_query('SELECT name FROM work.employee');
But in my code and in the book, I'm doing stuff like this:
global $db;
$query = "SELECT * FROM users WHERE username='$username'";
$results = $db->query($query);
$results = $results->fetch();
What's the difference between these two 'styles'?
First the function those are mysql_* (like mysql_query, mysql_connect etc) are deprecated and will not be supported in PHP future versions. So PDO or Mysqli are preferred way of communication with database.
The PDO 's prepared statements are used for avoiding SQL injection attacks. like in normal mysql_query you will use like this
$query = "SELECT * FROM users WHERE username='$username'";
$results = mysql_query($query);
but in PDO you have to use like this
$params = array(':username' => 'test', ':email' => $mail);
$pdo->prepare('
SELECT * FROM users
WHERE username = :username
AND email = :email');
$pdo->execute($params);
So PDO is recommended way. For more detail you can refer to
http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers
http://php.net/pdo
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
The first style was written long ago, or was written by people who stopped learning PHP before PHP5 came out. mysql_query is deprecated, and has been for a while now, and you should never be using it in a new project.
The second is using PDO, one of the newer database APIs. PDO supports a bunch of things that make working with SQL easier.
It's still pretty hideous as written, though. Most people would recommend using parameterized queries (a form of prepared statements) to separate the data from the SQL. This helps prevent "SQL injection", a process by which someone feeds you data that tricks your database into executing queries you never intended for it to.
I am investigating a project that could be sql injected (one user reported it).
Here is part of login script:
$loginemail = $_POST['loginemail'];
$loginemail_sql = mysql_real_escape_string($loginemail);
$password = $_POST['loginpass'];
$password_sql = mysql_real_escape_string($password);
//get user data
$q = 'SELECT uid, full_name, score, status FROM users WHERE email="'.$loginemail.'" AND password="'.$password_sql.'" LIMIT 1';
I would like to now if this is part of code that could be injected?
Is there a problem that $loginemail and $password are treated incorrectly and could contain some dangerouse "SQL parts"?
Let's see:
Please, don't use mysql_* functions in new code. They are no longer maintained and are officially deprecated. See the red box? Learn about prepared statements instead, and use PDO or MySQLi - this article will help you decide which. If you choose PDO, here is a good tutorial.
For starters.
Second, you're escaping $loginemail, but using the unescaped version (instead of $loginmail_sql).
Third, it's implied by your code that you store your passwords in the database as-is. You shouldn't. If an attacker gets his hands on the database, your passwords would be compromised. You should hash the password and store the hash there.
Yes, you are using $loginemail in your query that is unchanged. Use $loginemail_sql instead.
For further reading, have a look at How to prevent SQL injection in PHP?
Well as #Madara Uchiha said it's good to stop using mysql_* functions. But he gave no example. So i'll. Sorry for my english, i'm brazilian :)
PDO:
<?php
$loginemail = $_POST['loginemail'];
$password = $_POST['loginpass'];
// open a PDO connection within mysql driver
// PDO uses DSN string for connections they look like: "driver:options"
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
// DB username
$dbUser = 'dbuser';
// DB password
$dbPassword = 'dbpass';
// PDO is instantiable, so you need to create an object for each connection
$dbh = new PDO($dsn, $dbUser, $dbPassword);
// You must prepare your query, PDO uses a placeholder system for sanitize data and avoid injection, params can be passed using "?" or ":varname"
$stmt = $dbh->prepare('SELECT uid, full_name, score, status FROM users WHERE email= :email AND password= :password LIMIT 1');
// Binding a param called :email
$stmt->bindValue(':email', $loginemail);
// Binding a param called :password
$stmt->bindValue(':password', $password);
// Execute the PDOStatement
$stmt->execute();
// Then fetch result
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// ALSO, you can fetch only one row using
$firstRow = $stmt->fetch(PDO::FETCH_ASSOC);
For one thing, you're escaping $loginemail, but then using the unescaped version in the query.
One thing you might want to consider, if possible, is using PDO and prepared statements to simplify escaping and parameter substitution.
You use mysql_real_escape_string on $loginemail to arrive at $loginemail_sql, but you then stop short of using it in your query:
$q = 'SELECT uid, full_name, score, status FROM users WHERE email="'.$loginemail.'" AND password="'.$password_sql.'" LIMIT 1';
I'm no expert on sql injection but escaping the strings is a great first step which should be enough although there is probably more that can be done.
My biggest issue with your code is that you seem to store the passwords in plan text in the database. You should use a salted hash to store them. Check this out for more information:
Secure hash and salt for PHP passwords
Also you made a mistake in this code:
$q = 'SELECT uid, full_name, score, status FROM users WHERE email="'.$loginemail.'" AND password="'.$password_sql.'" LIMIT 1';
Should be:
$q = 'SELECT uid, full_name, score, status FROM users WHERE email="'.$loginemail_sql.'" AND password="'.$password_sql.'" LIMIT 1';
I have used tutorials, examples and looked at numerous other questions about my problem and I still can't get it to work, I am relatively new to PHP and do not have any understanding of PDO. I have changed my code to mysqli rather than mysql to get rid of the depreciated code my university gave me but they have been less than helpful during this situation.
If anyone could shed some light onto this issue for me I would be very grateful.
Below are my code samples:
<?php /*connect to the db */
$link=mysqli_connect("dbhost","user","pass");
mysqli_select_db("db",$link);
/*checking connection*/
if ($link->connect_errno)
throw new exception(sprintf("Could not connect: %s", $link->connect_error));
session_start();
$insert_query="
INSERT INTO testone_tbl (age,hours,flexibility,fastpaced,retailexp,
workedus,conviction,permit,education)
VALUES ('$age','$hours','$flexibility','$fastpaced','$retailexp','$workedus',
'$conviction','$permit','$education');
INSERT INTO testtwo_tbl
(contribute,insales,initiative,success,alternatives,targets,
newthings,custfeed,incdevelop,standards,confident,stretch,
opportunities,polite,ideas,deadline,supported,duties)
VALUES ('$contribute','$insales','$initiative',
'$success','$alternatives','$targets','$newthings',
'$custfeed','$incdevelop','$standards','$confident','$stretch',
'$opportunities','$polite','$ideas','$deadline','$supported','$duties')";
/*execute multi_query*/
mysqli_multi_query ($link, $insert_query);/*error1*/
/*close connection*/
if(!$link>connect_errno) $link->close(); /*error2*/
?>
The data is both from the form this is written in (the last form) and sessions from the previous forms. However I am also getting this error: Warning: mysqli_multi_query() expects parameter 1 to be mysqli and Warning: mysqli_close() expects parameter 1 to be mysqliand I have been stuck on this the past few days! Thank you in advance.
You should first check with your web host if they have enabled multi-SQL-queries.
Some web hosts only allow single-SQL queries to help prevent against injection attacks.
If, however, you want to multi-insert to the same table, you could do it like this:
INSERT INTO tbl_name (col1, col2)
VALUES ('?', '?'),
('?', '?'),
('?', '?'); # inserts 3 records to the same table in one query
Also, if you do have PDO available to you, use it!
With a PDO object, your queries will be safer by using prepared statements. Example:
$db = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$data = array($col1, $col2, $col3);
$sql = "INSERT INTO tbl_name (col1, col2, col3) VALUES ('?', '?', '?');";
$query = $db->prepare($sql); # prepares the sql statement
$query->execute($data); #binds the array of data to the ?
#question mark parameters, and executes.
If you create a database abstraction layer, you could change the database connection mode without having to rewrite your code which executes your queries.
Also, do you have a reason not to loop and query? Example:
$sql_array = array("INSERT INTO tbl_one(col1) VALUES '?';",
"INSERT INTO tbl_two(col3) VALUES '?';");
function performAll($sql_array) {
# execute all of the queries
}
It has occured to me that you may be using some function to access your database connection. Now that is not a problem, unless you actually try to access the database connection from within a function (in case you have not told us). Example:
$db = new PDO("...", $user, $pass);
$query = $db->prepare($sql); # works fine
function executeQuery($sql) {
$query = $db->prepare($sql); # error: $db is not defined
# within the scope of this function
...
}
To get around this, use the global keyword in PHP. Example:
$db = new PDO("...", $user, $pass);
function executeQuery($sql) {
global $db; # use $db in the global scope
$query = $db->prepare($sql); # works fine
...
}
From the warnings it is clear that $link is not a mysqli object. Either you did not connect, or at some point you reassigned $link to something else.
You also need to check your connection immediately after your connect. An intermediate action on the link (in this case, mysqli_select_db) will clear any errors that were set.
You should also not mix-and-match object-oriented and procedural style interfaces for mysqli. The object-oriented style is much clearer, but if it's too difficult to change the existing code then stick to the procedural style.
Connect like this instead:
$link = mysqli_connect("dbhost","user","pass", "db"); // no need for an extra db select
if (mysqli_connect_errno()) {
throw new Exception("Could not connect: ".mysqli_connect_error());
}
Also, I hope this isn't your real code, because it is wide open to mysql injection attacks. Consider dropping the use of multi-queries entirely and using prepared statements with placeholders.
I have created my own mvc pattern based on the codeigniter framework style. My problem now is that i want to prevent from SQL injection, and for that purpose i would like to use mysql_real_escape_string(). But for when i use it, it keeps erroring since it apparently don't have a the "link/source" to the database?
I get the php error:
Warning: mysql_real_escape_string(): Can't connect to local MySQL
server through socket '/var/lib/mysql/mysql.sock' (2) in
/hsphere/local/home/../dev/simple_blog/models/users_model.php on line
8
Warning: mysql_real_escape_string(): A link to the server could not be
established in
/hsphere/local/home/../dev/simple_blog/models/users_model.php on line
8
I don't quite understand why though, since i can get stuff in and out of my DB but for some reason i can't protect it???
Here is my function giving the error
public function getUserByName($username){
$username = mysql_real_escape_string($username);
$sql = "SELECT * FROM ".$this->db_table." WHERE username='".$username."' LIMIT 1";
$q = $this->db->query($sql);
if($q->rowCount() > 0){
foreach($q->fetch() as $key => $row){
$data[$key] = $row;
}
return $data;
}
}
As you can see I use mysql_real_escape_string() at the top, and then later on, do query stuff. Anyone know why this don't work and if yes, how would i fix it?
NOTE: Im not a shark to PDO, and $this->db is the PDO class.
To use mysql_real_escape_string you'll need to connect to the database server first, using the MySQL Functions, which you probably don't have done.
You are mixing up two completely different PHP extensions: mysql and PDO!
Also, you don't need to escape strings, when using PDO prepared statements, that's done via PDO for you.
An example using PDO:
$userDataStmt = $this->database->prepare('SELECT * FROM ' . $this->db_table . ' WHERE username = :username LIMIT 1');
$userDataStmt->bindValue(':username', $username);
$userDataStmt->execute();
if(!$userDataStmt->rowCount() <= 0)
{
$result = $userDataStmt->fetchAll();
}
Don't do this. PDO will escape for you if you use prepared statements:
$stmt = $this->db->prepare("SELECT * FROM ".$this->db_table." WHERE username=:user LIMIT 1";
$stmt->bind(':user', $username);
$stmt->execute();
PDO uses (I believe) the mysqli library behind the scenes. mysql_escape_real_string uses the mysql library (note the lack of an i). Both libraries have completely independent connection pools, so unless you establish a throw-away link with mysql_connect(), you cannot use mysql_real_escape_string anyways, as it requires an active DB connection.
The PDO version is PDO::quote(). See http://php.net/manual/en/pdo.quote.php
So in your case it would be
$username = $this->db->quote($username);
However, most recommend using PDO prepared statements for avoiding SQL Injection in PDO. See
http://php.net/manual/en/pdo.prepared-statements.php
Generally I connect and retrieve data using the standard way (error checking removed for simplicity):
$db = mysql_select_db("dbname", mysql_connect("host","username","passord"));
$items = mysql_query("SELECT * FROM $db");
while($item = mysql_fetch_array($items)) {
my_function($item[rowname]);
}
Where my_function does some useful things witht that particular row.
What is the equivalent code using objects?
Since version 5.1, PHP is shipped with the PDO driver, which gives a class for prepared statements.
$dbh = new PDO("mysql:host=$hostname;dbname=$db", $username, $password); //connect to the database
//each :keyword represents a parameter or value to be bound later
$query= $dbh->prepare('SELECT * FROM users WHERE id = :id AND password = :pass');
# Variables are set here.
$query->bindParam(':id', $id); // this is a pass by reference
$query->bindValue(':pass', $pass); // this is a pass by value
$query->execute(); // query is run
// to get all the data at once
$res = $query->fetchall();
print_r($res);
see PDO driver at php.net
Note that this way (with prepared statements) will automatically escape all that needs to be and is one of the safest ways to execute mysql queries, as long as you use binbParam or bindValue.
There is also the mysqli extension to do a similar task, but I personally find PDO to be cleaner.
What going this whole way around and using all these steps gives you is possibly a better solution than anything else when it comes to PHP.
You can then use $query->fetchobject to retrieve your data as an object.
You can use the mysql_fetch_object()
http://is2.php.net/manual/en/function.mysql-fetch-object.php