I have one question
In my aplication all SQL Queries are with PDO. For Example Notes:
<?php
include "config.php";
$User_Check = $_SESSION['Login_User'];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
Notes = $_POST["Notes"];
try {
$sql = $conn->prepare('UPDATE Accounts SET Notes = :Notes WHERE Username = :User_Check');
$sql->execute(array('Notes' => $Notes, 'User_Check' => $User_Check));
header('Location: home.php?Message=Uspesno');
} catch(PDOException $e) {
header('Location: home.php?Message=Greska');
}
}
$sql = $conn->prepare('SELECT Notes FROM Accounts WHERE Username = :User_Check');
$sql->execute(array('User_Check' => $User_Check));
$row = $sql->fetch(PDO::FETCH_ASSOC);
$SelectNotes = $row['Notes'];
conn = null;
?>
Now I wnat to know how much is this way secure? Can anyone do SQL Injection? And do I need to add some other form of protection? Thanks!
As long as the string passed to prepare() is static (i.e. does not contain any variables), you should be safe from SQL injections.
The important part is separating user input from your SQL statements, and you do that by having the SQL passed to prepare() and the user input to execute().
Similar question: How does a PreparedStatement avoid or prevent SQL injection?
(The question is tagged java, but neither the question nor the answer are specific to Java.)
With PDO you don't need to escape string for prevent sql injection because prepare fx do this job.
So yes your requests are secure.
Related
I want to protect some content in my site with a password and I am thinking in using this php script
Do you think is a good way to go?
Do you know something better for this task or a way to improve ( if needed) thin one ?
The code to load the content from the database is :
<?php
error_reporting(0);
include("config.php");
if (!isset($_REQUEST["p"])) {
echo 'document.write("<div id=\"protected_'.intval($_REQUEST["id"]).'\">");';
echo 'document.write("<form onsubmit=\'return LoadContent(\"'.intval($_REQUEST["id"]).'\",\"protected_'.intval($_REQUEST["id"]).'\",document.getElementById(\"pass_'.intval($_REQUEST["id"]).'\").value); return false;\'\"><input type=\'password\' size=\'30\' placeholder=\'Content is protected! Enter password.\' id=\"pass_'.intval($_REQUEST["id"]).'\"></form>");';
echo 'document.write("</div>");';
} else {
$sql = "SELECT * FROM ".$SETTINGS["data_table"]." WHERE `id`='".intval($_REQUEST["id"])."' AND password='".mysql_real_escape_string($_REQUEST["p"])."'";
$sql_result = mysql_query ($sql, $connection ) or die ('request "Could not execute SQL query" '.$sql);
if (mysql_num_rows($sql_result)==1) {
$row = mysql_fetch_assoc($sql_result);
echo $row["content"];
} else {
echo 'Wrong password';
}
}
?>
As I said in comments, you shouldn't be spending anymore time with what you downloaded since it's old and not safe.
You may be saving passwords in plain text which is definitely not a good idea.
It's time to step into the 21st century.
The mysql_ API is in deprecation and has been deleted from PHP 7.0 entirely.
You are best to use a prepared statement and password_hash() or the compatibility pack.
Here are a few references:
http://php.net/manual/en/book.password.php
http://php.net/manual/en/function.password-hash.php
http://php.net/manual/en/pdo.prepared-statements.php
http://php.net/manual/en/mysqli.quickstart.prepared-statements.php
N.B. The use of mysql_real_escape_string() does not fully guarantee protection against a possible SQL injection.
Consult the following Q&A on the subject:
SQL injection that gets around mysql_real_escape_string()
Here is a piece of code pulled from one or ircmaxell's answers which uses a (PDO) prepared statement and password_hash().
Pulled from: https://stackoverflow.com/a/29778421/1415724
Just use a library. Seriously. They exist for a reason.
PHP 5.5+: use password_hash()
PHP 5.3.7+: use password-compat (a compatibility pack for above)
All others: use phpass
Don't do it yourself. If you're creating your own salt, YOU'RE DOING IT WRONG. You should be using a library that handles that for you.
$dbh = new PDO(...);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $dbh->prepare("insert into users set username=?, email=?, password=?");
$stmt->execute([$username, $email, $hash]);
And on login:
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $dbh->prepare($sql);
$result = $stmt->execute([$_POST['username']]);
$users = $result->fetchAll();
if (isset($users[0]) {
if (password_verify($_POST['password'], $users[0]->password) {
// valid login
} else {
// invalid password
}
} else {
// invalid username
}
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Can I mix MySQL APIs in PHP?
(4 answers)
Closed 3 years ago.
I am very new to mysqli earlier i am writing queries in mysql but mysqli is more advanced so, i am first time using it.
Below is my php code.
function clean($str) {
$str = #trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return mysql_real_escape_string($str);
}
$email = clean($_POST['email']);
$password = clean($_POST['password']);
//$password =md5($password);
if(empty($res['errors'])) {
$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
if($result->num_rows == 1){
$res['success'] = true;
}
else{
array_push($res['errors'], 'Invalid login details');
$res['success'] = false;
}
}else{
$res['success'] = false;
}
echo json_encode($res);
}
clean function is not working as expected because sql queries return false if i enter username and password correct.
So, it seems like this is not valid in mysqli case.
I checked this link PHP MySQLI Prevent SQL Injection and got to know that we have to prepare query.
I can see there is an example but i am not able to understand how to prepare/bind if i have to use two or more form data.
Thanks for your time.
Updated code
$result = $mysqli->prepare("SELECT uid FROM users where email=:email and password = :password");
$result->execute([
':email' => $email,
':password' => $password]);
//$result->execute();
if($result->num_rows == 1){
//if(mysqli_num_rows($result) === 1) {
$res['success'] = true;
}
else{
array_push($res['errors'], 'Invalid login details');
$res['success'] = false;
}
As already stated in comments, you need to be consistent with your API choice. You can't mix APIs in PHP.
You started out with mysqli_*, so I'll continue with that. You had some mysql_* and PDO in there, and it might not be a bad idea to use PDO over mysqli_* - but if your server supports mysqli_*, there is nothing wrong with using that. See Choosing an API and decide for yourself (just stay away from mysql_*, it's outdated).
Using mysqli_*, you connect to the database like this (you didn't show your connection).
$mysqli = new mysqli("host", "username", "password", "database");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: (".$mysqli->connect_errno.") ".$mysqli->connect_error;
}
$mysqli->set_charset("utf8");
As for preventing SQL injection in it self, all you need is to use prepared statements. You can still clean or sanitize your data if there are some kind of values you don't want sitting in your tables - but that's kind of another discussion.
You also need to know if your passwords are hashed in the database. They really should be, and you should be using password_hash($password, $algorithm) and password_verify($password, $hash) if you're on PHP5.5 and above (if not, look into something like password_compat).
You need to be consistent with your hashes too, you can't insert it with md5 and selecting it with no hash. It all needs to be the same. Because if you are selecting an md5 hash, and comparing it to an unhashed string, they will be different, and the query fails.
I'm showing you an example of using password_verify(), so that means that the password stored in the database will also need to be stored with password_hash() (or your query fails).
if ($stmt = $mysqli->prepare("SELECT uid, password FROM users where email=?")) {
$stmt->bind_param("s", $_POST['email']); // Bind variable to the placeholder
$stmt->execute(); // Execute query
$stmt->bind_result($userID, $password); // Set the selected columns into the variables
$stmt->fetch(); // ...and fetch it
if ($stmt->num_rows) {
if (password_verify($_POST['password'], $password)) {
// Password was correct and matched the email!
} else {
// Password was incorrect...
}
} else {
// Accountname not found
}
}
This is just a basic example, but it will get you started. Never trust user input, use prepared statements.
You can bind more variables like so:
$stmt = $mysqli->prepare("SELECT uid FROM users where email= ? and password = ?");
$stmt->bind_param('ss', $email, $password);
/* execute prepared statement */
$stmt->execute();
As you can see, you can expand on the bind_param() function. You can also add different type of variables:
i corresponding variable has type integer
d corresponding variable has type double
s corresponding variable has type string
b corresponding variable is a blob and will be sent in packets
From: http://php.net/manual/en/mysqli-stmt.bind-param.php
First of all, I suggest you learn PDO instead of MySQLi, just because it supports more drivers.
Second, you use mysql_real_escape_string, as you might see, that is a MySQL function, not a MySQLi function.
So where you have:
$result = $mysqli->query("SELECT uid FROM users where email='$email' and password = '$password'");
You should do something like:
<?php
$stmt = $dbConnection->prepare("SELECT uid FROM users where email = :email AND password = :password");
try{
$stmt->execute([
':email' => $email,
':password' => $password
]);
}
catch(Exception $e){
echo $e->getMessage(); //Remove when putting online
}
if($stmt->num_rows){
$res['success'] = true;
}
?>
You're presently mixing MySQL APIs/functions with mysql_real_escape_string(), then num_rows and then a PDO binding method where email=:email and password = :password which seems to have been taken from another answer given for your question.
Those different functions do NOT intermix.
You must use the same one from connection to querying.
Consult: Can I mix MySQL APIs in PHP?
It looks like you're wanting to setup a login script. I suggest you use the following and pulled from one of ircmaxell's answers:
Pulled from https://stackoverflow.com/a/29778421/
Just use a library. Seriously. They exist for a reason.
PHP 5.5+: use password_hash()
PHP 5.3.7+: use password-compat (a compatibility pack for above)
All others: use phpass
Don't do it yourself. If you're creating your own salt, YOU'RE DOING IT WRONG. You should be using a library that handles that for you.
$dbh = new PDO(...);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $dbh->prepare("insert into users set username=?, email=?, password=?");
$stmt->execute([$username, $email, $hash]);
And on login:
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $dbh->prepare($sql);
$result = $stmt->execute([$_POST['username']]);
$users = $result->fetchAll();
if (isset($users[0]) {
if (password_verify($_POST['password'], $users[0]->password) {
// valid login
} else {
// invalid password
}
} else {
// invalid username
}
It's safer and uses a safe password hashing method, rather than what you seem to want to use is MD5 $password =md5($password); and is no longer considered safe to use now.
References:
PDO connection http://php.net/manual/en/pdo.connections.php
PDO error handling http://php.net/manual/en/pdo.error-handling.php
To check if a user exists, you can see one of my answers https://stackoverflow.com/a/22253579/1415724
http://php.net/manual/en/mysqli.error.php
http://php.net/manual/en/function.error-reporting.php
Sidenote: If you do go that route, remember to read the manuals and that your password column is long enough to hold the hash. Minimum length is 60, but they recommend 255.
It is also unclear if your HTML form does have name attributes for the POST arrays, so make sure the form is using a POST method.
http://php.net/manual/en/tutorial.forms.php
I believe I have given you enough information to get started.
What you must NOT do, is to use the above with your present code and simply patching it. You need to start over.
Add error reporting to the top of your file(s) which will help find errors.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// rest of your code
Sidenote: Displaying errors should only be done in staging, and never production.
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 9 years ago.
I have problem with my login form sql injection is working on it so how to stop it.
I am using mysql_real_escape_string but nothing changed
if(isset($_POST['submit-login'])) {
$user = $_POST['username'];
$pass = $_POST['password'];
$username = mysql_real_escape_string($user);
$password = mysql_real_escape_string($pass);
$usertool = new Usertool();
if($usertool->login($username, $password)){
//successful login, redirect them to a page
header("Location: index.php");
}else{
$error = "Incorrect username or password. Please try again.";
}
}
Here is usertool
class usertool {
public function login($username, $password) {
$hashedPassword = md5($password);
$result = mysql_query("SELECT * FROM tbl_user WHERE uname = '$username' OR eemail = '$username' AND password = '$hashedPassword'");
if (mysql_num_rows($result) == 1) {
$_SESSION["user"] = serialize(new User(mysql_fetch_assoc($result)));
$_SESSION["login_time"] = time();
$_SESSION["logged_in"] = 1;
return true;
} else {
return false;
}
}
it is not a classic SQL injection in your case but rather wrong SQL logic.
You need to add braces to your query:
SELECT * FROM tbl_user
WHERE (uname = '$username' OR eemail = '$username')
AND password = '$hashedPassword'"
In your original query the whole statement evaluates to true if entered username or email matches, and password not even being checked
And regarding SQL injections in general, to make your queries safe, you have to format your query parts according to these rules
Formatting have to be complete. mysql_real_escape_string does incomplete formatting alone: you ought to add apostrophes around whatever data you escaped using this function.
Formatting have to be adequate, means you can't format a number or an identifier with string formatting. EVery SQL literal require it's own distinct formatting.
Formatting have to be done as close to the query execution as possible.
Following these rules you'll be pretty safe from injection. And using prepared statements is the easiest way to follow them.
One don't need neither mysqli not PDO to use native prepared statements though - you can create your own variant. But nevertheless, you have to move mysql_real_escape_string as closer to the query execution as possible and always add apostrophes around the result.
I can't figure out why the password isn't matching when attempting to login after activation. I've trimmed down the pasted code below for ease of viewing.
Here is the relevant registration code:
$salt = substr(sha1(uniqid(rand(),true)),0,20);
$password_db = hash('sha256', $salt.$password1);
$sqlinfo = mysql_query("INSERT INTO db_1 (email, password, salt)
VALUES('$email1','$password_db','$salt')") or die(mysql_error());
Here is the correlating code for login:
$email = $_POST['email'];
$password = $_POST['password'];
$sqlinfo = mysql_query("SELECT * FROM db_1 WHERE email='$email' AND emailactiv='1'");
if($sqlinfo['password'] == hash('sha256', $sqlinfo['salt'].$password)){
while($row = mysql_fetch_array($sqlinfo)){
... }
else { ...
I've done several iterations thus far to no avail. Any insight would be much appreciated.
you code, currently, is vulnerable with SQL injection. One suggestion is to reformat your code using PDO or MySQLI.
Example of PDO:
<?php
$stmt = $dbh->prepare("SELECT * FROM db_1 WHERE email = ? AND emailactiv=? ");
$stmt->bindParam(1, $email);
$stmt->bindParam(2, 1);
$stmt->execute();
?>
you didn't fetch the row that's why it's not matching anything.
add this line before the IF statement:
$rowHere = mysql_fetch_row($sqlinfo);
and use $rowHere in your IF statement.
$email = $_POST['email'];
$password = $_POST['password'];
$sqlinfo = mysql_query("SELECT * FROM db_1 WHERE email='$email' AND emailactiv='1'");
//You need to first fetch data before using it.
while($result = mysql_fetch_array($sqlinfo)) {
//Now you can use the data
if($result['password'] == hash('sha256', $result['salt'].$password)){
//matched... login correct
} else {
//not matched.. invalid login
}
}
...
Hope it's self-explanatory.
You missed one very important line!
BTW, stop using mysql_* functions as they are deprecated, use PDO or mysqli_*
EDIT: Please try now. I thought it can only hold one value (for login purpose)
You need to use mysqli_fetch_array.
Also, mysql_* functions are deprecated. Use MySQLi or PDO instead.
And you need to 'sanitize your inputs' (a common phrase) to avoid SQL injection, using mysqli_real_escape_string or PDO.
See the Bobby Tables link as per the comments on the question.
I've done quite a bit of research and preparation with my code to try and prevent SQL injections, but I wanted to discuss something that I'm not quite sure about.
I understand the the mysqli_real_escape_string does not escape _ (underscore) and % (percent) characters. If I'm not using any LIKE clauses in my SQL statements, does this open me up to any risk?
Below is an example of one the instances I'm interested in talking about. Here is the login script I'm using. I want to make sure that I'm not opening myself up to any injection vulnerabilities here. Your insight and feedback would be greatly appreciated.
// Initiate login process if the mode is set to login
if ($_REQUEST['mode'] == "login") {
// Open shared database connection
$cxn = connectDb();
// Escape characters to help prevent a SQL injection attack
$username = mysqli_real_escape_string($cxn, $_POST['user']);
// Convert submitted password to hashed value using
// custom password hashing function
$password = custompwhash($_POST['pass']);
// Execute SQL statement to determine if the credentials provided
// match a valid user
$sql = "SELECT count(*) as countOK FROM user_def WHERE ".
"username = '$username' AND password = '$password'";
$result = mysqli_query($cxn,$sql);
$row = mysqli_fetch_array($result);
extract($row);
// If the username value submitted is null, throw and error
if ($username == "") {
die2("Please enter your username and try again.<br />");
failedloginalert($username);
}
// If the password value submitted is null, throw and error
else if ($password == "") {
die2("Please enter your password and try again.<br />");
failedloginalert($username);
}
// If the credenetials provided match a valid user in the database,
// initate login
else if ($countOK == '1') {
$sql2 = "INSERT INTO `user_activity` (`username`, `time`, `ip`)".
" VALUES ('$username', NOW(), '{$_SERVER['REMOTE_ADDR']}')";
$result2 = mysqli_query($cxn,$sql2);
$_SESSION['auth'] = 1;
$_SESSION['username'] = $username;
// If the user does not need to be directed to a specific page, direct to home.php
if (empty($_GET['page'])) {
die2("<span style='color:#000;'>You have successfully logged in. <br /><br />
Please click here if you are not automatically redirected.</span>
<meta http-equiv='refresh' content='0;url=home.php'/>");
die();
}
// Otherwise, if the user does need to be directed to a specific page, direct to the requested page
else {
$loginredirectpage = $_GET['page'];
die2("<span style='color:#000;'>You have successfully logged in. <br /><br />
Please click here if you are not automatically redirected.</span>
<meta http-equiv='refresh' content='0;url=".$loginredirectpage."'/>");
die();
}
}
// Since the credenetials provided do not match a valid user in the database, throw an error
else {
die2("The username or password you entered is invalid. Please try again. <br/><br/>If the problem persists, reset your password.");
failedloginalert($username);
}
}
Start using PDO and Prepared Statements and your issues with SQL injection will go away.
If I'm not using any LIKE clauses in
my SQL statements, does this open me
up to any risk?
No it doesn't. = just gives you more exact answer than LIKE
SQL Injection is more to do with quote sign ' " since the technique tries to append on the query string. So I'd say % and _ won't open up any security risk if you filtered all the possible quotes. In this case, mysqli_real_escape_string() can help.
PDO is an abstraction layer that assists you with dealing with databases more efficiently. It can help you with SQL injection, but I don't recommend using it unless you're building something big that requires a lot of interaction with database.
So final my point is your code looks ok (: