I'm currently setting up php scripts for my app and I'm abit clueless about how to obtain a level of safety to prevent injections to the sql server.
there are a few scripts that receive input from the app and not from the user directly such as content browsing and content rating, altho it is eventually an input.
the script that does receive user direct input as "name" and "creator name" is this :
$utc_str = gmdate("M d Y H:i:s", time());
$TIMESTAMP = strtotime($utc_str);
$DATA = $_POST['DATA'];
$NAME = $_POST['NAME'];
$CREATOR = $_POST['CREATOR'];
if(strlen($NAME) > 15 || strlen($CREATOR) > 15) exit("Error 2");
$stmt = $connect->prepare("INSERT INTO `ugcl` (`DATA`,`NAME`,`CREATOR`,`CREATEDSTAMP`)
VALUES (?, ?, ?, ". $TIMESTAMP .")");
$stmt->bind_param("sss", $DATA, $NAME, $CREATOR);
if($stmt->execute())
{
echo "Successs";
}
else
{
echo "Error";
}
should i use bind params in all of the scripts that receive input?
is there any thing else that is recommended?
Yes you should use PREPARED STATEMENTS in php whenever making an input or output.
Always bind the parameters so that the server always knows what datatype to expect. This will make sure you've an added security to your application. Everything you enter should be used as a ? in the original statement and bind the variables with the appropriate datatypes.
You're directly entering the $TIMESTAMP which I won't personally recommend either. Running that through a bind_param wont take much effort.
Also, always close your connections with a $stmt->close() and $conn->close() once a query statement is completed. If you have multiple queries in a page, start the connection at the beginning of the queries and end it after all the queries are done.
Also, another note on security- always validate and sanitize user inputs first. Never trust user data. Never take them to be valid in the first place.
Edit: Also consider using PDO for database interaction.
Related
I've been troubleshooting this for two days and am at a loss.
I have this code where a SELECT query is run in one table and then if the token matches the one in the db it runs a INSERT query on a different table.
The SELECT query works just fine however, no matter which way I execute the INSERT query it always throws back a db is locked error.
I have tried executing the INSERT query with
exec()
query()
querySingle()
prepare() / bindParam() / execute()
Understanding that it could also have not finished the SELECT query I have also tried
setting the busyTimeout() time out to 5 seconds
Adding a sleep() in between the SELECT and INSERT queries
Closing and reopening the file
Using object oriented and closing and reopening the file in each function
Code
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
$db = new SQLite3('../grasscar_test.sqlite');
$token = $_POST['token'];
$res = $db->query("SELECT key FROM admin WHERE key = '$token'");
if ($res === FALSE){
echo '{"correct":"no"}';
}
while ($row = $res->fetchArray()){
$key = "{$row['key']}";
if ($key === $token){
//generate token
$cookieog = openssl_random_pseudo_bytes(32);
$cookie = bin2hex($cookieog);
//respond with token
echo '{"correct":"yes","token":"'.$cookie.'"}';
//get expiary time
$expiary = date("Y-m-d H:i:s", strtotime("+30 minutes"));
//add token and expiary date
$insert = $db->exec("INSERT INTO admin_cookie (cookie, expiary) VALUES ('$cookie', '$expiary')"); //This is the line throwing the error
print_r($insert);
}else{
echo '{"correct":"no"}';
}
}
$db->close();
?>
Warning
Your query is insecure & vulnterable to SQL Injection. See https://www.php.net/manual/en/sqlite3.prepare.php for how to do prepared statements or How can I prevent SQL injection in PHP?. $token needs to be bound, rather than directly put into the query string. Or you can escape the $token, but binding is much better IMO & ends up with cleaner code IMO.
Option 1
You could get the whole SELECT into an array, then explicitly close the open connection or cursor for the SELECT:
while ($row = $res->fetchArray()){
$resultsArray[] = $row;
}
$res->finalize();
foreach ($resultsArray as $row){
// your existing code
}
https://www.php.net/manual/en/sqlite3result.finalize.php
Option 2: Not Cryptographically secure random
You could alternatively put it all into a single query, if you don't need to print everything to screen, and you don't need cryptographically secure randomness. I couldn't find anything to produce crypto-randomness in sqlite (in a couple minutes of searching).
INSERT INTO admin_cookie (cookie, expiary)
SELECT hex(randomblob(32)), datetime('now', '+30 minutes')
https://sqlite.org/lang_datefunc.html
https://sqlite.org/lang_corefunc.html#randomblob
P.S.
You could try pdo. Pretty sure it supports sqlite, & you might like the interface better.
Instead of INSERTing in a loop, build a VALUES list, then execute a single INSERT statement. However, that's a slight pain when using bound paramaters (though entirely doable by incrementing the name of the placeholder & building a bind array as you build the VALUES list)
You probably DO need cryptographically secure randomness, so Option 2 is not ideal. I'm not a security expert by any means.
So this is my first attempt at converting some previously used code to prepared statements in order to prevent SQL injections. I'm using mysqli procedural, as follows,
Old query:
mysqli_query($con,"UPDATE ulogs SET invalid_hits = invalid_hits + 1 WHERE user_id = $link AND date = '$date'");
if(mysqli_affected_rows($con) < 1) {
mysqli_query($con,"INSERT INTO ulogs(user_id,date,invalid_hits,unique_hits,non_unique_hits,earned,ref_earned,bonus) VALUES ('$link','$date',1,0,0,0,0,0)");
}
Prepared query:
$q1 = "UPDATE ulogs SET invalid_hits = invalid_hits + 1 WHERE user_id =? AND date =?";
$qa1 = "INSERT INTO ulogs (user_id,date,invalid_hits,unique_hits,non_unique_hits,earned,ref_earned,bonus) VALUES (?,?,1,0,0,0,0,0)";
if ($stmt = mysqli_prepare($con, $q1)) {
mysqli_stmt_bind_param($stmt,"is", $link, $date);
mysqli_stmt_execute($stmt);
$ucheck = mysqli_stmt_affected_rows($stmt);
mysqli_stmt_close($stmt);
}
if ($ucheck < 1) {
if ($stmt = mysqli_prepare($con, $qa1)) {
mysqli_stmt_bind_param($stmt,"is", $link, $date);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
}
}
mysqli_close($con);
exit();
And many more which get triggered under different circumstances but basically the same UPDATE first, then INSERT if nothing was updated.
Apparently it worked as I based my queries on examples from php.net. Apparently...
Because, after about 1 hour later, Apache was returning 500 server errors with php timing out while waiting for data from the db. Whereas with non-prepared queries such thing never happened.
So question is: have I done something wrong here? $stmt was being closed every time, along with $con so I don't know what may have caused the db to hang. Note that the server load did not went up either. It was just php-fpm reaching max clients due processes waiting for the db.
While I still haven't found WHY my original code for prepared statements ended up crashing the DB few hours later, I did eventually find a SIMPLE and WORKING out-of-the-box alternative by using a popular class made especially for this purpose: https://github.com/colshrapnel/safemysql
This class is pretty straightforward so even a newbie like myself was able to implement it using the examples found on their Github page.
Special thanks goes out to #YourCommonSense for pointing me in the right direction.
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 6 years ago.
This is my basic code of a login.php. It contains a basic $_POST method to get the values from form. Someone said me it is insecure. It is prone to SQl Injection. I dont want to make it complex. Please tell me how to make it more secure by simple methods.
<?php
session_start();
require('connection.php');
$email=$_POST['username'];
$password=$_POST['password'];
//variables in query will be in single quotes.
$query="select * from user_info where email='$email' and password = '$password' ";
//fetching result
if($result=$con->query($query))
{
$row =$result->fetch_array(MYSQLI_NUM);
if($row>0)
{
//creating session variables
$_SESSION['loggedIn']=true;
$_SESSION['email']=$row[1];
$_SESSION['id']=$row[0];
header("Location:profile.php");
}
else
{
echo "flag1";
header("Location:index.php?loginerror=invalid login");
}
}
else {
echo "Failed";
}
?>
Dangers for the database: SQL-Injections
Those are the dangers when using input data from the user to operate with the database. Some input might cause the sql-syntax to break, the database will get a badly formatted statement and run onto an error (if it was unintentionally) or even execute statements it was not supposed to (sql injection).
A possibility for unintended syntax breaking would be to ask the user for his name and he inserts something like O'Connor.
Way to treat them: How can I prevent SQL injection in PHP? tldr: prepared statements are king.
(+) I would guess most ORMs or abstraction layers between your application and your database would solve this problem on their own, should inform yourself about it tho - better safe than sorry.
Dangers for the frontend: XSS attacks
A user uses your form to input some JavaScript. When you are displaying it on your website it will get parsed and executed.
Way to treat them: use htmlentities() when displaying userdata.
(+) There are libraries like htmlpurifier which allow you to only sanitize specific elements. So if you want to let your users use HTML for a blogpost or whatever, you can a library like this which will allow certain elements and defuse the dangerous stuff.
Dangers for the filesystem: shell-injections (?)
When you are for some reason using user input to do something on your filesystem (creating a directory with the same name as the username or whatever..) someone could do the same as in sql - break your syntax and inject some commands.
Way to treat them: escapeshellcmd + Don't use user input for something like directory or filenames on your server! Put the names of the user into the database, use the id or a hash or something you generated as the filename.
Cross-Site-Request-Forgery:
Doesn't directly involve the data given to you, but I am adding it because it is something you should know about: Understanding CSRF
Remember: Treat the data based on the context in which you are using it!
There are different places for attacks and each one uses other weaknesses. So there is not the one solution to fix all the problems.
You just have to consider the context in which you are handling the data - are you saving it or displaying it.
When displaying it you want to take care of the JavaScript that might be hidden in the data so you want to escape the charakters which are special in html.
When saving data you worry only about the charakters which might break the syntax of your sql. Your database knows best which charakters to escape and how to treat which data, so just use the escaping and quoting functions provided by your database-api OR BETTER: Prepared Statements (just use them always when interacting with the database + userdata) as they work differently then normal queries and you don't have to think about escaping and quoting.
I would use PDO's. A very basic example;
<?php
session_start();
require('connection.php');
$sError = "";
$sEmail = "";
$sPassword = "";
if(isset($_POST['Login'])){
if(isset($_POST['username'])) $sEmail = $_POST['username'];
if(isset($_POST['password'])) $sPassword = $_POST['password'];
if($sPassword != '' && $sEmail == ''){
// create an instance of the connection
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );
// prepare the sql
$sSQL = "SELECT Email, Id from user_info where email=:email and password = :password ";
$st = $conn->prepare( $sSQL );
// bind the input vars
$st->bindValue(":email", $sEmail, PDO::PARAM_STR);
$st->bindValue(":password", $sPassword, PDO::PARAM_STR);
$st->execute();
// if data is returned from calling fetch
if( $row = $st->fetch() ){
$_SESSION['loggedIn'] = true;
$_SESSION['email'] = $row['Email'];
$_SESSION['id'] = $row['Id'];
header("Location:profile.php");
exit;
}
}else{
// must have empty input add to errror string
$sError .= "[EmptyInput]";
}
// user hasnt been logged in set error
$sError .= "[InvalidLogin]";
}
// detect anthign in error stirng
if($sError != ""){
// do something
}
?>
Havent tested it but should work.
I would also salt and encrypt your passwords.
Additionally I would generate a code to use to identify the user publicly.
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 9 years ago.
I have been using this code on my website for a long time, and just want to make sure I am correctly sanatizing my PHP $_POST inputs...
foreach($_POST as $key=>$val) //this code will sanitize your inputs.
$_POST[$key] = mysqli_real_escape_string($connection, $val);
Say for example I had the POST value $_POST['comment'] that I wanted to add to a database, would this be a good and safe way to sanatize it before database entry?
foreach($_POST as $key=>$val) //this code will sanitize your inputs.
$_POST[$key] = mysqli_real_escape_string($connection, $val);
//is this safe? or is there another step?
$comment = $_POST['comment'];
if($comment != ""){
//add $comment to database
}
Is there something that I still need to do before adding $comment to the MYSQL database? Or do those top two lines do the magic by themselves? Please let me know if this is a good safe way to do it, or if there is an even better way! Thanks!
Possible duplicate of: https://stackoverflow.com/questions/15664021/php-escaping-vars-posted-through-var-and-got-by-postvari-with-a-meth
I already tried your way. It seems there's no magic function. However, from classic MySQL injections, you can be safe, when adding mysqli_real_escape_string to each posted value, then use it as a string (quoted) in the db, but it's considered bad practice, also is not the most secure way
Since MySQLi presents parametised queries, you should get familiar with them, and leave the real corresponding to the database driver, to the library.
It's not. One can use multibyte attacks, which will bypass all these sanitizers.
Moreover,
According to this answer one should avoid writing to post so one can keep sanitized code far from un-sanitized. Even though you "sanitize" everything, it leads to bad habits.
This is not a good way to sanitize input. Queries should be parameterized and input should be fed as arguments no matter where it comes from. No additional sanitation should be done (otherwise it could be duplicated).
If you have specific rules (such as $comment != "") this is validation, and it is up to you to decide validation rules and how to handle invalid input (which is different than unsanitized input).
Example of using properly parameterized prepared statement with mysqli:
$stmt = mysqli_prepare($connection, "INSERT INTO comments VALUES (?)");
mysqli_stmt_bind_param($stmt "s", $comment);
mysqli_execute($stmt);
_real_escape_string does not sanitize user inputs completely. You must use prepared statements.
Object oriented style
$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
$stmt->bind_param('sssd', $code, $language, $official, $percent);
$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;
Procedural style
$stmt = mysqli_prepare($link, "INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
mysqli_stmt_bind_param($stmt, 'sssd', $code, $language, $official, $percent);
$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;
Parameter types
Character Description
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
Documentation
down side is that you maim the post vars so you cant use them for other purposes than queries. for example what if you still wanted to echo out some post vars?
better is to escape to a new array
and even better is to not escaped but use parameterized queries.
Your missing some of the most important things to safe PHP coding.
Lets start from the beginning.
Start with these links please : Please read the code comments
This first // and this second!
1) Validate and then filter your data if it passes validation!
So we have a registration form, one that takes emails... so now what we do is validate the email.
$email = $_POST['email']; // Declare the variable
if (filter_var($email, FILTER_VALIDATE_EMAIL)) { // If validation passes ...
$safe_email = filter_var($email, FILTER_SANITIZE_EMAIL) // Sanitize the email
} else { // Validation fails no need to sanitize
echo "WRONG EMAIL PUNK!!!!";
}
2) Now using either Mysqli or PDO (I prefer PDO) we do :
$dbh = new PDO("mysql:host=xxxxxx;dbname=xxxxxx;charset=utf8", USERNAME(XXXXXXXXX), PASSWORD(XXXXXXXX); // Set up the PDO instance PLEASE DO NOT FORGET TO EXPLICETELY STATE A CHARSET!!!!
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Set up error mode
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE); // I prefer emulate prepares to be false
$sql = INSERT INTO ..... (........., email) VALUES (............, :email); // Set up our named parameter
$query -> $dbh -> prepare($sql); // Prepare the query
$query -> bindParam (':email', $email);
$query -> execute() // Yay!
Its all fine and dandy using PDO and MysqlI but there is an expression called :
Its not the wand, its the wizard.
PDO / MysqlI can not solve everything! Make sure to
Refer to my other question on how to set up PDO
1) Validate
2) Sanitize
3) use parameters for safer queries!
4) Escape any outside (un-trusted data)
Follow these security PHP practices for safer php coding.
Enjoy
I am starting a very basic site that uses a single line form to post into a database and then later echo that $comment variable on the page. I don't know PDO, but am willing to learn if I truly need it for something this simple.
else
mysql_query("INSERT INTO posts (postid, post_content)
VALUES ('', '$comment <br />')");
}
mysql_close($con);
Above this code I have basic strpos commands to block out some of the things I don't want posted.
Am I going to experience any issues with injections down the road from how I am doing this?
No, it's not safe, you need to use mysql_real_escape_string to escape $comment.
But, PDO is nothing difficult and make your code stronger.
// create the connection. something like mysql_connect/mysql_error
try {
$dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
// create the prepared statement.
$stmt = $dbh->prepare("INSERT INTO posts (postid, post_content) VALUES (?, ?)");
// execute it with parameters.
$stmt->execute(array('', $comment.'<br>'));
Yes this is dangerous. All someone has to do is put a single quote then the SQL code they want after. Use $comment = mysql_real_escape_string($comment) before this statement if you want to fix it the old way or use PDO prepared statements as the newer way. Here is a basic example from the documentation:
<?php
$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();
// insert another row with different values
$name = 'two';
$value = 2;
$stmt->execute();
?>
This is susceptible to sql injection as your $comment is input from the user they may as well enter some SQL command and your PHP code will end up executing the same.
Consider $comment value is set to 'TRUNCATE TABLE USERS;' the USERS table could be anything which might be critical for your app.
In PHP I believe you safeguard against sql injection by using mysql_real_escape_string(). Read up on it.
Refer this doc for details abt SQL innjection: https://docs.google.com/document/d/1rO_LCBKJY0puvRhPhAfTD2iNVPfR4e9KiKDpDE2enMI/edit?pli=1
Binding form input data to mysql query is the perfect solution to the sql injection. Use binaParam method for this purpose.
No, judging only by the code you’ve posted here, you are not protected against SQL injections. Here’s a simple example for $comment:
'), (null, (select concat(user(),':',password) s from mysql.user where concat(user,'#',host)=user() LIMIT 1) --
This will add another row containing the login credentials of the current user. With LOAD_FILE he could also be able to read files from your file system. He could also write arbitrary files on the file system:
' + (select '<?php echo "Hello, World!";' into dumpfile '/path/to/your/document_root/foobar.php')) --
With this technique the attacker could upload arbitrary files to your server, e. g. a web shell to run arbitrary commands on your system.
So you definitely must protect yourself against SQL injections whereby automatic escaping using prepared statements or parameterized statements is favored over manual escaping using functions like mysql_real_escape_string.