views:

234

answers:

7

Am I doing this right? Will this help avoid sql injections?

$deleteid = htmlspecialchars(strip_tags(mysql_real_escape_string($_POST['listid'])));

mysql_send("DELETE FROM stage where listid='$deleteid'");
+2  A: 

Yes, using mysql_real_escape_string on values that are intended to be used in string declarations in MySQL statements will prevent you from SQL injections. That’s the exact purpose of that function.

But you don’t need the other two functions strip_tags and htmlspecialchars. Because these functions are used to either remove (HTML) tags and replace the HTML special characters by character references respectively. They are not intended to protect you against SQL injections.

In fact, using strip_tags and/or htmlspecialchars after mysql_real_escape_string could break the escaping under some certain instances (e.g. when using non-US-ASCII based character sets, see also addslashes() Versus mysql_real_escape_string()). So make sure that you use that function right before inserting its returned value into the SQL statement.

Apart from encoding the output using mysql_real_escape_string you could also validate the input using ctype_digit:

if (ctype_digit($_POST['listid'])) {
    mysql_send("DELETE FROM stage where listid='".$_POST['listid']."'");
} else {
    // invalid value
}

This validation ensures that only (positive) integer values are used in the query that don’t need to be escaped.

Gumbo
So the above code is 100% no problems with it before I apply this to the rest of the site??
That said, putting `strip_tags` and `htmlspecialchars` after it actually decreases the security of the system.
Billy ONeal
Exact purpose of mysql_real_escape_string function is to quote delimiters and nothing else. It's very common misbelief that this function is SQL inj. related. I's just a part of set of syntax rules. And should be used independently from SQL injection possibility. Of course. And it cannot protect anything but quoted strings. I find this term more appropriate for numbers casting.
Col. Shrapnel
+13  A: 

No.

You should call nothing but mysql_real_escape_string.

The htmlspecialchars and strip_tags functions are used to encode strings to be displayed as HTML.
They should not be used with SQL

SLaks
how do I do this???
+1... Well, either that, or at least make `mysql_real_escape_string` the outer most call...
ircmaxell
`mysql_real_escape_string($stuff_to_encode);`
Amber
@user: Just remove `strip_tags` and `htmlspecialchars`.
Billy ONeal
$deleteid = mysql_real_escape_string($_POST['listid'];mysql_send("DELETE FROM stage where listid='$deleteid'");
@user: Exactly.
SLaks
`mysql_real_escape_string($_POST['listid']);` should be enough. Or if you want to use `htmlspecialchars` and `strip_tags` (or any other function) you should escape their return value : `mysql_real_escape_string( htmlspecialchars(strip_tags($_POST['listid'])));`
a1ex07
that's correct... mysql_real_escape_string should do the trick. htmlspecialchars and strip_tags are useful to prevent XSS which doesn't apply to the example you show here.
Chepech
+8  A: 

It may prevent SQL injection attacks, but its a poor way to approach it. Use prepared queries instead.

Since your comment says you're systematically making changes to your whole site, go with the better approach. While you're at it, you may want to move to a non-MySQL-specific database API, in case you want to switch to another backend later.

Novelocrat
+3  A: 

Apart from the mentioned suggestions (only mysql_real_escape_string but even better prepared statements), I would like to add that it is always useful to analyse exactly the value you are trying to clean / make safe.

If your ID is supposed to be an integer, I would simply use intval($_POST['listid']) to make sure the result is an integer and reserve mysql_real_escape_string for strings (although I personally would use prepared statements / PDO).

jeroen
A: 

use stored procedures and grant execute permissions only to your app db user (plus the myriad of other benefits sprocs bring)

f00
But a poorly written stored procedure will still be open to injection since you can build queries via string concatenation... So it's not a simple "do this and done"...
ircmaxell
-1 for spreading the myth that stored procedures are magic proof against SQL injection. See http://is.gd/eLNEg
Bill Karwin
you're quoting me a WTF example lool
f00
Stored Procedure doesn't prevent injections, also the quoted WTF example makes total sense in this context.
Chepech
a poorly written WTF type of sproc doesnt prevent injections so you'd avoid writing any if that's possible - errrm, ummmm.
f00
you've quoted me the worst example of a sproc in the "history of history" which no reputable coder would put his name to and this is the basis of your argument for !using sprocs - pls.
f00
A: 

I always use a database helper class on all my projects.. that way I don't have to use mysql_real_escape_string keeping code clean and easy to read you'll be safe from injections if you forget to use it.

Below is an example the one I use...

<?php 
    final class DatabaseException extends Exception {
        function __construct($strErrMessage, $intErrCode) {
            parent::__construct($strErrMessage, $intErrCode);   
        }
    }

    class Database {
        protected $host     = ""; //database server
        protected $user     = ""; //database login name
        protected $pass     = ""; //database login password
        protected $database = ""; //database name
        protected $prefix   = ""; //table prefix        
        protected $connected = false;
        protected $db = null;   
        protected $record = array();
        protected $error = "";
        protected $errno = 0;

                //table name affected by SQL query
        protected $field_table= "";

        //number of rows affected by SQL query
        protected $affected_rows = 0;

        protected $link_id = 0;
        protected $query_id = array(); 

        function __construct($server, $user, $pass, $database, $pre='') {
            $this->connected = false;
            $this->host = $server;
            $this->user = $user;
            $this->pass = $pass;
            $this->database = $database;
            $this->prefix = $pre;
            $this->connect();
        }

        function __destruct() {
            //mysql_close($this->link_id); 
        }

        public function connect() {
            if ($this->link_id > 0 && $this->connected && mysql_ping($this->link_id)) { return; }

            $this->link_id = mysql_pconnect($this->host, $this->user, $this->pass);
            if (!$this->link_id) { //open failed
                throw new DatabaseException("mysql_pconnect failed",0);    
            }

            if(!@mysql_select_db($this->database, $this->link_id)) {//no database
                throw new DatabaseException("mysql_select_db failed",0); 
            }
            $this->server='';
            $this->user='';
            $this->pass='';
            $this->database=''; 
            $this->connected = true;
            $this->query("SET time_zone = '".Settings::get('db.timezone_offset')."';",TRUE);
        }

        public function escape($string) {
            if(get_magic_quotes_gpc()) 
                $string = stripslashes($string);
            return mysql_real_escape_string($string);
        }

        public function insert($table,$data,$tbl_key='id') {
            $v=''; 
            $n='';
            foreach($data as $key=>$val) {
                $n.="`$key`, ";
                if(strtolower($val)=='null') 
                    $v.="NULL, ";
                elseif(strtolower($val)=='now()') 
                    $v.="NOW(), ";
                elseif(strcmp(substr($val,0,7),'**ESC**') == 0) 
                    $v .= str_replace('**ESC**','',$val);
                else 
                    $v.= "'".$this->escape($val)."', ";
            }

            if ($v=='' || $n=='') 
                return false;

            $q  = "INSERT INTO `".$this->prefix.$table."` ";
            $q .= "(". rtrim($n, ', ') .") VALUES (". rtrim($v, ', ') .");";
            if($this->query($q)){
                $id=mysql_insert_id();
                if ($id === 0) {  // The ID generated for an AUTO_INCREMENT column by the previous INSERT query on success, 
                                  // 0 if the previous query does not generate an AUTO_INCREMENT value, or FALSE if no MySQL 
                                  // connection was established.
                    return TRUE;
                } 
                return $id;
            }
            else {
                return false;
            }
        }

        public function replace($table,$data,$tbl_key='id') {
            $v=''; 
            $n='';
            foreach($data as $key=>$val) {
                $n.="`$key`, ";
                if(strtolower($val)=='null') 
                    $v.="NULL, ";
                elseif(strtolower($val)=='now()') 
                    $v.="NOW(), ";
                elseif(strcmp(substr($val,0,7),'**ESC**') == 0) 
                    $v .= str_replace('**ESC**','',$val);
                else 
                    $v.= "'".$this->escape($val)."', ";
            }

            if ($v=='' || $n=='') 
                return false;

            $q  = "REPLACE INTO `".$this->prefix.$table."` ";
            $q .= "(". rtrim($n, ', ') .") VALUES (". rtrim($v, ', ') .");";

            if($this->query($q)){
                $id=mysql_insert_id();
                if ($id === 0) {  // The ID generated for an AUTO_INCREMENT column by the previous INSERT query on success, 
                                  // 0 if the previous query does not generate an AUTO_INCREMENT value, or FALSE if no MySQL 
                                  // connection was established.
                    return TRUE;
                } 
                return $id;
            }
            else {
                return false;
            }
        }

        public function update($table,$data,$where='1') {
            $q = "UPDATE `".$this->prefix.$table."` SET ";
            foreach($data as $key=>$val) {
                if(strtolower($val)=='null') $q .= "`$key` = NULL, ";
                elseif(strtolower($val)=='now()') $q .= "`$key` = NOW(), ";
                elseif(strcmp(substr($val,0,7),'**ESC**') == 0) $q .=  "`$key` = ".str_replace('**ESC**','',$val);
                else $q.= "`$key`='".$this->escape($val)."', ";
            }
            $q = rtrim($q, ', ') . ' WHERE '.$where.';';
            $result = $this->query($q); 

            if ($result) {
            }
            return $result;
        }

        public function search($table, $field, $value, $exact=FALSE)
        {
                    $value = escape($value);
            if (!$exact) {      
                $q = "select * from $table where $field like '%$value%';";
            } else {
                $q = "select * from $table where $field = '$value';";
            }
            return $this->query($q);
        }

        public function delete($table,$where='1') {
            $q  = "DELETE FROM `".$this->prefix.$table."` ";
            $q .= " WHERE ".$where.";";
            $result = $this->query($q);             

            if ($result) {
            }
        }

        public function query($sql,$reset=FALSE) {
            //echo "<pre>$sql</pre>";
            $this->connect();
            $command = strtok(trim($sql)," \n\t");
            switch (strtoupper(trim($command))) {
                case "SELECT":
                case "SHOW":
                case "DESCRIBE":
                case "EXPLAIN":
                    if (isset($this->query_id[md5($sql)]) && $reset==FALSE) {
                        $row = mysql_fetch_array($this->query_id[md5($sql)], MYSQL_ASSOC);
                        if ($row == FALSE) {
                            unset($this->query_id[md5($sql)]);
                            return FALSE;
                        } else {
                            return $row;
                        }
                    } else {    
                        $this->query_id[md5($sql)] = @mysql_query($sql, $this->link_id);
                        if (!$this->query_id[md5($sql)]) {
                            throw new DatabaseException(mysql_error($this->link_id),mysql_errno($this->link_id));
                        }
                    }
                    $row = mysql_fetch_array($this->query_id[md5($sql)], MYSQL_ASSOC);
                    if ($row == FALSE) {
                        unset($this->query_id[md5($sql)]);
                        return FALSE;
                    } else {
                        return $row;
                    }
                    break;
                default:
                    return @mysql_query($sql, $this->link_id);
                    break;
            }
        }
    }
?>

To create and use the Database Class:

$db = new Database("db.host","db.user","db.pass","db.database");

Getting data from $_POST into your db is super simple if all your form elements are named the same as your table fields.. for example:

$data = $_POST;
$ok = $db->update('mytable', $data, 'something = something_else'); //$ok will be false if something went wrong
Mikey1980
That's what i call "I was first in computer science class but understand nothing". Getting data from $_POST into your db is super simple if all your form elements are named the same as your table fields and leads you directly to SQL injection
Col. Shrapnel
If your run a generic query just use escape($value), this also protect you if magic quotes are enabled if your server is running an older version of PHP.
Mikey1980
oh. do you escape field names too?
Col. Shrapnel
@ Col. Shrapnel, not sure what you mean.. update() escapes the values in $_POST in my example, if there's an issue with the class please let me know
Mikey1980
values. You do escape values. Wrong way but we'll put it aside. I am talking not of values but of **field names** which you are putting into query directly from untrusted ecil and dangerous user input. Field names. This $key variable you know.
Col. Shrapnel
@ Col. Shrapnel.. so you recommend using escape() on field names, I am self-taught with no formal training so please watch the arrogance.. comments like that make people scared to attempt to answer someone's question
Mikey1980
No, I don't suggest to use escape() on field names, as it will help nothing. The only possible way is to enumerate all field names explicitly. Make an array with allowed field names and check POST input against it. Yeah it seems boring but thats the only safe way
Col. Shrapnel
@ Col. Shrapnel -- +1 thanks man!
Mikey1980
A: 

in this case

mysql_query("DELETE FROM stage WHERE listid=".intval($_POST['listid']));

full reference: dynamical SQL syntax explained

Col. Shrapnel