Background
I have been in the process of developing a Content Management System from scratch. It is primarily being developed from scratch for experience reasons, but it will also be used down the road.
I developed a DatabaseManager class which extends the mysqli class in PHP. I am using it do MySQL queries and connections. A function I have developed passes both the SQL query string (which is parameterized) followed by an array of the correct values to be replaced.
TL;DR
Long story short, I'm using a ReflectionClass to bind the parameters to the SQL query and execute. I am curious if this is a secure approach, or is there another method that would be best seen fit?
The Method
This is the DatabaseManager::do_query() method:
function do_query($sql, $values){
if(!isset($this->connect_error)){
$num_vals = count($values);
$i = 0;
$type = "";
while($i < $num_vals){
if(is_int($values[$i]) == true)
$type .= "i";
elseif(is_string($values[$i]) == true)
$type .= "s";
$i++;
}
$i = 0;
while($i < $num_vals){
$values2[$i] = &$values[$i];
$i++;
}
$values2 = array_merge(array($type), $values2);
$expr = $this->prepare($sql);
if($expr != false){
$ref = new ReflectionClass('mysqli_stmt');
$method = $ref->getMethod("bind_param");
$method->invokeArgs($expr, $values2);
$expr->execute();
return "Success";
}else{
$error_string = "Error: Query preparation resulted in an error. ";
$error_string .= $this->error;
__TGErrorHandler(TG_ERROR_DB_PREP);
return $error_string;
}
}
}
I have not run into any direct errors by testing, and it seems to hold up against SQL injections, using prepared statements. Is there any underlying issues with the structure of this method, though?
P.S. I am handling SELECT statements in a different way. This will handle DELETE and INSERT statements primarily.
First of all, you're on the right track. Only few, may be one out of hundred PHP users even come to the idea of using such a useful function.
Next, Reflection is just superfluous for this task, call_user_func_array() is a way to go. Even though it's a little tricky, it's a straightforward way, while Reflection is not.
Finally, to the security. I would remove automatic type detection and bind all the parameters as strings. It won't do any harm, while with your current approach there is a chance that database will return you bizarre results, if you happen to compare a number with a string from database.
Error handling in this function is questionable too.
There's nothing inherently insecure about using reflection here, but it's completely unnecessary. You can accomplish the exact same thing without reflection using call_user_func_array:
if ($expr !== FALSE) {
call_user_func_array([$expr, "bind_param"], $values2);
$expr->execute();
...
Alternatively, consider using PDO instead of mysqli. PDOStatement::execute() can take an array of bind parameters as an argument. (Also, using PDO means your code may be portable to databases other than MySQL!)
Related
I'm doing something where I need to store if statements in a mysql database. I need to pull these statements and use them, each one is an algorithm that I'm testing. I know you can store an if statement in a variable like "$abc = $x > 1 && $f == 1;" and if you run if($abc) {} it will work, I figured I could do the same here, but when I run if($abc) with $abc being an if statement from the database it's not working, it's running the code within the if statement because $abc exists. Code is as follows...
$getAlgorithms = mysqli_query($mysql_connect, "SELECT * FROM `algorithms2`");
while($algorithms = mysqli_fetch_array($getAlgorithms)) {
$algorithmID = $algorithms['id'];
$algorithm = $algorithms['algorithm'];
if($algorithm) {
echo("HELLO WORLD");
}
}
dummy example of what $algorithms['algorithm']; would pull: $r >= $var_x && $z <= $var_y && $lz >= $var_c
I'd really appreciate the help, this is very important. If you have any questions please ask.
NOTE: This is a 100% internal platform
Your code needs to make use of eval() to work as-is:
$getAlgorithms = mysqli_query($mysql_connect, "SELECT * FROM `algorithms2`");
while($algorithms = mysqli_fetch_array($getAlgorithms)) {
$algorithmID = $algorithms['id'];
$algorithm = $algorithms['algorithm'];
if(eval("return {$algorithm};")) {
echo("HELLO WORLD");
}
}
However, executing arbitrary code from an external source (the database) is potentially a horrible security risk: just because you're expecting $algorithm to be a benign arithmetic expression doesn't mean that it can't be a malicious function call or other statement, for example if someone can enter system('rm -rf /') as the algorithm into your database, you're probably going to have a bad day.
Without knowing the precise problem you're trying to solve, it's hard to suggest a better solution, but I'd favour putting the "algorithms" in an array or other hard-coded data-structure within your code rather than the database, it's far safer as anyone who can alter that list can already execute arbitrary code.
For further reference: http://php.net/manual/en/function.eval.php
Sounds like you're looking for eval(), but note that it is especially dangerous to use if there's any chance someone besides you will be creating the strings. There is probably a better, safer way to achieve whatever it is you are trying to do here.
Store the If condion in your db as string. And then execute it using eval() php function .
usage
mixed eval ( string $code )
PHP eval documentation
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to prevent SQL injection in PHP?
I wanted to ask a few questions about protecting against sql injection. From what I've been reading I keep coming across these three things:
stripslashes
which is used in conjunction with magic_quotes_gpc
mysql_real_escape_string (or mysqli I suppose in the newer php?)
The question is, should I be using both of these or will real_escape_string suffice?
For instance I have this line of code pertaining to a registration page (which I know for a fact is vulnerable as sqli helper let me find everything about my database :( as I've not implemented any of the above yet):
if(isset($_POST['submit'])){
//cleanup the variables
$username = ($_POST['username']);
$password = ($_POST['password']);
$email = ($_POST['email']);
$username = sanitise($username);
$password = sanitise($password);
$email = sanitise($email);
//quick/simple validation
if(empty($username)){ $action['result'] = 'error'; array_push($text,'Please type in a username'); }
if(empty($password)){ $action['result'] = 'error'; array_push($text,'Please type in a password'); }
if($action['result'] != 'error'){
$password = md5($password);
//add to the database
$add = mysql_query("INSERT INTO Users VALUES(NULL,'$username','$password','$email',0, 'First', 'Last', 'Phone Number Here', '...', 'Something about me...')");
if($add){
//get the new user id
$userid = mysql_insert_id();
//create a random key
$key = $username . $email . date('mY');
$key = md5($key);
//add confirm row
$confirm = mysql_query("INSERT INTO Confirm VALUES(NULL,'$userid','$key','$email')");
if($confirm){
//include the swift class
include_once 'swift/swift_required.php';
//put info into an array to send to the function
$info = array(
'username' => $username,
'email' => $email,
'key' => $key);
//send the email
if(send_email($info)){
//email sent
$action['result'] = 'success';
array_push($text,'Thanks for signing up. Please check your e-mail for confirmation.');
}else{
$action['result'] = 'error';
array_push($text,'Could not send confirmation e-mail');
}
}else{
$action['result'] = 'error';
array_push($text,'Confirm row was not added to the database. Reason: ' . mysql_error());
}
}else{
$action['result'] = 'error';
array_push($text,'User could not be added to the database. Reason: ' . mysql_error());
}
}
$action['text'] = $text;
}
?>
I thought my sanitisation function would help things - got it online, however it would appear it is a bit useless. Or perhaps it only helps against cross site scripting. Here it is:
function cleanInput($input) {
$search = array(
'#<script[^>]*?>
.*?</script>#si', // Strip out javascript
'#<[\/\!]*?[^<>]*?>#si', // Strip out HTML tags
'#<style[^>]*?>.*?
</style>
#siU', // Strip style tags properly
'#<![\s\S]*?--[ \t\n\r]*>#' // Strip multi-line comments
);
$output = preg_replace($search, '', $input);
return $output;
}
function sanitise($input) {
if (is_array($input)) {
foreach($input as $var=>$val) {
$output[$var] = sanitise($val);
}
}
else {
if (get_magic_quotes_gpc()) {
$input = stripslashes($input);
}
$input = cleanInput($input);
$output = $input;
}
return $output;
}
Would you suggest that function is useless?
If so, how would I go about securing the original bit of code? Namely:
$username = ($_POST['username']);
$password = ($_POST['password']);
$email = ($_POST['email']);
Whatever you do, Read about injection here. As you can see on this site, prepared statements are the way to go, preferably using PDO:
$stmt = $pdo->prepare('SELECT foo from db.bar WHERE foobar = :something;');
$stmt->execute(array(':something' => $_POST['something']));
There's no need to rely on deprecated features like magic-quotes or, in fact, anything in the mysql_* extension for that matter; as the latter is being deprecated entirely, and will eventually be removed from the language all together (hopefully some time soon).
In case you (or somebody else) is wondering why I think PDO is to be preferred:
It supports multiple drivers (MySQL, MSSQL, PostrgreSQL,... ) full list here
Its OO API is just more in-tune with the time, whereas mysqli_* offers a procedural API, too. This is regarded as a plus by some, mainly those who aren't to familiar with OOP, but you'll have to learn sooner or later.
Personally, I get the impression that PDO is more widely used, and since PHP is an open-source product, it's what the community uses that matters: it'll be supported for a longer period of time, it'll be tested better (and by more people) and if you're stuck, there's a lot more peer-support.
PDO emulates prepares, you can turn this off but that'll slow you down. Also, when emulating, PDO takes a closer look at the placeholders you're using. If, for example some wacky driver doesn't support named placeholders, you can still use them, but PDO will replace them with ?, or as the man pages put it "something more appropriate". All in all, PDO is quite clever :)
use prepared statements, PDO or mysqli i personally prefer PDO, but both will do the job.
None of the functions you listed are connected to SQL injections :)
stripslashes are just to recover from magic quotes
magic quotes is a mis-feature, already removed from language
mysql_real_escape_string (the way you're using it) - just a manual replica of magic quotes
as a matter of fact, escaping (done by magic quotes and real_escape) is only a fraction of a real formatting required for the properly built query. One have to follow whole set of rules, not just single one "escape everything and you're fine".
Nevertheless, there is nothing essentially wrong in escaping, as long as it is used in it's place, not as a magic wand protecting you from injections. For example, PDO is using it internally all the way.
Please note, that "use prepared statements" is not hte magic wand either, but rather the same mantra as "escape everything" - insufficient and unsafe as well.
Some explanations I posted before
Would you suggest that function is useless?
I would suggest that this function is quite harmful
Some explanations from the similar questiuon
any really good way of doing this?
Always use a placeholder to add a dynamical part to the query. It is safe and can be very convenient to use. Please note that prepared statements is not the only way to use placeholders and apparently not the most reliable one.
Also, bear in mind that using raw API calls in the application code, be it PDO, mysqli or mysql, are equally bad. One have to use an abstraction library to handle their SQL, not raw API functions.
Also, bear in mind that most of people who propagate PDO, never ever used it a real project, or at least some of it's "benefits". They actually just repeat the same text they just heard from others ;)
Say, "multiple database support" is not the thing that used frequently, and of course it is not that easy as just change the DSN string. It is quite toilsome task to switch databases, and different API functions would be least problem.
Or whoever who says "turning off emulation will slow you down" just never ever tried it in real :)
Or, ask someone who says "PDO is quite clever" to create a simple query with IN clause populated from an array ;)
I have a website hosted on a shared hosting.
They have php 5.2.13 installed.
I know the vulnerabilities of SQL Injection and I want to prevent it.
So I want to use PDO or mysqli for preventing it.
But the problem when I used phpinfo(); to view the hosting environment php setup info,
I found that there was no mysql driver for PDO and there was no support for mysqli in it.
So I wanted to know whether it will be safe to use that old mysql_* functions( along with
functions like mysql_real_escape_string).
I looked at this one on SO but it wasn't much helpful to me.
Prepared statements possible when mysqli and PDO are not available?
UPDATE:
I forgot to mention that most of the queries will be simple. There are no forms used so no user input will be used to make a query. All the queries will be hard coded with necessary parameters and they will not be changed once set.
No. The lack of more secure solutions is never a valid excuse to fall back to a less secure or more vulnerable solution.
You're much better off finding a different hosting provider that doesn't disable arbitrary PHP features even in their shared hosting packages. Oh, and try to get one that uses PHP 5.3, or better yet if you can, PHP 5.4.
If you're really rigorous about always using mysql_real_escape_string() with all user-supplied input then I think you should be safe from any SQL injection that prepared statements protects you from.
How perfect are you at this? I'll bet most of the buffer overflow vulnerabilities were created by programmers who thought they were good at checking inputs....
A good way to do that is to implement a Wrapper class for the use of the mysql_* functions, with a few methods to create prepared statements.
The idea is that you must pass strongly-typed parameters in your queries.
For instance, here is a piece of code with the general idea. Of course it needs more work.
But that can prevent from SQL Injection attacks if it's fairly implemented.
You can also search for 3rd party libraries that already implement that, because this is common.
<?php
class MyDb
{
protected $query;
public function setQuery($query)
{
$this->query = $query;
}
public function setNumericParameter($name, $value)
{
if (is_numeric($value)) // SQL Injection check, is the value really an Int ?
{
$this->query = str_replace(':'.$name, $value);
}
// else, probably an intent of SQL Injection
}
// Implement here the methods for all the types you need, including dates, strings, etc
public function fetchArray()
{
$res = mysql_query($this->query);
return mysql_fetch_array($res);
}
}
MyDb $db = new MyDb();
$db->setQuery('SELECT * FROM articles WHERE id = :id');
$db->setNumericParameter('id', 15);
while ($row = $db->fetchArray())
{
// do your homework
}
Here's an example wrapper for an SQL query
public function where ($col, $val)
{
if (!preg_match('~^[a-z0-9_]+$~i', $col))
throw new Exception('Invalid parameter $col');
$this->where.= "WEHERE $col = :$col";
}
Is this quite overkill since regex is probably using resources.
Note I am actually using this to wrap PDO (notice the colon in :$col).
If $col can be specified through user input then this is not overkill but rather your only defense against SQL injection.
If $col is known safe (for example your code produces its value with a switch statement) then it's probably not worth it to include the runtime check. But you should take into account the possibility of the "known safe" status changing as the program is maintained in the future.
From what I know, checking preconditions is a good practice. If a method needs an int value then it's a good solution to do use something like this:
public function sum($input1, $input2) {
if (!is_int($input1)) throw new Exception('Input must be a integer');
However after looking to the source code of Zend/Codeigniter I don't see checks like this very often. Is there a reason for this ?
Because it is difficult / inefficient to test each and every variable before you use it. Instead they check just input variables - check visitors at the door, not once inside the house.
It is of course a good defensive programming technique to test at least more important vars before using them, especially if the input comes from many places.
This is a bit off-topic, but the solution I would recommend is to test input variables like this:
$username=get('username', 'string');
$a=get('a', 'int');
...
$_REQUEST and similar should never be used (or even be accessible) directly.
Also, when doing HTML output, you should always use this:
echo html($username); // replaces '<' with '<' - uses htmlentities
To avoid SQL injection attacks one can use MeekroDB, but it is unfortunately very limiting (MySQL only, single DB only,...). It has a good API though which promotes safety, so I would recommend checking it out.
For myself I have build a small DB library that is based on PDO and uses prepared statements. YMMV.
Specifying such strict preconditions in any case is not necessary and feels not useful in a dynamical typed language.
$sum = sum("1", "2");
Why one should forbid it? Additional if you throw an Exception, one tries to avoid it. This means, he will test and cast himself
function sum ($a, $b) {
if (!is_int($a)) throw new Exception('Input must be a integer');
if (!is_int($b)) throw new Exception('Input must be a integer');
return $a + $b;
}
if (!is_int($value1)) { $value1 = (int) $value1; }
if (!is_int($value2)) { $value2 = (int) $value2; }
$sum = sum($value1, $value2);
Every is_int() occurs multiple times just to avoid unnecessary Exceptions.
Its sufficient to validate values, when you receive them, not all over the whole application.
Speaking about ZF, i'd say that they try to minimize it in favour of interfaces and classes. You can see in many definitions across ZF something like this:
public function preDispatch(Zend_Request_Http $request)
which is fine enough. Also at critical places where ints/strings are needed there are some sanity checks. But mostly not in the form of is_string() but rather as isValidLocale() that calls some other class to check validity.