So I have finished creating a database and I use php to insert data into it, I have been trying to do SQL injection attacks and other things to see if I am secure, but since I am no expert I was hoping to check that what I have done is secure and the correct way to go about.
I have this(names/variables have been modified) form and when the submit button is pressed, the function insert() runs
<form action="" method="POST">
var1: <input type="text" name="var1"><br>
var2: <input type="text" name="var2"><br>
<input type="submit" value="Submit">
</form>
<php?
function insert() {
$connect = mysqli_connect("localhost","user","user","table");
$var1 = $_POST['var1'];
$var2 = $_POST['var2'];
mysqli_query($connect, "INSERT INTO column_name (var1, var2) VALUES ( '$var1','$var2'); ");
}
?>
and I can't seem to inject my form which has var1 and var2 with this
$var2 = '): DROP TABLE test --and several variants of this
From looking around I have found that mysqli_query will only accept one query so that is why it is not working. Correct me if I am wrong.
my other idea was affecting the PHP script that is running,
by injecting the form with this
$var2 = "'); "); mysqli_query($connect,"DROP TABLE test");//
Question: can this type of thing happen? where you can affect the PHP function through the $post method while it runs? I have looked around and can't find anything. Is that because it can't?
any research papers, articles, etc. that I can have a look at to help if what I am asking is obvious would be appreciated :)
EDIT: I will be adding prepared Statements to make this secure
SQL injections use commands like union to run multiple queries at once at vulnerable place. Your form IS vulnerable, because you are either not using any sort of escaping, nor prepared statements. What if your $var2 would contain for example hi')? That would escape the brackets and open a vulnerability. Also if you just $_POST['value'] and insert it directly in database, it opens XSS vulnerability.
If you want to make sure your site is safe i suggest you:
Fisrt, Use prepare statement:
$mysqli->prepare("SELECT Distinct FROM City WHERE Name=?");
$stmt->bind_param("s", $city);
$stmt->execute();
Second, Use filter_input method, for example:
filter_input(INPUT_POST,'email',FILTER_EMAIL);
You are not supposed to certify your site against SQL Injection by trying to figure out how to exploit the hundreds of potential security breaches you leave all around the place.
If you want to make sure your site is safe, just take the applicable security measures, like using prepared statements.
Related
<?php include_once("database.php");
?>
<?php include_once("header.php");
?>
<?php
if ($_POST['submit'] )
{
$food_name = $_POST['food_name'];
$food_calories = $_POST['food_calories'];
echo $food_name . $food_calories;
if (!empty($food_name) && !empty($food_calories) )
{
$query = 'INSERT INTO foods VALUES(0, $food_name, $food_calories)';
mysqli_query($con, $query) or die(mysqli_error($con));
echo 'added';
} else {echo'fail';}
} else {echo'fa2oo';}
?>
<h1> Please Fill out Form Below to Enter a New Food </h1>
<form action="addfood.php" method="post">
<p>Name:</p>
<input type="text" name="food_name"/>
<p>Calories:</p>
<input type="text" name="food_calories"/> </br>
<input type="submit" value="submit" />
</form>
<?php include_once("footer.php")?>
Really don't understand why this simple insert is not working. The form is self-referencing. The form does not echo anything and simply resets when i hit the submit button. database connects with no errors.
Since you're inserting strings, you need to enclose them by single quotes ('):
$query = "INSERT INTO foods VALUES(0, '$food_name', $food_calories)";
Note, however, that building an SQL statement by using string manipulation will leave your code vulnerable to SQL inject attacks. You'd probably be better off using a prepared statement.
You have a few errors in your code:
1. Missing name attribute
You are missing the name attribute for your submit button. So add it like this:
<input type="submit" name="submit" value="submit" />
//^^^^^^^^^^^^^
2. Wong variables in empty()
You have to check if the $_POST variables are empty! Otherwise you would try to assign an undefined variable to another variable. So change your second if statement to this:
if (!empty($_POST['food_name']) && !empty($_POST['food_calories']) )
//^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
And also put the assignments inside the second if statement.
$food_name = $_POST['food_name'];
$food_calories = $_POST['food_calories'];
3. Wrong quotes + missing quotes
You have to use double quotes that your variable in the query gets parsed as variables. Also you have to put single quotes around them since they are strings, so change your query to this:
$query = "INSERT INTO foods VALUES(0, '$food_name', '$food_calories')";
//^ ^ ^ ^ ^
Side notes:
Add error reporting at the top of your file(s) to get useful error messages:
<?php
ini_set("display_errors", 1);
error_reporting(E_ALL);
?>
You may also want to change to mysqli with prepared statements since they are, much safer.
Here's the safe way of doing this using mysqli. Prepared statements will make sure you don't have (as high of) a risk of SQL injection
editing to include the connection:
$conn = new mysqli($host, $username, $password, $dbname);
If you want to see errors, you need to tell php to give the the errors. this should suffice:
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
This part is how to do the query.
Note the bind_param part; this is where you identify how your variables are going to into the database, and what datatype they need, so you don't need to remember which items to put in quotes in the actual query.
$query = $conn->prepare("INSERT INTO foods VALUES(0, ?, ?)");
$query->bind_param("si",$food_name, $food_calories);
$query->execute();
$query->close();
As mentioned before, $food_name is a string, so you specify it as such with the s in the bind_param and assuming that calories are an integer, they go in as i.
Another nice feature of using this approach is you no-longer need to worry about escaping variables; items in inputs go in exactly as they are entered
If you want more information in detail there's always this reliable source:
http://php.net/manual/en/mysqli.quickstart.prepared-statements.php
If you find this a bit too much, here's a great site to learn step by step how to use prepared statements from scratch (it also includes PDO but you may find it easier to use the mysqli at first and it still pretty good). http://www.w3schools.com/php/php_mysql_prepared_statements.asp
Have fun!
There are a few things wrong here.
Firstly, anything inside this conditional statement will not happen because of your submit button not bearing the "submit" name attribute.
if ($_POST['submit'] ){...}
However, it's best using an isset() for this.
if (isset($_POST['submit'] )) {...}
Modify your submit to read as:
<input type="submit" name="submit" value="submit" />
^^^^^^^^^^^^^
Then, we're dealing with strings, so wrap the variables in your values with quotes.
Wrap your query in double quotes and the values in single quotes:
$query = "INSERT INTO foods VALUES (0, '$food_name', '$food_calories')";
Sidenote #1: If you experience difficulties, use the actual column names in which they are to go inside of.
I.e.: INSERT INTO table (col1, col2, col3) VALUES ('$val1', '$val2', '$val3')
Sidenote #2: Make sure that 0 for your column is indeed an int, however I don't know why you're using that.
If that column is an auto_increment, then replace the 0 with '' or NULL, should your schema accept it.
Now, should there be any character that MySQL may complain about, being quotes, etc., then you will need to escape/sanitize your data.
Say someone entered Tim's donuts in an input:
MySQL would translate that in your values as 'Tim's donuts', in turn throwing a syntax error.
Using mysqli_real_escape_string() for instance, would escape the apostrophe and render it as 'Tim\'s donuts' being a valid statement since it has been escaped.
Better yet, using prepared statements, as outlined below.
In its present state, your present code is open to SQL injection. Use prepared statements, or PDO with prepared statements, they're much safer.
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: Error reporting should only be done in staging, and never production.
Footnotes:
Given that we don't know which MySQL API you are connecting with, please note that different APIs do not intermix with each other.
For example:
You can't connect using PDO and querying with mysqli_
You can't connect using mysql_ and querying with mysqli_
etc. etc.
You must be consistent from A to Z, meaning from connection to querying.
Consult Choosing an API on PHP.net
https://php.net/mysqlinfo.api.choosing
Final closing note(s):
As stated by Rizier123, you are best using:
if (
!empty($_POST['food_name'])
&&
!empty($_POST['food_calories'])
)
It is a better solution.
Your issue (at least one of them) might be the SQL statement itself. Depending on the columns that you have in this foods table, you'll be required to specify the columns that you're inserting into. Try this:
INSERT INTO foods (col1, col2, col3) VALUES (val1, val2, val3)
Also, if val1 is supposed to be the ID column, you can't specify a value for that if it's auto-incrementing... the db will take care of that for you.
Any way to prevent malicious sql statements without using prepared statements and parameterized queries?
Example after simplify:
<?php
$con = mysqli_connect($_POST['db_server'], $_POST['db_user'],
$_POST['db_password'], $_POST['db_database']) or die(mysql_error());
$result = mysqli_query($con, $_POST['query_message']);
?>
Is it possible to check out the parameter $_POST['query_message'] is safe or not?
You should always build your queries within your code and then sanitise any variables you're going to use within them. NEVER pass the query or the database connection variables in via $_POST unless your user is querying the database via that form, in which case I'd recommend you just install phpMyAdmin.
As for sanitising your variables, if you really don't want to use PDO's prepared statements, you can sanitise incoming integers as follows:
$id = (isset($_POST['id']) ? (int)$_POST['id'] : null);
if ($id) {
$sql = "SELECT *
FROM `table`
WHERE `id` = {$id}";
}
And for strings use this:
$username = (isset($_POST['username']) ? mysqli_real_escape_string($con, $_POST['username']) : null);
if ($username) {
$sql = "SELECT *
FROM `table`
WHERE `username` = {$username}";
}
You can also call real_escape_string() directly on your $con object as follows:
$username = (isset($_POST['username']) ? $con->real_escape_string($con, $_POST['username']) : null);
However, as with #Shankar-Damodaran above, I highly suggest you do use PDO prepared statements to query your database.
Why you don't wanna use Prepared Statements ? That is really weird. I strongly suggest you should go for it.
You could make use of mysqli::real_escape_string for escaping quotes that is commonly used for SQL Injection Attacks.
Something like...
OOP Style
$message = $mysqli->real_escape_string($_POST['query_message']);
Procedural Style
$message = mysqli_real_escape_string($link,$_POST['query_message']);
other way is using:
htmlentities($query);
as an extra you could use preg_match() regular expressions to avoid
the inclusion of certain words (SELECT, DROP, UNION .......)
Example:
try{
$query = sprintf("SELECT * FROM users WHERE id=%d", mysqli_real_escape_string($id));
$query = htmlentities($query);
mysqli_query($query);
}catch(Exception $e){
echo('Sorry, this is an exceptional case');
}
There are real world cases where prepared statements are not an option.
For a simple example, a web page page where you can do a search on any number of any columns in the database table. SAy that table has 20 searchable columns. you would need a huge case statement that has all 20 single column queries, all 19+18+17+16+15+14+13+... 2 column queries, all possible 3 column queries... that's a LOT of code. much less to dynamically construct the where clause. That's what the OP means by prepared statements being less flexible.
Simply put, there is no generic case. If there was, php would have it already.
real_escape_string can be beaten. a common trick is to % code the character you are trying to escape so real_escape_string doesn't see it. then it gets passed to mysql, and decoded there. So additional sanitizing is still required. and when all characters used in injection are valid data, it's a PITA, because you can't trust real_escape_string to do it.
If you are expecting an integer, it's super easy.
$sanitized=(int)$unsanitized;
done.
If you are expecting a small text string, simply truncating the string will do the trick. does't matter that it's not sanitized if there's not enough room to hold your exploit
But there is no one size fits all generic function that can sanitize arbitrary data against sql injection yet. If you write one, expect it to get put into php. :)
Hello I need help finding a way to protect from sql injection on my current project, Im making bash tutorial site but ive run into a problem. I put most my content in database and depending on what link the user clicks it will pull different data onto the page.
This is how im doing it
apt-get <br>
And on bash_cmds.php
<?php
require_once("connections/connect.php");
$dbcon = new connection();
$bash = $_REQUEST['id'];
$query2 = "SELECT * FROM bash_cmds WHERE id = $bash ";
$results = $dbcon->dbconnect()->query($query2);
if($results){
while($row = $results->fetch(PDO::FETCH_ASSOC)){
$bash_cmd = $row['bash_command'];
$how = $row['how_to'];
}
} else { return false; }
?>
<?php echo $bash_cmd ?>
<br />
<table>
<tr><td><?php echo $how ?> </td></tr>
</table>
However this leaves me vulnerable to sql injection, I ran sqlmap and was able to pull all databases and tables. Can someone please help I would appreciate it a lot the infomation would be invaluable.
There are a couple of ways to do this. I believe the best way is to use some database abstraction layer (there's a good one built into PHP called PDO) and use its prepared statements API. You can read more about PDO here, and you can see the particular function which binds a value to a ? placeholder here.
Alternatively, you could use the mysqli_real_escape_string API function, which should escape any SQL inside your $bash variable.
Of course, in this particular case, simply ensuring the ID is an integer with (int) or intval() would be good enough, but the danger of using this approach in general is that it's easy to forget to do this one time, which is all it takes for your application to be vulnerable. If you use something like PDO, it's more "safe by default," one might say - it's more difficult to accidentally write vulnerable code.
You could bind the values to a prepared statement.
But for something simple as a numeric variable a cast to an integer would be good enough:
$bash = (int) $_REQUEST['id'];
Using this, only a number would get stored into $bash. Even if someone enters ?id=--%20DROP%20TABLE%20xy;, as this will get casted to 1;
I've found one of the easiest ways to protect against injection is to use prepared statements.
You can do this in PHP via PDO, as CmdrMoozy suggested.
Prepared statements are more secure because the placeholders ? can only represent values, and not variables (ie: will never be interpreted as a table name, server variable, column name, etc. It {currently} can't even represent a list of values). This immediately makes any modification to the logic of the query immutable, leaving only possible unwanted values as injection possibilities (looking for an id of 'notanid'), which in most cases isn't a concern (they'd just get a blank/wrong/error page, their fault for trying to hack your site).
Addendum:
These restrictions are what is in place when the prepared statements are done on the server. When prepared statements are simulated by a library instead of actually being server side the same may not be true, but often many of these are emulated.
<?php
$g_id=$_GET['gid'];
// $one = $pdo->query("SELECT * FROM contactgroups WHERE id=".$g_id);
// $result = $one->fetch();
?>
Rename groupname: <input type="text" placeholder="<?php // $one['gr_name']; ?>">
Here is my little code which simply doesn't work and I can't find what I have done wrong. Any help would be appreciated.
Your PHP code is commented
You're not calling echo
You're using $one instead of $result
You're not sanitizing the input, which is not causing this problem but it is something that should be fixed. Sanitizing is as easy as $g_id = intval( $_GET['gid'] );, but I advise looking into prepared statements.
Having said that, this should work:
<?php
$g_id=$_GET['gid'];
$one = $pdo->query("SELECT * FROM contactgroups WHERE id=".$g_id);
$result = $one->fetch();
?>
Rename groupname: <input type="text" placeholder="<?php echo $result['gr_name']; ?>">
The result is stored in the $result array, not the PDO object $one. This also needs an echo if you're not going to use shorttags.
<input type="text" placeholder="<?php echo $result['gr_name']; ?>">
I'd use shorttags, so if you PHP setup has them enabled, but they are being deprecated in PHP 5.6:
<input type="text" placeholder="<?= $result['gr_name']; ?>">
You can use shorttags for now, and I will continue to use them to protest the removal of this incredibly useful feature. Deprecating shorttags will break a lot of WP themes and simple template engines!
You should also consider using prepared statements. Without them, this script is vulnerable to SQL injections.
<?php
$query = $pdo->prepare("SELECT * FROM contactgroups WHERE id=:id");
if( $query->execute(array(':id' => $_GET['id'])) ) {
$result = $query->fetch();
?>
<input type="text" placeholder="<?php echo $result['gr_name']; ?>" />
<?php } ?>
How PDO Prevents SQL Injection (too long to put in a comment - scroll down to see a better explanation)
Let's begin with a query:
mysql_query("DELETE FROM users WHERE id='".$id."'");
If $id = "' OR 1=1 --"; then the query would look like this when sent to MySQL (-- signifies the start of a comment):
DELETE FROM users WHERE id='' OR 1=1 --'
Obviously, the destruction that would follow could be catastrophic and possibly unreversible (unless you've got some smart DB admins). The fix here instead of using the lengthy, mysql_real_escape_string() (I really never understood why the function name was so wordy in the first place), we can now use PDO prepared statements.
By PDO::preparing() a statement you are sending a message to your DB telling it to store and optimize this query because it will be used later. Your DB stores an optimized query, taking careful note of where the data belongs.
$statement = $pdo->prepare('DELETE FROM users WHERE id=:id');
PDO will give you an instance of PDOStatement that you can PDO::bindParam() values to and execute. So let's do that and execute.
$statement->bindParam(':id', $id);
$statement->execute();
Now some behind the scenes magic happens here. PDO sends the data to MySQL. MySQL examines the data and inserts into the prepared statement. By knowing where the data was supposed to be placed and how long the inserted data was, MySQL can determine the character ranges in a query that don't need execute (read: the data).
So, when a hacker tries an SQL injection, MySQL doesn't even worry about evaluating anything that is bound to the prepared statement.
PDO says to MySQL, "The data for :id is ' OR 1=1 --"
MySQL finds the location where :id was in the prepared statement. (In this example, character 28)
MySQL counts the length of the data (In this case, 11 characters)
MySQL does 1st grade math and remembers that it should treat everything from character 28 to 39 as characters.
MySQL inserts the data and now the query looks like this:
DELETE FROM users WHERE id=' OR 1=1 --
However, because it knows the position of the data, MySQL only analyzes everything outside of the pipes (|) for commands and keywords.
DELETE FROM users WHERE id=|' OR 1=1 --|
So the text between the pipes never actually gets analyzed. When MySQL needs compare id's it still compares id's in the table with the data, but since the data was never executed, the SQL injection fails.
A Better Explanation of How PDO Prevents SQL Injections
When we prepare a statement with PDO, it notifies the database of the upcoming query and where the data will be in the query. When we bind data to that query and execute it the database does some behind the scenes work to make sure that SQL injections are thwarted.
Let's take this behind the scenes work to another context. You manage a PHP blog whose engine you wrote entirely by yourself. You are proud of the clever comment system you wrote until some jerk decides to post this comment:
<script type="text/javascript">
alert('You just been H4X0RED!!!!1 LOLS');
</script>
After you yell some four letter words at the computer screen and make sure that the script kiddie's parents never let him on the internet again, you solve the XSS vulnerability in your code with htmlspecialchars().
$comment_text = htmlspecialchars($_POST['comment_text']);
Now what have you done here? When the script kiddie wakes up at 3 AM and sneaks down to his computer to try the code again, htmlspecialchars() turns his lame attempt at humor into a jibberish mess. The function takes any character that is important in HTML (namely the < and >) and turns them into their literal value (< and >).
<script type="text/javascript">
alert('You just been H4X0RED!!!!1 LOLS');
</script>
The HTML parser in everyone's browser interprets the < not as the beginning of an HTML tag, but as a sign to actually output the character <. This is essentially what the database engine does with all data inputted into prepared statements. Except since in SQL letters make up valid commands (and also valid data), the engine interprets all characters in the data as their literal value. So instead of:
DELETE FROM users WHERE id = 0 OR 1=1 --'
It evaluates each character in the data as it's literal value. In HTML that would be:
DELETE FROM users WHERE id = 0 OR 1=1 --'
If you look at both here, they both output the same thing, except in the second, the 'data' is being interpreted as it's literal value by the parser and not it's functional value. The SQL does the same thing. By using the literal value of the data, none of the actual data can be interpreted as a command or part of one.
<?php
$id = intval($_GET['id']);
$sql = mysql_query("SELECT username FROM users WHERE id = $id");
$row = mysql_fetch_assoc($sql);
$user = htmlspecialchars($row['username']);
?>
<h1>User:<?php echo $user ?></h1>
Can you see any threats in the above code? Do I have to use htmlspecialchars on everything I output? And should i use is_numeric or intval to check so that the get is numeric?
I'm just building a minimal site. I'm just wondering if the above code is vulnerable to sql injection, xss?
Generally speaking mysql_real_escape_string() is preferred but since it's a number, intval() is OK. So yes, it looks OK from a security perspective.
One thing though, on many platforms, ints are limited to 32 bits so if you want to deal in numbers larger than ~2.1 billion then it won't work. Well, it won't work how you expect anyway.
These sorts of security precautions apply to any form of user input including cookies (something many people forget).
I would strongly recommend using PDO and prepared statements. While your statement above looks safe, you're going to have problems as soon as you do more complex queries.
Instead of puzzling over whether a particular query is safe, learn about prepared statements and you won't have to worry. Here is your example, re-written with PDO:
# Make a database connection
$db = new PDO('mysql:dbname=your_db;host=your_db_server', 'username',
'password');
# The placeholder (:id) will be replaced with the actual value
$sql = 'SELECT username FROM users WHERE id=:id';
# Prepare the statement
$stmt = $db->prepare($sql);
# Now replace the placeholder (:id) with the actual value. This
# is called "binding" the value. Note that you don't have to
# convert it or escape it when you do it this way.
$stmt->bindValue(':id', $id);
# Run the query
$stmt->execute();
# Get the results
$row = $stmt->fetch();
# Clean up
$stmt->closeCursor();
# Do your stuff
$user = htmlspecialchars($row['username']);
I've added a lot of comments; it's not as much code as it looks like. When you use bindValue, you never have to worry about SQL injection.
Well,
You are casting the received id to an int ; so no possible SQL injection here.
And the rest of the DB query is "hard-coded", so no problem there either.
If id was a string in DB, you'd have to use mysql_real_escape_string, but for an integer, intval is the right tool :-)
About the output, you are escaping data too (and, as you are outputting HTML, htmlspecialchars is OK) ; so no HTML/JS injection.
So, this short portion of code looks OK to me :-)
As a sidenote, if you are starting developping a new website, it is the moment or never to take a look at either mysqli (instead of mysql), and/or PDO ;-)
It would allow you to use functionnalities provided by recent versions of MySQL, like prepared statements, for instance -- which are a good way to protect yourself from SQL injection !