I have just taken over an old PHP website which is riddled with SQL injection vulnerabilities. The writer is including $_GET variables directly in SQL statements.
I'm not a PHP programmer and time is short so there is no way I will be allowed to use prepared/parameterised statements.
The best I will probably have time to do is this sort of thing:
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
Is there any way to do this quickly to all pages on a website? For example could I have an include which iterated around the $_GET array and sanitised or escaped the variable and then overwrote its unsafe version with its safe version in the $_GET array. This would mean I could fix all pages in one go :)
EDIT
I should note i am not a php programmer and i have no option to use prepared statements so Im not up to date on this debate about which php functions are safe for sanitising input. On futher reading I believe the real-escape function is safe if i set the mysql charset to utf8
<?php
$conn = mysql_connect("localhost", "my_user", "my_pass");
$db = mysql_select_db("world");
if (!mysql_set_charset('utf8', $conn)) {
echo "Error: Unable to set the character set.\n";
exit;
}
I also believe the code does not process the GET variables at all but simply adds them into the sql query.
Yes, you could do an include or just a one liner like:
foreach($_REQUEST as $key => $val) { $_REQUEST[$key] = mysql_real_escape_string($val); }
This assumes no array-esque form fields are being posted.
However, you have to understand that
it doesn't mitigate injections
but just lowering the risk
Safest way to do it would be to dowload all the sources, open all the .php files in notepad++ and search in all opened files for mysql_, then validate that it has to be replaced.
If your project is not that big, it won't take too long and it will avoid breaking your code if the call is not exactly the same in one file.
This would mean I could fix all pages in one go :)
Unfortunately, it won't help.
As there is obviously no quick way.
Just because mysql_real_escape_string by no means makes variables "safe".
The only rough equivalent of safety you can achieve if make positively sure that every one variable you put into query is not only escaped but also quoted as well.
Related
Ive read alot of different ideas lately on how to do this. Im running php 5.5 and mysql 5.6, and Im using PDO prepared statements.
PDO is supposed to not require sanitation beforehand, so why do I see so many references to filter_input and filter_var?
Is FIEO relevant with PDO? Below is a simple example of a query and echo. How should i ultimately set it up differently?
$name = "John";
$query = $db->prepare("SELECT `name` FROM `users` WHERE `name`=?");
$query->bindValue(1, $name);
try{
$query->execute();
$row = $query->fetch();
} catch(PDOException $e){
die($e->getMessage());
}
echo "Name is " . $row['name'] . ".";
Im looking forward to finally understanding proper coding for security.
filter_var isn't just for sanitizing input; there are also validation filters that can be used for type checking (http://www.php.net/manual/en/filter.filters.validate.php) and as processing instructions (http://www.php.net/manual/en/filter.filters.flags.php).
On a tangential note:
There's also the whole concept of using the stored data; just because you may be more or less safe against SQL injection doesn't mean your data can then be consumed safely. You still have to make sure your data doesn't contain malicious content (like, for instance, checking for encoded text that renders out as executable JavaScript when accepting HTML content from a user).
If you want to see some examples of things to watch out for in the example I mention above, I found the test suite for the HtmlSanitizer library very interesting: https://github.com/mganss/HtmlSanitizer/blob/master/HtmlSanitizer.Tests/Tests.cs
Variables passed as arguments to prepared statements will automatically be escaped by the underlying driver which helps to prevent SQL injection.
Although Prepared Statements helps in defending against SQL Injection, there are possibilities of SQL Injection attacks through inappropriate usage of Prepared Statements
String strUserName = //retrieved from webpage via text field
$query = $db->prepare("SELECT `name` FROM `users` WHERE `name`=strUserName ");
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.
I'm creating a basic blog and I'm using the following code.
It's collecting the id (always a number) from the url and before I use, I wondered if anyone could check the security of the code and let me know if its ok?
I really don't want any injections, etc, and I want to keep it as much secured as possible.
<?php
if(is_numeric($_GET['id']) && $_GET['id'] > 0){
include("connectionfile.php");
$ia = intval($_GET['id']);
$ib = mysql_real_escape_string($ia);
$ic = strip_tags($ib);
$qProfile = "SELECT * FROM #### WHERE id='$ic' ";
$rsProfile = mysql_query($qProfile);
$row = mysql_fetch_array($rsProfile);
extract($row);
$title = trim($title);
$post = trim($post);
$date = trim($date);
mysql_close();
}else{
echo 'hack error here';
}
?>
$ia = intval($_GET['id']);
$ib = mysql_real_escape_string($ia);
$ic = strip_tags($ib);
strip_tags is useless, because it is only relevant in an HTML context. Any one of the other two methods would be sufficient to prevent SQL injection. Generally, just use the appropriate escaping mechanism for the language you're dealing with. In this case you're dealing with SQL, so mysql_real_escape_string alone is fine. See The Great Escapism (Or: What You Need To Know To Work With Text Within Text) for a step-by-step approach to escaping.
Better yet, learn PDO with prepared statements instead of the deprecated mysql_ functions, which solves the issue of SQL injection much better.
Don't use mysql_ functions. They are deprecated. Use mysqli or
PDO.
Use parameterized queries
Don't use "extract" as it pollutes the local scope. There are rare cases where it's safe, usually internal to an ORM, where it's
within the object. This is dangerous otherwise as all forms of
nasty variable names could be introduced, especially with successful
SQL injection.
Do exception handling so that database errors do not break the page entirely, and in the case of a bad query somehow forced via SQL Injection, nothing is displayed to indicate that the query was broken.
Even after you do all the above, still make sure you use htmlentities() or otherwise validate the data is what you expect before you display.
This code is a mess ;-)
if statement can be simplified "if (($id = (int)$_GET['id']) > 0) {"
if you acknowledge my 1. point, then $ia, $ib and $ic can be deleted
don't trim() database data! data should be trimed before INSERT into database.
read what #FilmJ has answered you
<?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 !
So I'm a slightly seasoned php developer and have been 'doin the damn thing' since 2007; however, I am still relatively n00bish when it comes to securing my applications. In the way that I don't really know everything I know I could and should.
I have picked up Securing PHP Web Applications and am reading my way through it testing things out along the way. I have some questions for the general SO group that relate to database querying (mainly under mysql):
When creating apps that put data to a database is mysql_real_escape_string and general checking (is_numeric etc) on input data enough? What about other types of attacks different from sql injection.
Could someone explain stored procedures and prepared statements with a bit more info than - you make them and make calls to them. I would like to know how they work, what validation goes on behind the scenes.
I work in a php4 bound environment and php5 is not an option for the time being. Has anyone else been in this position before, what did you do to secure your applications while all the cool kids are using that sweet new mysqli interface?
What are some general good practices people have found to be advantageous, emphasis on creating an infrastructure capable of withstanding upgrades and possible migrations (like moving php4 to php5).
Note: have had a search around couldn't find anything similar to this that hit the php-mysql security.
Javier's answer which has the owasp link is a good start.
There are a few more things you can do more:
Regarding SQL injection attacks, you can write a function that will remove common SQL statements from the input like " DROP " or "DELETE * WHERE", like this:
*$sqlarray = array( " DROP ","or 1=1","union select","SELECT * FROM","select host","create table","FROM users","users WHERE");*
Then write the function that will check your input against this array. Make sure any of the stuff inside the $sqlarray won't be common input from your users. (Don't forget to use strtolower on this, thanks lou).
I'm not sure if memcache works with PHP 4 but you can put in place some spam protection with memcache by only allowing a certain remote IP access to the process.php page X amount of times in Y time period.
Privileges is important. If you only need insert privileges (say, order processing), then you should log into the database on the order process page with a user that only has insert and maybe select privileges. This means that even if a SQL injection got through, they could only perform INSERT / SELECT queries and not delete or restructuring.
Put important php processing files in a directory such as /include. Then disallow all IPs access to that /include directory.
Put a salted MD5 with the user's agent + remoteip + your salt in the user's session, and make it verify on every page load that the correct MD5 is in their cookie.
Disallow certain headers (http://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST) . Disallow PUT(If you dont need file uploads)/TRACE/CONNECT/DELETE headers.
My recommendations:
ditch mysqli in favor of PDO (with mysql driver)
use PDO paremeterized prepared statements
You can then do something like:
$pdo_obj = new PDO( 'mysql:server=localhost; dbname=mydatabase',
$dbusername, $dbpassword );
$sql = 'SELECT column FROM table WHERE condition=:condition';
$params = array( ':condition' => 1 );
$statement = $pdo_obj->prepare( $sql,
array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );
$statement->execute( $params );
$result = $statement->fetchAll( PDO::FETCH_ASSOC );
PROs:
No more manual escaping since PDO does it all for you!
It's relatively easy to switch database backends all of a sudden.
CONs:
i cannot think of any.
I don't usually work with PHP so I can't provide advice specifically targeted to your requirements, but I suggest that you take a look at the OWASP page, particularly the top 10 vulnerabilities report: http://www.owasp.org/index.php/Top_10_2007
In that page, for each vulnerability you get a list of the things you can do to avoid the problem in different platforms (.Net, Java, PHP, etc.)
Regarding the prepared statements, they work by letting the database engine know how many parameters and of what types to expect during a particular query, using this information the engine can understand what characters are part of the actual parameter and not something that should be parsed as SQL like an ' (apostrophe) as part of the data instead of a ' as a string delimiter. Sorry I can not provide more info targeted at PHP, but hope this helps.
AFAIK, PHP/mySQL doesn't usually have parameterized queries.
Using sprintf() with mysql_real_escape_string() should work pretty well. If you use appropriate format strings for sprintf() (e.g. "%d" for integers) you should be pretty safe.
I may be wrong, but shouldn't it be enough to use mysql_real_escape_string on user provided data?
unless when they are numbers, in which case you should make sure they are in fact numbers instead by using for example ctype_digit or is_numeric or sprintf (using %d or %u to force input into a number).
Also, having a serarate mysql user for your php scripts that can only SELECT, INSERT, UPDATE and DELETE is probably a good idea...
Example from php.net
Example #3 A "Best Practice" query
Using mysql_real_escape_string() around each variable prevents SQL Injection. This example demonstrates the "best practice" method for querying a database, independent of the Magic Quotes setting.
The query will now execute correctly, and SQL Injection attacks will not work.
<?php
if (isset($_POST['product_name']) && isset($_POST['product_description']) && isset($_POST['user_id'])) {
// Connect
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password');
if(!is_resource($link)) {
echo "Failed to connect to the server\n";
// ... log the error properly
} else {
// Reverse magic_quotes_gpc/magic_quotes_sybase effects on those vars if ON.
if(get_magic_quotes_gpc()) {
$product_name = stripslashes($_POST['product_name']);
$product_description = stripslashes($_POST['product_description']);
} else {
$product_name = $_POST['product_name'];
$product_description = $_POST['product_description'];
}
// Make a safe query
$query = sprintf("INSERT INTO products (`name`, `description`, `user_id`) VALUES ('%s', '%s', %d)",
mysql_real_escape_string($product_name, $link),
mysql_real_escape_string($product_description, $link),
$_POST['user_id']);
mysql_query($query, $link);
if (mysql_affected_rows($link) > 0) {
echo "Product inserted\n";
}
}
} else {
echo "Fill the form properly\n";
}
Use stored procedures for any activity that involves wrinting to the DB, and use bind parameters for all selects.