i always like to use other sanitazing methods (and prepared statments) but i am under certain conditions where the client doesn`t want me to change a single line from his original script (im only going to add a plugin)
im worried someone could exploit this by modifying the $POST values and he could blame me (like clients usually do):
extract($_POST, EXTR_PREFIX_ALL, "POST");
$q = sprintf(ARQ, trim($POST_string));
$res = doexec($q);
DEFINE("ARQ", "INSERT INTO library (string) VALUES ('%s')");
i tried several combinations but all i could get was an error, no mysql inject
so, is this secure enough, as is?
I suppose doexec calls something that does not accept multiple commands chained with ;? You could still use a subquery to insert passwords and other sensitive data from other tables (assuming the user can read back the data he saved - otherwise you can use a timing attack). If the filesystem security is as poor as that of the database access code, you can use SELECT INTO to create a php script you can execute.
Related
I have discovered that some old (2009) code that was written for a website, did, under certain circumstances on a search query save the SQL as a a $_GET variable!
When the search was carried out, the details are POSTED and then sanitized, and the results are paginated with the LIMIT clause in MySQL. If there is more than one page (ie +30 results) the pages are anchor links in the HTML with a GET var containing the SQL statement.
I know, this is absolutely not the way to do this. It's old code I've just seen it by chance. This needs to be fixed.
So I've fixed it, sanitized it and used an alternative method to reload the SQL, BUT:
My question is thus:
The page outputs the data relating to thumbnail images, all data is output as named array var (the original clause is a SELECT * clause), so if someone does abuse the GET variable, the page itself will only output the columns named,
I have managed to DELETE rows from the DB using the GET abuse, I would like to think the abuse is only effective if the result is not involving any returned output (such as DELETE) but I don't know; so given that the user can input anything into the GET clause but only get the displayed output of what's coded (ie named columns in a 30 row array) -- what other abuses can this gaping hole be open to?
Further details: The code is MySQLi
A tool like SQLMAP can probably take over the entire server and do with it whatever the user wants.
Having an unsanitized database input isn´t even hacking anymore, it´s waiting for someone to run a script on your machine and basically own it from that point on.
What the attacker can do depends on your database configuration and database user access. If you create a new user with a permission to only SELECT that one specified table, and use that user for that particular script, the harm it can do is reading data from that table.
Still this is bad practice. Never use it.
So far I store all the relevant parameters that I need for my website as variables in a php file; then I point to this file through a requirestatement in each page that needs those parameters.
This is most of times good and easy to mantain, but sometimes I need to change those variables "on the fly" and I feel the need of some sort of web-panel where I can change them even more easily (I'm thinking to a web page with a form to update the parameters).
So I've created a table in my MySQL database to store parameters (basically, this table has two columns: ParamName and ParamValue; I've stored the parameter names as a varchar without the $ sign at the beginning), in order to create the web-panel I've in my mind.
I was thinking to extract all the parameters names and values using this query:
$query=$mysqli->query('select * from parameters');
while($row=mysqli_fetch_assoc($query)) {
$ParamName=$row["ParamName"];
$$ParamName=$row["ParamValue"]; }
Is this a good or a bad idea?
What are the main issues I could encounter doing so (in terms of security, too)?
How deprecable and dangerous is the use of $$ParamName?
I'd better change my mind or can I proceed this way?
EDIT: I changed mysql_ syntax into mysqli_ as suggested.
Using arbitrary values in a database as variable references is highly risky. What you'd want to do is fetch the data from your key/value store into a self-contained associative array and use that instead.
You also do not want to be using mysql_query for anything these days. Please, put that thing away.
If the parameters are all constants that will not be changed over time, it is faster to use a single file that stores them as constants in a simple associative array.
If these parameters will change, using a database that specializes in key-value stores might be more useful for you instead of using a standard relational database.
If you cannot change your database from a relational database, or you are not changing the values often, it would be faster to cache them using something like memcached.
A coworker of mine has written something really awful.
Our boss wanted to be able to write any SELECT queries from the Back Office of our website, then get the result in CSV format.
Those queries will be executed by our PRODUCTION MySQL cluster.
This back-office feature should reject any non-SELECT queries.
So he comes up with a really naive solution for that.
This is the PHP code:
function checkQuery()
{
$sQuery = trim($_POST['query']);
if (empty($sQuery))
return false;
$sCmd = substr($sQuery, 0, 6);
if (strtolower($sCmd) != 'select')
return errorDiv('Only SELECT queries are authorized');
return $sQuery;
}
For people not knowing PHP, this code removes white space from the begining and the end of an SQL query string, then get the 6 first characters, transforms them to lowercase characters, and if it doesn't match (erf... loosely match) 'select', the query is rejected.
It looks awful and disgusting to me.
I tried to convince him to create at least another MySQL user, with limited privileges, but he is too lazy to do that.
However I cannot prove him that some kind of hacks are possible.
He uses mysql_query() to run the query string, and this driver rejects multiple queries at once.
I can't find any real exploit, but I think there are at least 50% of chance that something bad can happen.
Maybe some NUL char, or some obscur utf-8 chars can do the trick?
The correct approach to this feature (if you can't convince him it is a bad idea) is to run the queries as a limited MySQL user. Grant that user only the SELECT permission. You can also limit the table set if desired.
I do not see any way to bypass your colleague's checkQuery routine. It would be trivial if you weren't using the mysql_* functions in PHP, but as they only allow a single statement to be executed they at least aren't vulnerable to such classics as the Little Bobby Tables style of attack. And in fact, this is one of the key reasons why the standard query functions in both mysql_* and mysqli_* don't allow multiple statements (this is described in the mysqli Multiple Statements documentation.)
I've looked at other routes and I'm pretty satisfied that your colleague's code does actually work. (There's a way of generating false positives, e.g. triggering the detection even on a valid SELECT query, but that's not much use to you.) And as long as you don't have functions that change data as a side-effect, it shouldn't be possible to do anything other than SELECT with a statement that begins SELECT....
There's a possibility of writing new files to the server using SELECT ... INTO OUTFILE. That's restricted to new files, so you can't clobber /etc/passwd, or whatever, but there are dangers.
The most dangerous scenario I can see would be if your MySQL server is the same machine as your PHP server, your MySQL user can write to your PHP directory, and the user you're using has FILE privilege. In that case, a carefully crafted SELECT ... INTO OUTFILE along these lines:
SELECT '<?php echo \'DANGER WILL ROBINSON!\';' INTO OUTFILE '/Users/matt/Sites/dangerous.php'
might allow someone to create a new PHP file which could be executed by then browsing to it, at which point someone can execute arbitrary PHP code. I tested this potential exploit locally and after giving a user SELECT and FILE privilege and allowing write access by MySQL to my wwwroot, I could successfully create a PHP file with the above code in it and execute it in my browser.
Apart from attacks along that particular line, which may be impossible if the user you're currently using doesn't have FILE privilege, I can't see much danger. I'm aware this doesn't help you much, but it does seem to be the answer to your question.
However, I'd still strongly advise that you use MySQL authentication and security to create a new user for this application that only has SELECT permission. This is the built-in, database-level, utterly standard, well-proven, well-tested way of solving the problem, and limits potential future problems.
For example, if someone hacks your PHP server in some other way, to the extent of being able to read your MySQL user's password, they then have access to an authenticated user with high privileges they can use for other attacks (perhaps by throwing their own scripts onto your PHP server and running them.) If the MySQL user is SELECT-only, that limits the damage that can be done to your data/database even if your PHP server is completely compromised.
I would like to be nice to my users but don't want to get malicious code by mistake.
Looking in SO many questions I decided to go whitelisting, so I get only characters I approve.
How is it possible to be kind to the user, so I let them enter many characters and still keep my inputs safe from malicious code?
I am using Apache with PHP 5.3.8, I haven't incorporated PDO because the time I heard of it it I was deep into the project and feared it might be too big of a change.
Any ideas would help!
If it can be output to a user, you must prevent the potentially malicious user from including HTML tags in his code. For example, if, in that post, I could include a script tag, that could be very dangerous for any user reading my post. To prevent this, you want to use htmlentities:
$clean_data = htmlentities($_POST['data']);
That way, my <script> tag will be translated as <script> so it won't harm users when displayed by their browsers.
Now, if you want to save my post in your database, you must beware of SQL injections. Let's say you're saving my post with that query (yes, you shouldn't use mysql_* functions as they are deprecated, but that's just to explain the idea) :
mysql_query($db, "INSERT INTO posts(data) values('$clean_data');");
Sounds good ? Well, if I'm nasty, I'll try to include that post :
test'); DELETE FROM posts; SELECT * FROM posts WHERE data = '
What your MySQL gets is then
INSERT INTO posts(data) values('test'); DELETE FROM posts; SELECT * FROM posts WHERE data = '');
Ouch. So, you must basically prevent your user from including quotes and double quotes in his post, or, more precisely, you should escape them. That really depends on the library you are using, but in the obsolete one I used, that would be written :
$really_clean_data = mysql_real_escape_string($db, $clean_data);
mysql_query($db, "INSERT INTO posts(data) values('$really_clean_data');");
So, with the above malicious post, MySQL would now receive
INSERT INTO posts(data) values('test\'); DELETE FROM posts; SELECT * FROM posts WHERE data = \'');
Now, to MySQL, the whole INSERT INTO posts(data) values('test'); DELETE FROM posts; SELECT * FROM posts WHERE data = ''); part is a correct string, so what happens is what you want to happen.
Basically, that's all you need in almost all the cases. Just remember that, when you feed user data to an interpreter (it can be a web browser, a SQL engine or many other things), you should clean that data, and the best way to do it is to use the dedicated functions that should come with the library you are using.
Just to add to the answer that Fabien already gave, text data types are not the only item you need to be concerned with. Numbers are just as important. Type casting your variables is one way to handle that. For example,
$really_clean_data = mysql_real_escape_string($db, $clean_data);
$query = "UPDATE posts SET post = '".$really_clean_data."' WHERE id = '".(int)$_POST['id']."'";
All data submitted, including the id number of the post you'll be looking to update, is just as suspect and just as open to malicious fiddling as a textarea or text input entry. When you know it should be an integer (number) you can cast it as such. If it's not numeric, that (int) will cast it as 0. You can't update row 0 so the SQL query will fail.
To check for that failure prior to executing the query, you can check the posted data to see if it is a number using the is_numeric() function.
if(!is_numeric($_POST['id'])){
die("Post id is not a number.");
}
For input fields, most notably username and password fields, you can set a max length and check. For example,
if(strlen($_POST['username']) > 24){
$error = "username is too long";
}
If you can get creative about it, preventing SQl injection is actually fun! Keep in mind that a year from now everything on this page could be completely outdated and irrelevant. Security is a process, not a step, so keep on top of it.
I'm trying to update the database library that we use at work to use parameterized queries so that coworkers who are not very knowledgeable about SQL injection won't have to remember to escape input and just pass in an array of parameters instead (I'm using pg_query_params).
However, I am running into a problem. One of the requirements of the database library is that it logs each query that is executed and I can't figure out a way to get the text of a parameterized query once the parameters have been filled in. Is there any way to do this (aside from rolling my own function for parameterized queries, I guess)?
In other words, when executing a parameterized query like
pg_query_params('SELECT id FROM table WHERE foo = $1', array('bar'));
I want to get something like
SELECT id FROM table WHERE foo = 'bar'
PostgreSQL extended protocol (which is used by pg_query_params) separates parameters from queries.
The queries are ultimately constructed only on the server side, there is no way to construct them on the client side using only PostgreSQL's capabilities.
You can substitute the values of the parameters provided and log the query before sending the params using preg_replace.
Update:
You can enable PostgreSQL logging which will log the queries into a file or syslog (including bound parameters) on the server side, then periodically load this file into a PostgreSQL table.
Unless you can replace every occurrence of pg_query_params() in your source code with your own wrapper function (if you can, then you might want to use the auto prepend functionality in PHP to include the declaration of the wrapper) then you can only really do this at the database level.
C.