This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 3 years ago.
I'm working on a site that has been hacked through SQL Injection (at first glance only db entries are corrupted with cross-site scripting) the potential vulnerability I found after looking at the code is that there's a lot of mysql_query call whose inputs are not escaped at all.
The good old :
$query = "SELECT * FROM mytable where name LIKE '%".$_GET['name']."%'"; /*HACK HERE*/
mysql_query($query, $connection);
Nevertheless I can't find how can we do something cool from that injection vulnerability (by cool I mean something like an INSERT or an UPDATE). I've tried to build a statement like this one :
SELECT * FROM mytable where name LIKE '%' AND WHERE id IN (INSERT INTO secondtable (id,description) VALUES (15, 'Fifteenth description');--%'
No success. I guess that the INSERT has nothing to do here.
I'm escaping all user's inputs in the code right now but I've not really get how hackers have penetrated this site, then I'm not 100% sure that my fix will do the job. Any brilliant suggestions ?
Thanks
Depending upon the version of mysql you are using, and the setup of the connection, mysql_query may allow more than one statement.
You should look at how the connection is being created, and for any usage of mysql_set_server_option.
Because mysql_query is not supporting multiple queries, So any injection that is doing like '; DROP TABLE mytable; -- won't be successful.
However, the attacker can combine with other select statement to select the other info like password info.
Interesting that your question hasn't received many (correct) answers yet!
As you discovered, usual PHP MySQL APIs like mysql_query, mysqli::query etc. only execute the first SQL statement in case one passes several of them (separated by semicolons), as would an attacker using the most common class of SQL injections.
Defender tip: banish mysqli::multi_query and friends from your code; the minute performance improvements are not worth the risk.
Does this clever move by PHP-folk completely close all attack channels on some code that goes like "SELECT yadda yadda" . $_GET["untrusted"]? Not quite. As knittl remarks, even a pure-DQL SELECT can be used to escalate one's privileges by UNION ALL SELECT'ing from any nearby interesting table, including but not limited to the passwords table. There are cheat sheets out there giving enough tips and tricks to basically extract the entire database this way.
Defender tip: apply defense-in-depth with known-good techniques:
input validation
whitelist indirection in an associative array
prepared statements (if you can make sure that they are effective and not just string escaping in disguise!)
an ORM
protective encoding (e.g. Base64) when the untrusted input cannot be satisfactorily sanitized (e.g. a blog post that may legitimately contain SQL-sensitive punctuation)
or as a last resort only, string escaping
Next, one may observe that not all DQL is side-effect free, in particular when it ends with INTO DUMPFILE somethingsomething.
Defender tip: always configure secure_file_priv in your MySQL / MariaDB server.
Last but not least, even an attacker who is in a position to inject arbitrary SQL is limited by the authority granted to the Web app as a whole.
Defender tip: secure your app by applying POLA.
Only GRANT the Web app's MySQL user as much authority as it needs. It is not a bad idea to design your app so that it requires no DDL at all. If you must provide a “back up / restore DB” feature or some such from the Web UI, use a separate MySQL user for that.
Automate backups, even though they are useless — restores are what matters.
Possible scenario 1
Weak passwords/hashing will let an attacker to select administrator's password.
It would be wise to change all administrators passwords.
Now, it's been a while since did any php, but in general most data access libs have some sort of parameterized sql to reduce the risk. A quick google came up with this for php:
http://php.net/manual/en/pdo.prepared-statements.php
The other poster have already described how to do a sql injection so I won't get into that.
I'm pretty sure that a hacker would be able to modify the query easily. Even if mysql_query() doesn't support multiple queries, there are ways around that. you could just use a mysql IF statement added on to the end, and of course that will execute a completely new query.
$query = "SELECT * FROM mytable where name LIKE '%".$_GET['name']."%'";
$_GET['name']="'; DROP TABLE mytable; -- ";
so
$query = "SELECT * FROM mytable where name LIKE '%'; DROP TABLE mytable; -- %'";
Related
I am learning mysql now and one of the subjects it touches is the security issue when dealing with user input - one concern is the injection attack. I tried to duplicate the attack the book demonstrated like add a query $query = "select * from temp_table; drop table temp_table, which I used mysqli_query($connection,$query). Nothing happen. I changed to use mysqli_multi_query() and found it executed both statements. Finally I found that mysqli_query only runs one query each time.
my question is, if I use mysqli_query, theoretically speaking, the system shouldn't be worried on additional statement injection attack? Or, there is still any other way that the users can run additional statement even the server is using mysqli_query?
It's true that the basic mysqli_query() will only run one statement. So you don't have to worry that an SQL injection attack will trick your application into running multiple statements.
But one statement can include a subquery, or a SELECT... UNION SELECT....
One statement can read data it isn't intended to read. Or cause a huge sort that is intended to overwhelm your server as a denial-of-service attack.
Or it can simply be an error, not a malicious attack at all.
SELECT * FROM Users WHERE last_name = 'O'Reilly'; -- woops!
The solutions to SQL injection are pretty simple, and easy to follow. I don't understand why so many developers look for excuses not to write safe code.
I have pages that only display data from DB tables, the php pages only display the information they don't have any buttons, links, drop down menus, or forms.
Im using the old mysql and not the mysqli or PDO for syntax
Can I still get a SQl injection hack?
In order for SQL Injection to work, they need a way to send SQL Code to your server, as there is no input, it is in theory impossible for them to Inject SQL. (Although I am not an expert in the subject)
I would still recommend you to use a framework like mysqli or PDO, you should familiarize yourself with such frameworks as they became the norm in website design.
SQL Injection works by injecting strings into the SQL you're executing.
This is easiest if the application presents you with a nice text box whose content it glues into a SQL query, but it's also possible through other means.
For instance, if your reporting application uses any kind of the HTTP request to show data, an attacker can use WGET to spoof the request and inject SQL. For instance, if your reports have a URL format of http://myserver/report.php?month=february and you use february to build a SQL query, you're vulnerable.
It all depends on the query source. If ANY part of your SQL query comes from a user input (in other words; if not your whole query string is a string constant) then you are not safe.
For example:
SELECT * FROM USERS ;
SELECT * FROM myTable WHERE id = 5;
is safe but
SELECT * FROM myTable WHERE id = (some variable derived from user input like querystring or post-form)
is DEFINETELY not.
You should use
SELECT * FROM myTable WHERE id = #0
syntax for maximum security. This is the only one proven way to keep safe from SQL injection.
If the query is independent of the request you are safe. Be warned that mean users are very creative. You might have some dependency on user data which you are not aware of. E.g. a script like this might be broken:
$locale = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
$sql = "SELECT * FROM entities WHERE id = 7 and locale = '$locale'";
Disclaimer: I'm not aware if Locale::acceptFromHttp() does some validation. Let's just assume for the context of this post it doesn't.
I don't want to say it's not possible to write safe code without prepared statements. OTOH it really doesn't hurt using them.
SQL injection occurs if user-controllable data is used in an SQL statement without proper processing:
The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.
Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Note that user-controllable data include any data that can be influenced by the request of a user, directly or indirectly.
Conversely, this means if your SQL queries don’t contain any user-controllable data, i. e., the queries are fixed in code and are not influenced by any changeable input, it cannot vulnerable to SQL injections.
I have pages that only display data from DB tables, the php pages only display the information they don't have any buttons, links, drop down menus, or forms.
I would dare to say that such a setup is just impossible.
The very idea of having a dynamical web-page, served by a php script from database, is to use the same script to show different content from database.
So - either you indeed have only set of static pages (which I doubt) - you need no PHP/database for them then - just static HTML would be enough.
Or - most likely - you have dynamical pages showing content based on user input of some sort - and thus perfectly vulnerable.
I have started in web development not long time ago. I know some stuff now, but I'm really concerned about security issues that may arise. I know simple security solutions like preg_replace , but I'm not confident with that.
So I would like to ask you for any sort of speaking "universal" security standards that can be applied in the following cases. As I mentioned, I'm not pro so it would be great if you can start with something simple, yet useful. If possible could you provide examples please?
I did have a look at php manual, although I would like to know additional info from person.
Here are some typical MySQL / PHP things I use in my projects. Could you suggest any improvements to make them more secure?
$sql = mysql_query("SELECT * FROM stories WHERE showing = 1 ORDER BY cr_date DESC LIMIT 5") or die (mysql_error("There was an error in connection"));
while($row = mysql_fetch_assoc($sql)){
$story_id = $row["id"];
// etc...
}
$username = $_POST['username'];
$sql = mysql_query("INSERT INTO myMembers (username, //etc... )
VALUES('$username' //etc.. ")or die (mysql_error());
$username = $_GET['username'];
//gets username from url like http://myweb.com/profile.php?username=blabla
First of all, thank you for caring about web security. Many PHP developers don't know anything about it, and don't care to learn. They are the ones who are exposing our passwords and bank accounts to hackers. Be part of the solution! :-)
1. Treat the mysql extension as if it is deprecated.
Use the PDO or mysqli extensions instead. The plain mysql extension does not support prepared statements, and some other features, such as transaction control. No one should be using mysql if they have PDO_mysql or mysqli available to them.
2. Do not interpolate external data into SQL.
Anytime you get a value from $_GET or $_POST, you should consider it to be unsafe to use in any SQL statement, or shell_exec(), or other instance where you execute the string as some kind of code.
3. Use prepared query parameters instead of interpolation.
It's really easy. In fact, it's easier to use query parameters than it is to interpolate variables into SQL strings. You don't need to worry about escaping, or long complex string-concatenation.
See example code here: http://us.php.net/manual/en/pdo.prepare.php
4. For corner cases, use careful filtering.
A query parameter takes the place for one literal value in an SQL expression. Not table names, not column names, not SQL keywords, not lists of values or full expressions. For those, you do need to use string interpolation, but see my presentation SQL Injection Myths and Fallacies for examples of how you can "whitelist" values to interpolate.
Also check out the PHP filter extension, which offers a flexible way of validating inputs or stripping off invalid characters to make sure only the valid part of the input is used.
Looking at your examples, the SELECT query has no dynamic values interpolated from external sources like $_GET. So that one is safe.
The INSERT query takes a value from the request, which could contain malicious content that changes the way your query runs. This one is a good candidate for using query parameters.
Also consider that SQL injection is one of the two most prevalent security issues with PHP. The other one is Cross-Site Scripting (XSS). This is not directly related to SQL, but you should learn about it too.
Here's a good resource for learning more about web programming security: OWASP.org cheat sheets.
Many frameworks have a good set of security measures already in place that will do a great deal in preventing things like SQL injections. Yii, CakePhP, CodeIgnitre all may be of some use.
Although it's almost impossible to beat Bill, I feel I must clarify answers stating that "you have to trust no user input".
In fact, quite contrary - SQL injection protection will do any good only if it would be totally ignorant of the data source. And treat ALL the data as potentially malicious. And process it accordingly.
Thus, to summarize all the advises:
Prepared statements is a good approach but not a complete one.
It has a brilliant idea of using some placeholder, a proxy to represent the actual value in the query. Thus this value can be properly sanitized.
But these placeholders, as Bill said, are limited to the strings and numbers only. So, it would be a good idea to add another placeholder of your own - for identifiers. But you still have to watch out SQL syntax keywords manually.
So, instead of "Do not interpolate external data into SQL." statement one have to use
"2. Do not interpolate values into query directly but only by some proxy, performing necessary precautions"
The most important thing to remember is never trust anything from an external source (eg user input, responses from other web services etc). Always sanitise all input and where possible use code from your database provider to do so.
In the case of MySQL parameterising all queries is essential, so use a prepared statement, eg
$statement = $db->prepare('SELECT * FROM stories WHERE title = :title');
$statement->execute(array(':title' => $title));
$rows = $statement->fetchAll();
Your current insert statement is vulnerable to an SQL injection attack, modify it to be closer to:
$username = $_POST['username'];
$statement = $db.prepare("INSERT INTO myMembers (username) VALUES(':username');
$statement->execute(array(':username' => $username));
You should also ensure that you never store any passwords in plain text, always store a hashed version of a password (along with a salt) and check that the hash matches rather than the actual string. This means that should your database become compromised, figuring out your user's credentials becomes a non-trivial task.
These are only a couple of ways of making your app more secure, I'd highly recommend reading OWASPs top 10 site vulnerabilities and researching these individually as each one in itself is quite a big topic!
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 1 year ago.
I have a Single Page Application in which the browser does all the logic work. Except for initial loading, the server is pretty much a fancy interface to the database.
The browser sends data dictionary keys, column name / value pairs, and where clauses for SELECT, for example. The server assembles the parts into SQL, executes the queries, and replies. NEW: In a SELECT, for example, the table name and columns pulled are from the data dictionary - the browser supplies the data dictionary key and the SELECT where clause.
This very open environment is very susceptible to SQL Injection attacks. The goal is to prevent damage from said attacks.
Problems to be Overcome
First, as discussed, it is impossible to paramaterize a random SELECT where clause - SELECT cannot use a prepared statements.
Second, mysqli, the library for paramaterized statements with MySQL, does not support NULL nor MySQL functions, eg, CURRENT_DATE or NOW(), as discussed.
Proposed Solution
First, if SELECT cannot be paramterized, then execute SELECT by a user who has no DML or DDL rights. This will prevent SQL Injection Attacks from changing the database.
Second, write a wrapper function for mysqli that will allow NULL and MySQL functions to be passed as parameters. This will allow parameters to be easily used for all DML.
Third, shadow highly sensitive data where it cannot be seen or touched by normal queries or normal users. This will put sensitive data, such as passwords, out of the range of attacks.
Forth, write a wrapper order to enforce the user / query type relationship. This will ensure SELECT are executed by the select user, for example
The result of that effort is here. The question is, logically, will this approach successfully protect against SQL Injection Attacks?
Non-Answer Answers
I proposed this same question before. Due to my poor job of presentation, received answers and comments ended up focusing on spurious issues instead of addressing the (admittedly) difficult question - does this actually work?
As a reference, here are some of those comments and answers.
Use PDO's prepared statements
First, prepared statements cannot be used for all SELECTs - how does PDO help? Second, mysqli does not accept NULL or MySQL functions - how does PDO help?
Why re-invent the wheel
If you know of an approach that overcomes these problems, I would really really like to know - this is a difficult problem.
no mysql_real_escape_string() in sight
Values should be sanitized before being passed to the database query functions. mysql_real_escape_string() is one of a set of sanitizing functions available, for example, one would use a different sanitizer for dates.
too much work
Please share with me your know of any approach that overcomes these problems - I would really like better insight. That said, from my setting up the whole thing again, following my notes it took between 30 and 45 minutes. No time costs incurred thereafter.
I am happy with mysqli
How are you going to prevent SQL Injection Attacks when you cannot parameterize your SELECT? Do you expect to never use NULL when updating a column? Every man to his own poison, but these are issues I hope to solve - not live with.
#Konerack pointing out limited number of parameters
Right. Changed to code to use eval() (shudder) which solved the problem. Needs security review.
The Question Again
Will this approach protect against SQL Injection Attacks while overcoming the problems of no parameterized SELECT and mysqli's parameter limitations?
The answer is simple:
For parameters use PDO or mysql_real_escape_string().
For other dynamic SQL (table names, column names, syntax elements) use whitelisting.
See: How to prevent SQL injection with dynamic tablenames?
You cannot trust the browser to keep within the bounds you set in Javascript. This can be easily manipulated. So you must treat all data from that end as untrusted. Make sure you check all elements in the where clause against a list of allowed keywords.
For this purpose you'd use:
Check against whitelist for symbols: =, <>, >, LIKE, NULL, IS.
Check against a whitelist for boolean operators: AND, OR, XOR, NOT
All values must be properly enclosed in single ' quotes and you will need to feed these through mysql_real_escape_string() to make sure there are no shenanigans.
All values not in (1,2) and not enclosed in single quotes are column names, check them against a whitelist of allowed column names.
Reject all other input.
I have an ajax method that esentially works like this
function getRow(tableName, idName, idValue, callback)
The obvious benefit of this is that I have one function that can retrieve data from any table. However, this just feels wrong from a security perspective, is it a security risk to do so? The corresponding PHP files that actually read/manipulate the database are secured through a prior authentication process, so theoretically, the visibility of table names in a vacuum shouldn't be a risk (not to mention that the database only accepts localhost connections), but I wonder if there isn't a better/prettier way to accomplish this.
Edit: Just to clarify the authentication process, user/role security prevents access to all tables except those explicitly allowed for the user.
If you're sure that you have no SQL injection vulnerabilities, and that you never will, it's fine.
If you do have a SQL injection vulnerability, it will make the attacker's job somewhat easier.
It goes without saying (I hope) that the server-side script must use a whitelist of tables and columns that can be exposed via this method.
Tables names alone aren't that bad, however, the API you seem to be making may not be the best idea...
Think carefully about if there are some tables that people shouldn't be able to query, because from the looks of it, I could query any table in your database from the client side. For example:
getRow('users', 'id', '7', function(data){console.log(data)})
What if the users table returns their password? even if it is hashed, thats not good. Or their email? What if I want to harvest all of the users emails? Pretty easy script I could write to do that.
It is a risk because it provides information to attackers. It's the same as providing the software version of software that you are using. It is not a vulnerability per se, but it's a door to vulnerabilities.
This is a really bad idea.
For instance lets say you are using this code:
$table_name=mysql_real_escape_string($_GET['table']);
$id_name=mysql_real_escape_string($_GET['idname']);
$id_value=mysql_real_escape_string($_GET['idvalue']);
mysql_query("select * from `$table_name` where `$id_name`='$id_value'");
This can be exploited a number of ways:
NOT SQL Injection
This query will return the root user in mysql.user, and this is
?table=mysql.user&idname=user&idvalue=root
This request will create the query:
select * frommysql.userwhereuser='root'
SQL Injection:
This works because mysql_real_escpae_string does not escape back-ticks:``
?table=`table where 1 union select * from mysql.user/*&idname=junk&idvalue=junk
You can show eg users table name in JS but on server side add prefix like forum_users, which will be actual table name in database. That way if someone finds injection point he will try DROP TABLE users and the query will fail, if you are lucky :)
Also add a white/black list for tables and columns, add LIMIT 1 (don't loop), don't return multiple arrays, and you should sanitize with something like this.
$select = preg_replace("#[\w]#", "", $_GET["select"]);
$from = preg_replace("#[\w]#", "", $_GET["from"]);
$where = preg_replace("#[\w]#", "", $_GET["where"]);
$equals = mysql_real_escape_string($_GET["equals"]);
$query = "SELECT $select FROM $from WHERE $where = '$equals' LIMIT 1";
A direct database API on the client-side should be used for very special cases of course. But you can safeguard it easily with a whitelist. In PHP for example:
if (in_array($tableName, array("users", "log", "messages", ...))) {
So I don't think it's that big of a deal from a security standpoint as long as you have a fixed list here.