mysql_real_escape_string is used for SQL statements. Is it enough for database security alone? For example with get_magic_quotes_gpc() we have use stripslashes. Is there any issue that we have to know about using other function with mysql_real_escape_string ?
Thanks in advance
If you want to have a more secure database, simply escaping a string is not enough. This will definitely help in regards to SQL injection attacks, but there are a host of other methods to compromise a database.
Some pointers:
Practice "least privilege" in that the users and accounts that are GRANTed access to your database should have the minimum privileges to complete their tasks and nothing else.
Make sure your passwords are difficult to guess (composed of letters both lower and upper, numbers, symbols, etc.) and changed regularly.
Don't save credit card numbers unless absolutely necessary (assuming you're running a commercial site).
Hash and possibly salt your passwords before storing them in your database if you'll have user accounts
Check and double-check port numbers (3306 for MySQL) and permissions on files and directories, especially if users are uploading files
These are generally good practice and you should be aware of issues for databases outside the scope of just SQL injection attacks.
not really. SQL statements are different. for some of them it helps, for others - not.
I've answered that question recently: In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
Hope it can give you the full picture, but you are welcome to ask if something is unclear.
Note that get_magic_quotes_gpc() and stripslashes are NOT database issue. It's just input data validation thing, and it has nothing to do with SQL
1)turn off magic_quotes_gpc
2)Is it enough with mysql_real_escape_string()
Related
This question gets asked a lot, but I still haven't found a straight answer on stackoverflow. Are these two functions sufficient or not? There are a lot of contradictory comments around the internet "yes its fine?, "no, never use it". Others say, use PDO, which I don't understand. I'm just a beginner to PHP, so I don't fully understand all of the ins and outs of security.
I've tried reading and understanding the following, but many don't make much sense to me.
http://ha.ckers.org/xss.html
Do htmlspecialchars and mysql_real_escape_string keep my PHP code safe from injection?
What if I use preg_replace to strip unwanted characters?
I'm incredibly confused and don't know where to start.
EDIT: Could someone please also recommend how I go about understanding prepared statements (assuming this is the best option).
Sam, if you are storing the input in a database, to avoid SQL injection and XSS then those two functions are enough. If you are storing passwords, you must encrypt the passwords with one-way encryption (that is they can not be decrypted).
Let me expand my answer:
First of all, SQL Injection is a method where a malicious user will attempt to modify your SQL statement to make it do their will. For example, let's say you have a login form. By inserting one of the following values into an un-protected form, I will be able to log into the first account without knowing the username or password:
' or 1=1 --
There are many versions of the above injection. Let's examine what it does to the SQL executed on the database:
The PHP:
mysql_query("SELECT * FROM users WHERE username='" . $username."' AND password='" . $password . "';");
When the above is executed, the following SQL is sent to the database:
SELECT * FROM users WHERE username='' or 1=1-- ' AND password='' or 1=1--';
The effective part of this SQL is this:
SELECT * FROM users WHERE username='' or 1=1
as the double dash (with the space afterwards) is a comment, removing the rest of the statement.
Now that gives the malicious user access. With use of an escaping function such as mysql_real_escape_string, you can escape the content so the following is sent to the database:
SELECT * FROM users WHERE username='\' or 1=1-- ' AND password='\' or 1=1--';
That now escapes the quotes, making the intended strings, just that - strings.
Now let's view some XSS.
Another malicious user would like to change the layout of a page. A well known XSS attack was the Facespace attack on Facebook back in 2005. This involves inserting raw HTML into forms. The database will save the raw HTML and then it will be displayed to users. A malicious user could insert some javascript with use of the script tag, which could do anything javascript can do!
This is escaped by converting < and > to <l; and > respectively. You use the html_special_chars function for this.
This should be enough to secure normal content on a site. However passwords are a different story.
For passwords, you must also encrypt the password. It is advisable to use PHP's crypt function for this.
However, once the password is encrypted and saved in the database as an encypted password, how can you decrypt it to check that it is correct? Easy answer - you don't decrypt it. HINT: A password always encrypts to the same value.
Were you thinking 'We can encrypt the password when the user logs in and check it against the one in the database', you are correct...
Depends on what you're using the input for and where it's going.
the only thing to assume when sanitizing input for php applications is that there are a lot of smart people out there, and if they really wanted to break your app, they'll find a way to do it.
following the guides you listed and other guides are a great start. but then every bit of sanitizing you do will incur a non-trivial amount of time and memory to perform.
so it all really depends on what you're using the input for and where it's going.
mysql_real_escape_string and html_special_chars are nice utilities, but you shouldn't depend solely on them and they're are also not always needed.
don't get discouraged as even mature and well programmed sites have to keep up with the latest in xss and other attacks. fun game of cat and mouse.
one place you can start is with a framework, i like codeignitor. go through their security classes to see how they deal with it.
or better yet, write a simple form processor and try to come up with ways to run arbitrary code yourself. you'd be surprised with how many ways you can come up with.
Assuming you are asking about security, there is one problem with mysql_real_escape_string.
This function has absolutely nothing to do with security.
Whenever you need escaping, you need it despite of "security", but just because it is required by SQL syntax. And where you don't need it, escaping won't help you even a bit.
The usage of this function is simple: when you have to use a quoted string in the query, you have to escape it's contents. Not because of some imaginary "malicious users", but merely to escape these quotes that were used to delimit a string. This is extremely simple rule, yet extremely mistaken by PHP folks.
This is just syntax related function, not security related.
Depending on this function in security matters, believing that it will "secure your database against malicious users" WILL lead you to injection.
A conclusion that you can make yourself:
No, this function is not enough.
Prepared statements is not a silver bullet too. It covers your back for only half of possible cases.
I'm familiar with using mysql_real_escape_string() and the PHP FILTER_SANITIZE function to prevent sql injections.
However, I'm curious as to how I would determine within a PHP script whether or not user input was a likely sql injection attempt? That would be useful for tracking potentially malicious IPs.
If the output of mysql_real_escape_string is different to the input, then the input contained unsafe characters. You could infer that the user might have been attempting an attack, especially if the field in question is one where you'd normally expect a low number of unsafe characters (e.g. a zip code).
But it might also be because their name happened to be Robert'); DROP TABLE Students; --.
So in general, there is no way to do this that's even close to reliable.
Simple answer is you cannot. Just write code that assumes that the serve is going to receive a pounding then you cannot go wrong.
This is a very hard problem to solve, automatically detecting which SQL queries are attacks (or simple mistakes).
There are companies who make products that attempt to do this, like GreenSQL and DB Networks ADF-4200, by applying heuristic tests to see if queries look "suspicious."
But even they rely more on whitelisting the queries that your application runs. The heuristics are known to have both false positives and false negatives. And there are whole categories of queries that neither whitelisting nor heuristics can catch, like calls to stored procedures.
Someone mentioned mod_security too, but this requires a lot of configuration, and then you're back to hand-coding rules for whitelisting your application's legitimate queries.
Just follow good practices like parameterizing and whitelisting, so that user input (or any untrusted content) is never evaluated as code.
Actually there is no certain way !
but it is possible to guess attacks !
simply check for most common usefull sql injection structures
for example scan this words (in case insensitive) in your inputs :
union
select
drop
--
;
if you know how to stop sql injection , you shouldn't be worried and you can run the query safely. but as I understood you want to detect injections , so i prefer you just log suspicious inputs and then decide manually ! in most cases logged queries are real injections.
Test for '--' and ';' strings ... maybe also OR, AND, (, etc. But you can never be 100% sure it WAS an attack.
I would say it's safer to assume that ALL user input is an attack when you write your code and make your program secure enough to mitigate the attack rather than trying to retroactively fix something that may or may not have been an attack.
You can try the following:
If there are no characters to be escaped in the input string, then
it's not an attack attempt.
Put the user input in the query without quoting.
Try to check the query syntax without running it (e.g. make mysql do it for you somehow (?)).
If there is no syntax error, then it was an attack attempt.
But this way you can only detect potentially successful attack attempts, not those which give parse error.
You may search for certain keys (union,delete,drop, ...) and so on. Search for --, \n (new lines) if you are sure that original query would never ever have it. Create a query that always returns some value - if with user malicious input it doesn't that may indicate an attack.
But because users tend to ALWAYS make mistakes or attack servers (number, i will write letters to check how much mess it will make...) all user inputs should be filtered before used.
(joke deleted)
EDIT: You can try the following:
If there are no characters to be escaped in the input string, then
it's not an attack attempt.
Put the user input in the query without quoting.
Try to check the query syntax without running it (e.g. make mysql do it for you somehow (?)).
If there is no syntax error, then it was an attack attempt.
But this way you can only detect potentially successful attack attempts, not those which give parse error.
I use CodeIgniter, and having trouble with hacking. Is it possible to make SQL Injection to the login code below:
function process_login()
{
$username = mysql_real_escape_string($this->input->post('username'));
$password = mysql_real_escape_string(MD5($this->input->post('password')));
//Check user table
$query = $this->db->getwhere('users', array('username'=>$username, 'password'=>$password));
if ($query->num_rows() > 0)
{
// success login data
Am I using the mysql_real_escape_string wrong, or what?
No what have posted is not probably not vulnerable to sql injection. Although getwhere() could be doing a stripslashes(), I'm not sure.
Its likely that if there was SQL Injection that it is in another part of your application. The attacker could use this vulnerability to obtain your extremely weak md5() hash, crack it, and then login. Use any member of the sha2 family, sha-256 is a great choice.
If your site has been defaced then I seriously doubt that it is sql injection. Its difficult to automate the exploitation of sql injection to deface websites, but it is possible. I would make sure that all libraries and installed applications are fully updated. Especially if you have a CMS or forum. You could run an OpenVAS scan against your site to see if it finds any old software.
Database
Judging by your code I see you're not using the lastest CI version (2.0.2 as of 06/12).
As stated in the changelog the getwhere() function (which is now called get_where()) has been abandoned as for version 2.0. As for everty application out there you're strongly suggested to upgrade your current version, as there has been a lot of bugfixes in the meantime and you should always rely on the safest version available.
mysql_real_escape_string usually is considered 'enough' to give a good level of safety in your queries, but as it happend to its predecessor (mysql_escape_string) it isn't 100% safe against all kind of attack, so relying interely on that is not the best practice around. Although safe, there are still attacks that can go past this filter.
Check, among the many, this question on SO for further information about this.
In codeignier:
If you were developing your custom application, I'd suggest you to at least use the mysqli extensions or, better yet, the PDO class; prepared statements are undoubtely safest and should be favoured over everything else.
But we are in the framework context, and Codeigniter comes with 3 great ways of safely querying your database, applying the right tool to the right input without you having to worry about that. I'm talking about query bindings and manual escaping with $this->db->escape() family and the Active Record Class
You can find examples of use at the urls I just linked, or read the answers from other peers here, so I won't go into the details of each procedure in this post.
Password
Regarding your password, as already stated by other users, md5() is a now flawed hashing alghoritm. There are rainbow tables out there that can crack your md5 password in a relatively short amount of time, so you're better off with higher security level hashing algorhytms, like sha1() or sha256, sha512, and other
In codeigniter:
Codeigniter comes with a security helper class, which provides you with a handy function, do_hash() (might be dohash() in your older installation), which can be given the hashing alg. as paramter (currently I think it supports only md5 and sha1) and defaults to sha1() anyway.
Other observations
I'm not entirely clear on why you blame your login for your SQL injections. Are those the only 2 forms in your whole application? You dind't provide the info to tell if you use $_GET parameters or you follow the native URI segmentation, but I believe you're doing like this so I assume you're safe from this point of view. You should make sure then that there's no other input form in your website which contains input going into the database, otherwise you can secure your login how much you want, but someone could penetrate through a backdoor and read from there your database table and get log into your website in a "legitimate" way.
Moreover, there can be other source of intrusion, like a compromized cookie for example. As a piece of advice, whenever you choose to use a framework (and you're doing yourself a bigger favour than developing from scratch and all by yourself) you should tend to use MOST of its features, expecially when it comes down to security. It's a huge and very delicate question, so you MUST give this topic your top priority, and a well developed framework, with a huge community and frequent updates is the closest to safety you can get. Therefore, you're adviced to update your CI installation (guides can be found here in their manual. Choose your version and follow the instruction), always use the top tools you're given for each task, and don't think that barring your door will make you safe from an intrusion from your windows. Always check thoroughly and investigate all possibile causes.
Late Addendum: Don't forget XSS, CSRF, session fixations, and other hot security problems.
Have a look at this old SO question.
I would use:
$sql = "SELECT * FROM users WHERE username = '?' AND password= '?'";
$dbResult = $this->db->query($sql, array($this->input->post('username')),array($this->input->post('password')));
If this is an ongoing hack, then seriously consider putting some logging in place to record the username/password in a file somewhere. If it is an sql injection via this login snippet, then it would show up in this new log file somewhere. And while you're at it, if you can, log the generated SQL query as well.
In any case, remember that mysql_real_escape_string() only covers mysql's metacharacters: single-quote, double-quote, semi-colon, etc... It is still entirely possible to hack the login function via mangling of a boolean parameter. Impossible to say if your "getwhere" function is vulnerable, but consider the case where the submitted password is "xyz OR (1=1)". The generated query might end looking something like
SELECT id FROM users WHERE users=someusername AND password=xyz OR (1=1);
Perfectly valid query, and has also passed through mysql_real_escape_string intact because it didn't contain any of the critical metacharacters.
It's my understanding that Active Records will properly escape the values for you. So, with $query = $this->db->getwhere() you do NOT need to use mysql_real_escape_string().
But, you should always use some sort of Controller validation. In particular, you can limit the username and password to certain characters via a regular expression. This is always suggested.
Furthermore, if you are getting hit with typical attacks often then look into using PHPIDS. It's an Intrusion Detection System that will help you prevent the attacks from actually causing any damage, as you can thrown an error or notice of some sort.
You may also wish to view this tutorial on Security issues.
http://net.tutsplus.com/tutorials/php/codeigniter-from-scratch-security/
If all your functions and accessors do what their names seem to suggest, then this script is not vulnerable to SQL Injection.
As for whether you are using mysql_real_escape_string properly... yes and no. Yes, you have the right idea, but you're actually using it excessively. If you MD5 the password, then that input is now clean and can't be used for injection, so the mysql_real_escape_string call is superfluous. So this would be fine:
$username = mysql_real_escape_string($this->input->post('username'));
$password = MD5($this->input->post('password'));
As previously mentioned, MD5 is a rather weak hash algorithm by itself. Look into SHA and salting a hash.
To counter a previous answer posted, if a user inputs "xyz OR (1=1)" as a password, that poses absolutely no threat to his script for two reasons.
1) MD5 hashing would convert that to a harmless hash (e.g. 'd131dd02c5e6eec4693d9a0698aff95c'), making the query something along the lines of SELECT id FROM users WHERE users='username' AND password='d131dd02c5e6eec4693d9a0698aff95c';
2) mysql_real_escape_string prevents 'string' injections, that is injections that would require a single or double quote to break out of the expected SQL query, but does not prevent (for example) injection with numerical input, which you would need to use type-casting to block.
Everything is really contingent on your "$this->db->getwhere" call. If that just builds the query normally without doing anything funky (like stripping quotes, or treating strings as integers [which would throw an error anyway], etc) you are simply not vulnerable in this script. If you could give more details on the 'type' of hacking you've experienced, we could probably give you some guidance where to look. Were you defaced?
I haven't played with CodeIgnitor, but expect that their built in input/database functions wouldn't do anything weird like that - so unless you edited the getwhere function yourself, you're probably fine in this particular spot.
The code you provided is not vulnerable for SQL injection, but you should never put passwords into queries, not even hashed, because you cannot be sure if the query is saved to the server's log or not, and you usually don't have control about who can have access to that.
Query/load the user's record by username, and then compare the hash of the password in $_POST to that.
The code seems to resistant to injection attacks. You are taking md5 of password field which leaves out any injection attack on that field. The vulnerable field is username which you are escaping anyhow. Are you sure that this is an injection attack?
Is there any other field like access_token fields passed along to prevent CSRF
Following are probable causes of strange behavior:
You are using mysqli driver in database config and escaping string according to mysql lib. Try providing $this->db->conn_id as second parameter to mysql_real_escape_string and check your php logs for warnings.
mysql_real_escape_string needs connection link identifier as second parameter. If you don't provide it you will experience strange behavior. I have suffered this with codeigniter. In my case mysql_real_escape_string was returning empty strings. If the case is same with you, check that you don't have empty passwords and usernames in your users table (very less probable).
It has a been a long day but I cannot seem to choose in my own head which is better or if I should use both.
Basically what should I use to sanitize user inputted values. Is it either the htmlentities or preg_match function ?
I will then if the value goes into a sql query use the mysql_real_escape_string function but only until I change it to a prepared statement then I can remove this.
Or would it be good idea to use both htmlentities and preg_match ?
Why didn't you just ask this in your previous question ?
Use preg_match before you do any escaping, to ensure the data meets the whitelist of what you expect it to be. Then use the escape for the database insertion. This is called defense in depth (i.e. more than one layer of security checking, in case the attacker can break through the first layer).
If your using PHP 5.2+, you should look into the Filter functions to sanitize your data.
http://php.net/manual/en/filter.examples.sanitization.php
Its better to have too many validation checks and sanitization routines than too few. The system is no more or less secure by adding redundancy. Ether its a vulnerability or its not, its a Boolean not a Float. When I am auditing code and I see redundant secuirty measures I think of it as a red flag and it encourages me to dig deeper. This programmer is paranoid and perhaps they do not understand the nature of vulnerabilities although this is not always true.
There is another problem. htmlentities() doesn't always stop xss, for instance what if the output is within a <script></script> tag or even an href for that matter? mysql_real_escape_string doesn't always stop sql injection, what if: 'select * from user where id='.mysql_real_escape_string($_GET[id]);. a preg_match can fix this problem, but intval() is a much better function to use in this case.
I am a HUGE fan of prepared statements. I think this is an excellent approach because by default it is secure, but passing a variable to mysql_real_escape_string() before a prepared statement is just going to corrupt the data. I have seen a novice fix this problem by removing all validation routines thus introducing a vulnerability because of redundancy. Cause and Effect.
Web Application Firewalls (WAF) is an excellent example of how layers can improve security. WAF's are highly dependent on regular expressions. They try to look at the bigger picture and prevent nasty input or at the very least log it. They are by no means a silver bullet and should not be the only security measure you use, but they do stop some exploits and I recommend installing mod_security on production machines.
Basically what should I use to sanitize user inputted values. Is it either the htmlentities or preg_match function ?
Certainly not htmlentities, probably not preg_match either (for security purposes). You change the representation of any output to the medium its going to (htmlentites fora web page, urlencode for URL, mysql_real_escape_string for a mysql database....).
If someone really wants to register on your application as dummy' UNION SELECT 'dummy' AS user,'dummy' AS password FROM DUAL then let them!
Writing your code to insulate it from attacks is a lot more effective than trying to detect different types of attack in advance.
Some data input may have to match a particular format for it to be of any use - and there may be a delay between the data capture and the use of the data - e.g. if the user is asked to input an email address or a date - in which case preg_match might be appropriate. But this is nothing to do with security.
C.
I've got a chunk of code that validates a user's username and password, which goes something like this:
$sql = "SELECT *
FROM user
WHERE
username='{$_POST['username']}' AND
password=MD5('{SALT}{$_POST['password']}')";
Is this any more/less secure than doing it like this?
$sql = "SELECT *
FROM user
WHERE
username='{$_POST['username']}' AND
password='".md5(SALT.$_POST['password'])."'";
Regardless of where/if escaping is done, is the first method vulnerable to sql injection attacks? Would the answer be the same for other database engines besides MySQL?
You should use prepared statements instead and have a look at this question.
Speaking about injection, both ways are secure, if you properly escape variables.
The first case will be more vulnerable, if you use complete query logging, and so the password will appear as plain text.
Besides, if your system is affected by some virus that works as proxy between your script and database, it'll be able to catch your password.
One last problem that you may encounter (quite rarely, in fact), is when the system is inflicted with a virus, that reads sensible data from memory.
I hope this makes sense.
Oh god, please tell me you're doing some type of mysql_escape_string or mysql_real_escape_string or AT LEAST addslashes or addcslashes to any $_POST variables before you insert them into a raw MySQL statement?
I think the most secure way to do this is to:
a) use filter_var or preg_replace to get rid of extraneous characters from the $_POST['username']
b) SELECT the row by the username from MySQL (also grabbing the digested password)
c) compare the message digested version of the password from the $_POST to that of the retrieved row (assuming you don't leave your password cleartext) in your application code, not in the SQL statement
If you do it this way, there's only 1 possible place for injection (username), and it's pretty impossible when you're doing a preg_replace( '/\W/', '', $_POST['username'] ) which removes anything not A-Za-z0-9_- (or change to your username whitelist of characters).
However, if you're doing rock-solid proper sanitization, it really doesn't matter where you do your comparison. Theoretically, though, I'd allow for the least possible interaction with user input and raw SQL statements (i.e. only SELECTing by username and comparing outside of your DB).
To start off with MD5 is prevent to be an insecure algorithm and should never be used for passwords. You should use a staled sha256 and most databases do not have this function call. But even if the database did I think its a bad idea. Not a very bad idea, but its best to keep as few copies of your password around. Often the database can be on a completely different machine, and if that machine where compromised then the attacker could obtain clear text passwords by looking at the quires. In terms of SQL Injection, there is no difference in security and judging by your queries you should be more worried about SQL injection.
Regardless of where/if escaping is done, is the first method vulnerable to sql injection attacks?
SQL injection will not occur if proper escaping and sanitizing takes place
Would the answer be the same for other database engines besides MySQL?
I think you should look more at the expense taken to perform one action over another. The first method would take less time to execute than the second method, ceteris paribus.