I realize there are a lot of questions already about this. But my method isn't the same as theirs, so I wanted to know. I think I understand SQL, but I don't want to risk making a mistake in the future, so thanks for any help. (This is just a project I'm doing, not homework or anything important).
function checkLogin($username, $password) {
$username = strtolower($username);
connectToDatabase();
$result = mysql_query("SELECT * FROM `users` WHERE username='$username'");
$dbpassword = "";
while($row = mysql_fetch_array($result))
{
$rowuser = $row['username'];
if($username != $row['username']) continue;
$dbpassword = $row['password'];
}
if($dbpassword == "") {
return false;
}
$genpass = generatePassword($password);
return $genpass == $dbpassword;
}
So hit me with your best shot :)
And I don't think my method is as efficient as it could be. I don't understand php enough to understand what $row = mysql_fetch_array($result) is doing unfortunately.
Because you are taking an arbitrary string and placing it directly into an SQL statement, you are vulnerable to SQL injection.
( EDITED based on a comment below. )
The classic example of SQL injection is making a username such as the following:
Robert'); DROP TABLE users;--
Obligatory XKCD link
Explanation:
Given the "username" above, interpolation into your string results in:
SELECT * FROM `users` WHERE username='Robert'); DROP TABLE users;--'
The comment symbol -- at the end is required to "get rid" of your closing quote, because I just substituted one of mine to end your select statement so that I could inject a DROP TABLE statement.
As #sarnold pointed out, PHP's mysql_query only executes a the first query in the string, so the above example (known as query stacking) does not apply. The function is explained here: http://php.net/manual/en/function.mysql-query.php.
A better example can be found here. Here they use a username of
' OR 1 OR username = '
which interpolated becomes
SELECT * FROM `users` WHERE username='' OR 1 OR username = ''
and which would cause your application to retrieve all users.
The short answer is yes.
A perhaps more helpful answer is that you should never trust user input; prepared statements are the easiest way to protect against this, if you have PDO available. See PDO Prepared Statements
<?php
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE username=?");
if ($stmt->execute($username)) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
?>
The other answers are an excellent description of your problem, however, I think they both overlook the best solution: use PHP's PDO Prepared Statements for your queries.
$stmt = $dbh->prepare("SELECT * FROM users where username = ?");
if ($stmt->execute(array($username))) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
This is a small, simple example. There are more sophisticated ways of using PDO that might fit your application better.
When you use PDO prepared statements you never need to manually escape anything and so long as you use this slightly different style, you will never write an SQL injection vulnerability and you don't have to maintain two variables per underlying "data" -- one sanitized, one as the user supplied it -- because only one is ever required.
I would say yes it is open to SQL injection.
This is because you are taking user input in the form of $username and putting it into your SQL statement without making sure it is clean.
This is a function that I like to use in my applications for the purpose of cleaning strings:
function escape($data) {
$magicQuotes = get_magic_quotes_gpc();
if(function_exists('mysql_real_escape_string')) {
if($magicQuotes) {
$data = stripslashes($data);
}
$data = mysql_real_escape_string($data);
}
else {
if(!$magicQuotes) {
$data = addslashes($data);
}
}
return $data;
}
Then you can use it like this:
$username = escape(strtolower($username));
connectToDatabase();
$result = mysql_query("SELECT * FROM `users` WHERE username='$username'");
Related
I have a MySQL statement that won't work for me. I've checked several parts of the code but it keeps returning null as the result. I've also tried replacing the WHERE enc_mail = AND enc_public_id=" to "WHERE 1" to check if it was a problem with the variables, but it is not. I did not get errors either.
$connect_db = mysqli_connect("myhost","my username","my password","my db");
$mail_id = crypto(mysqli_real_escape_string($connect_db,htmlspecialchars($_GET['em'])),'e');
$public_id = mysqli_real_escape_string($connect_db,htmlspecialchars($_GET['public']));
$active_true = true;
$check = $connect_db->prepare("SELECT active FROM enc_data WHERE enc_mail=? AND enc_pub_id=?");
$check->bind_param("ss", $mail_id, $public_id);
$active = $check->execute();
if($active[0]=="" ){
//It goes here once the code is run
}
You need to apply bind_result and then fetch
Also there is absolutely no reason to escape_string when using prepared statements as #GrumpyCrouton said
i would recommend you switch to PDO as it is more straightforward
I agree with #Akintunde that you should NOT use escaping and htmlspecialchars on query parameters. Escaping is redundant when you use query parameters. htmlspecialchars is just when you output content to HTML, not for input to SQL.
You don't necessarily have to use bind_result() for a mysqli query. You can get a result object from the prepared statement, and then use fetch methods on the result object to get successive rows.
Here's how I would write your code:
// makes mysqli throw exceptions if errors occur
mysqli_report(MYSQLI_REPORT_STRICT);
$connect_db = new mysqli("myhost", "my username", "my password", "my db");
$mail_id = $_GET['em'];
$public_id = $_GET['public'];
$active_true = true;
$sql = "SELECT active FROM enc_data WHERE enc_mail=? AND enc_pub_id=?";
$stmt = $connect_db->prepare($sql);
$stmt->bind_param("ss", $mail_id, $public_id);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
if($row["active"]=="" ){
//It goes here once the code is run
}
}
But in fact I would prefer to use PDO instead of mysqli, so I guess that's not really how I would write the OP's code. :-)
Im new to database and i have written a LOT of PHP code that accesses a database using MySQL.
I didnt take into account SQL injection attacks so i have to re-write all that PHP code to use mysql prepared statements.
After looking at videos on how to used prepared SQL statements, to perform just ONE SQL command requires a whole lot of "prepared" statements. My existing code has lots of different SQL statements all over the place, it would be a nightmare to change all that code to pack and unpack all the required preparation for each "prepared" statement command.
Is there some kind of wrapper i can use to prevent turning one line of regular SQL into 6 or 7 lines of prepared statements?
For example use to do this line line of SQL
SELECT * from users where userid=10
needs many more lines of prepared SQL statements, especially if there are lots of other SQL statements too it now becomes very complex.
Is there was some sort of one line wrapper that i can call that accepts the template SQL string, plus the parameters, which also executes the command and returns the result in just one line of wrapper for different types of MYSQL statements it would be great and the code would be much less confusing looking and error prone.
For example
$users=WrapAndExecute($db,"SELECT * from users where userid=?","s",$userid);
$data=WrapAndExecute($db,"UPDATE table SET username=?,city=?","ss",$name,$city);
$result=WrapAndExecute($db,"DELETE from table where id=?","s",$userid);
$result=WrapAndExecute($db,"INSERT into ? (name,address) VALUES(?,?)","ss","users",$name,$address);
Each of those lines above would create a prepared statement template, do the bind, execute it and return the result that a regular MYSQL statement would. This would create minimal impact on existing code.
Anybody knows how to do this or if some easy php library or class already exists to do this, that i can just import and start using it?
Thanks
You don't need to change a query to a prepared statement if it has no PHP variables in it. If it has just constant expressions, it's safe from SQL injection.
$sql = "SELECT * from users where userid=10"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
You don't need to change a query that contains PHP variables, as long as the value of that variable is a constant specified in your code. If it doesn't take its value from any external source, it's safe.
$uid = 10;
$sql = "SELECT * from users where userid=$uid"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
You don't need to change a query that contains PHP variables, as long as you can filter the value to guarantee that it won't risk an SQL injection. A quick and easy way to do this is to cast it to an integer (if it's supposed to be an integer).
$uid = (int) $_GET['uid'];
$sql = "SELECT * from users where userid=$uid"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
That leaves cases where you are using "untrusted" values, which may have originated from user input, or reading a file, or even reading from the database. In those cases, parameters are the most reliable way to protect yourself. It's pretty easy:
$sql = "SELECT * from users where userid=?"; // Safe!
// two lines instead of the one line query()
$stmt = $pdo->prepare($sql);
$stmt->execute([$_GET['uid']]);
$data = $stmt->fetchAll();
In a subset of cases, you need one additional line of code than you would normally use.
So quit your whining! ;-)
Re your comment about doing prepared statements in mysqli.
The way they bind variables is harder to use than PDO. I don't like the examples given in http://php.net/manual/en/mysqli.prepare.php
Here's an easier way with mysqli:
$sql = "SELECT * from users where userid=?"; // Safe!
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('i', $_GET['uid']);
$stmt->execute();
$result = $stmt->get_result();
$data = $result->fetch_all();
I don't like the stuff they do in their examples with bind_result(), that's confusing and unnecessary. Just use get_result(). So with mysqli, you need two more lines of code than you would with PDO.
I've written query wrappers for mysqli that emulate the convenience of PDO's execute() function. It's a PITA to get an array mapped to the variable-arguments style of bind_param().
See the solution in my answers to https://stackoverflow.com/a/15933696/20860 or https://stackoverflow.com/a/7383439/20860
I were in the same boat, and I wrote such a wrapper that works exactly the way you want, save for it's being a class, not a function.
$user = $sdb->getRow("SELECT * from users where userid=?s", $userid);
$sdb->query("UPDATE table SET username=?s, city=?s", $name, $city);
$sdb->query("DELETE from table where id=?s", $userid);
$sdb->query("INSERT into ?n (name,address) VALUES(?s,?s)","users", $name, $address);
The above is a working code, as long as you have somewhere in your bootstrap file
$db = mysqli_connect(...);
...
require 'safemysql.class.php';
$sdb = new SafeMySQL('mysqli' => $db);
Note that none of the other suggestions could do anything like that.
Also note that if I were writing it today, I would have used PDO, as this class is duplicating a lot of functionality already exists in PDO.
Take a look at the PDO extension in PHP - http://php.net/manual/en/intro.pdo.php: it it secure against injections thanks to prepared statements; also, it allows you to connect to many different databases (e.g. MySQL, MSSQL, etc.).
You can then build your own wrapper as you wish to keep it clean; for example your own wrapper could be as follows:
(following example will return user rows as objects)
// connect to DB
$GLOBALS['default_db'] = new DB('localhost','db_name','username','password') ;
// Get users and output results
$query = new DBQuery('SELECT * FROM users WHERE userid = ?',array(10)) ;
var_dump($query -> results()) ;
var_dump($query -> num_rows()) ;
// DB connection
class DB {
public $connection;
public function __construct($host , $dbname , $username , $password) {
$this->connection = new \PDO('mysql:host=' . $host . ';dbname=' . $dbname , $username , $password);
}
}
// Wrapper
class DBQuery {
private $num_rows = 0;
private $results = array();
public function __construct($query , $params = null , $class_name = null , DB $db = null) {
if ( is_null($db) ) {
$db = $GLOBALS['default_db'];
}
$statement = $db->connection->prepare($query);
$statement->execute($params);
$errors = $statement->errorInfo();
if ( $errors[2] ) {
throw new \Exception($errors[2]);
}
$fetch_style = ($class_name ? \PDO::FETCH_CLASS : \PDO::FETCH_OBJ);
$this->results = $class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style);
$this->num_rows += $statement->rowCount();
while ( $statement->nextrowset() ) {
$this->results = array_merge($this->results,$class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style));
$this->num_rows += $statement->rowCount();
}
}
public function num_rows() {
return $this->num_rows;
}
public function results() {
return $this->results;
}
}
Since a key requirement seems to be that you can implement this with minimal impact on your current codebase, it would have been helpful if you had told us what interface you currently use for running your queries.
While you could use PDO:
that means an awful lot of work if you are not already using PDO
PDO exceptions are horrible
Assuming you are using procedural mysqli (and have a good reason not to use mysqli_prepare()) its not that hard to write something (not tested!):
function wrapAndExecute()
{
$args=func_get_args();
$db=array_shift($args);
$stmt=array_shift($args);
$stmt_parts=explode('?', $stmt);
if (count($args)+1!=count($stmt_parts)) {
trigger_error("Argument count does not match placeholder count");
return false;
}
$real_statement=array_shift($stmt_parts);
foreach ($args as $k=>$val) {
if (isnull($val)) {
$val='NULL';
} else if (!is_numeric($val)) {
$val="'" . mysqli_real_escape_string($db, $val) . "'";
}
$real_statement.=$val . array_shift($stmt_parts);
}
return mysqli_query($db, $real_statement);
}
Note that this does not handle IS [NOT] NULL nicely nor a literal '?' in the statement nor booleans (but these are trivial to fix).
When I call $mysqli->real_escape_string($str), I get back a safe string that I can use in a query. It has all special symbols that can lead to SQL Injection Attack escaped.
My question is - what is the correct way to use it in a query later? Do I single quote or double quote this safe string?
For example,
$safe_title = $mysqli->real_escape_string($_POST['safe_title']);
Now do I do this:
$r = $mysqli->query("SELECT * FROM articles WHERE title='$safe_title'");
Or this:
$r = $mysqli->query('SELECT * FROM articles WHERE title="'.$safe_title.'"');
Sometimes I do one, sometimes I do the other, but this one time when I did one of these and typed a bunch of garbage in input, I got an SQL query error somehow. I realized I'm doing it wrong and all my code is probably vulnerable. That's why I'm asking this question here.
You would do this:
$r = $mysqli->query("SELECT * FROM articles WHERE title='$safe_title'");
But as #JordiKroon pointed out prepared statements are preferred.
$stmt = $mysqli->prepare("SELECT * FROM articles WHERE title=?");
$stmt->bind_param("s", $safe_title);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_array(MYSQLI_NUM))
{
foreach ($row as $r)
{
print "$r ";
}
print "\n";
}
I've a website that hacked today. Server logs returned something like this as hacker's tries:
www.site.com/notifications.php?PID=7&id=999999.9%20union%20all%20select%20%28select%20distinct%20concat%280x7e%2C0x27%2Cunhex%28Hex%28cast%28schema_name%20as%20char%29%29%29%2C0x27%2C0x7e%29%20from%20%60information_schema%60.schemata%20limit%201%2C1%29%2C0x31303235343830303536%2C0x31303235343830303536%2C0x31303235343830303536--
But I've used mysql_real_escape_string() in my code:
if (isset($_GET['id']) && $_GET['id'] != '') {
$id = mysql_real_escape_string($_GET['id']);
} else {
$id = '';
}
if ($id == '') {
$stmt = "SELECT * FROM tbln13 ORDER BY id DESC";
} else {
$stmt = "SELECT * FROM tbln13 WHERE id = $id";
}
$NewsResult = mysql_query($stmt) or die (mysql_error());
Why my website could not prevent this attack?
Because escape_string add slashes and such to quotes. You didn't have any quotes in your query, or the string they submitted.
Your query doesn't have a STRING in it, it appears to expect an int. If you expected an integer, you should have verified it was an int, or forced it to an int, before using it in a query. Escaping a value as a string, then using it as an int, won't work.
Switch to prepared statements in MySQLi or PDO.
The sql injected query looks like this
SELECT * FROM tbln13 WHERE id = 999999.9
union all select
(select distinct concat(0x7e,0x27,unhex(Hex(cast(schema_name as char))),0x27,0x7e)
from `information_schema`.schemata
limit 1,1),
0x31303235343830303536, 0x31303235343830303536, 0x31303235343830303536--
as you see, you were injected because you have just allowed this!
You expected a number but you didn't check for it! So you got the number and something more.
You should have checked the $id variable for what you expected, which is the number. This is what I would use:
if (!preg_match('/^\d+$/', $id))
die("ERROR: invalid id"); // error, don't continue
Use prepared statements, that will, in most cases, prevent SQL injections.
A simple and comprehensible guide to prepared statements can be found in this website:
Bobby Tables
More over you should stop using MYSQL, it's outdated and will be removed in future implementations. Use MySQLi or PDO instead.
Because your escaped variable is not a string therefore it is not inside quotes in your query. If you want a quick fix you can change your query to:
$stmt = "SELECT * FROM tbln13 WHERE id = '$id'";
It is not standard use for numeric comparison but should work.
As mentioned by others, you should ditch deprecated mysql_* functions and instead used prepared statements via mysqli or PDO.
Even then you should also be validating your input, because just using prepared statements will not help you identify whether you have input values that are valid. You would ideally make sure all your input is valid before even attempting a prepared statement. In this case, this validation could be as simple as this:
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if (false === $id) {
// you do not have a valid integer value passed. Do something.
} else {
// continue with your prepared statement
}
I want to get some data from sql server using php but the sql doesn't seem reading the php variable
<?php
$q = $_POST["fl"];
echo $foo;
if ($_POST["fl"] == true);
{
$conn1=odbc_connect('SQLDB','','');
if (!$conn1)
{exit("Connection Failed: " . $conn1);}
$sql1= "SELECT * FROM dbo.Audit WHERE Details = '$q'";
$rs1=odbc_exec($conn1,$sql1);
if (!$rs1)
{exit("Error in SQL");}
while (odbc_fetch_row($rs1))
The best way to do that is:
$stmt = odbc_prepare($conn1, "SELECT * FROM dbo.Audit WHERE Details = ?");
$success = odbc_execute($stmt, array(PDO::quote($_POST["fl"])));
You're testing for the presence of your $q incorrectly. You should have something more like:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['fl'])) {
$q = $_POST['fl'];
$sql = "SELECT ... WHERE Details = '$q'":
etc..
} else {
die("No fl value was passed");
}
}
Also note that I have not corrected your SQL injection vulnerability. Your code, even if it was working, is just begging to get your database trashed.
To avoid the apparent downvote party I'm not going to post any SQL, but yadda yadda SQL injection, yadda yadda input validation, etc.
Your problem is:
if ($_POST["fl"] == true); //<-- this semicolon, get rid of it
{
$conn1=odbc_connect('SQLDB','','');
Aside from that your code is syntactically fine, though you should be using if(isset($_POST['f1'])) in place of if ($_POST["fl"] == true).
You can use PDO object for making a select query to the database....
$q = $_POST['fl'];
$pdo=new PDO('mysql:host=localhost;dbname=databasename','username','password');
$query="select * from dbo.Audit WHERE Details = :q;";
$result=$pdo->prepare($query);
$result->bindValue(':q',$q);
$result->execute();
First things first, please sanitize the post before you put it inside an SQL query.
You have to use htmlentities or even better, PDO.
Second, you can try this if your current one doesn't work:
$sql1 = "SELECT * FROM dbo.Audit WHERE Details = '".$q."'";