Bir veritabanı sınıfının SQL enjekte önlenmesi

7 Cevap php

Ben bir veritabanı sınıfı oluşturmak ve SQL enjeksiyon önleme (duh!) çeşit dahil etmek iyi bir fikir olacağını düşündüm ben. İşte bir veritabanı sorgusu çalıştırır yöntem:

class DB
{
    var $db_host    = 'localhost';
    var $db_user    = 'root';
    var $db_passwd  = '';
    var $db_name    = 'whatever';

    function query($sql)
    {
        $this->result = mysql_query($sql, $this->link);
        if(!$this->result)
        {
           $this->error(mysql_error());
        } else {
            return $this->result;
        }
    }
}

Orada daha sınıfta daha var ama ben sadece bunun için aşağı kesiyorum. Ben sadece mysql_real_escape_string($sql, $this->link); o tüm sorgu kaçar ve bir SQL sözdizimi hatası neden kullanabilirsiniz eğer ben bakan değilim sorundur. Nasıl dinamik olarak kaçtı gereken değişkenleri bulabilirim? Benim ana kod blokları mysql_real_escape_string() kullanarak kaçınmak istiyorsanız, ben yerine bir fonksiyon olurdu.

Teşekkürler.

7 Cevap

Sorun bir SQL sorgusu inşa zaman bu değişkenleri bularak enjeksiyon önlemek mümkün için çok geç olduğunu - aksi takdirde zaten PHP yerleşik olurdum.

Öncelemeli Eğer sorguları oluşturmak zaman çok daha erken yapılması gerekir. Bir sorgu bina sınıfını kullanabilirsiniz.

Bir nesne olarak, burada base db entity class hangi bir komple sağlar türetilmiştir user object bir örnek bir veritabanı tablosu sağlayan bir katman var - ancak ben farklı bir yaklaşım öneriyoruz active record pattern kullanarak bir veritabanına arabirim ve iterator pattern.

Ben bazı örnekler ile bu göstermek gerekir; Burada düzgün bir şey Yineleyicilerde size çok daha uzak soyut can ve veri ayıklamak için başka bazı oldukça genel sınıfları satır aşağı gibi.

Yukarıdaki yaklaşım kullanarak bir kullanıcı kaydı oluşturmak için:

$user = new DbUser();
$user->create();
$user->set_email('test@example.com');
$user->write();

Bir kullanıcı kaydını okumak için:

$user = new DbUser();
$user->set_email('text@example.com');
if ($user->load_from_fields())
{
}

Kayıtlar arasında yineleme için:

$user_iterator = DbUser::begin();
if ($user_iterator->begin())
{
    do
    {
         $user = $user_iterator->current();
         echo $user->get_email();
    } while ($user_iterator->next());
}

Kara dayalı yaklaşım ve Beyaz liste tabanlı yaklaşım: bir SQL enjeksiyon saldırıyı önlemek için iki yaklaşım vardır.

Blacklist approach implies that you need to check the whole query string and identify unwanted code and remove it. This is hell lot of difficult. Instead, use the white-list approach by using parametrized SQL. This way you will be sure that the only query that will be executed will be the one which you intentionally built using your code, and any injection attempt will fail as all the injection queries will be a part of parameter and hence wont be executed by the database. You are trying to find a way of preventing the injection after the query has already been built up, which indirectly means a blacklist based approach.

Kodunuzu parametrized SQL kullanmayı deneyin, onun küresel uyarlanmış güvenli kodlama ilkelerden biridir.

Parametrized veya tüm değerlerini geçmek için DB sınıfını inşa deneyin gibi bir şey kullanarak (ve değerler kullanmak ne olursa olsun) Ya:

$db->where(x,y);

yani.

$db->where('userid','22');

Ve sınıf korpus gibi bir şey kullanabilirsiniz

function where(var x, var y) // method
{
    $this->where .= x . ' = '.mysql_real_escape_string(y);
}

Tabii bu çoklu NEREDE girişlerini desteklemek için temizlenmesi gerekiyor.

Kod enjeksiyon önlemenin bütün mesele sizin sql ve kullanıcılar enjekte sql ayırt etmek istiyor. Sen yapamazsın ki bu en düşük seviyede daha fazla. Bir örnek vermek sağlar:

select * from users where username='test' and password='itisme' or '4'='4'

Bu mükemmel geçerli sql gibi görünüyor, ama aynı zamanda bir sql enjekte sürümü olabilir:

"select * from users where username='test' and password='" . "itisme' or '4'='4". "'"

Yani sizin kodunuzu daha yukarı bunu yapmak zorunda, ya da başkalarının önerdiği gibi sarma kullanın.

Bu doğru şekilde yapmak için, size sorgu bina konum olarak malzeme sterilize veya çekirdek sorgu bağımsız parametrelere geçmek için sınıfın içine koymak zorunda.

SQL enjeksiyon saldırıları önleme fikrine kendi SQL çalıştıran kullanıcıları önlemektir. Kullanıcıların kendi SQL çalıştırmak için izin vermek istediğiniz gibi Burada, öyle görünüyor ki, neden onları sınırlamak?

Veya kullanıcıların sorgu () metodu içine SQL geçmesine izin vermiyor değilseniz. Bu kaçan parametreleri uygulamak için bir seviyenin çok düşüktür. Eğer fark ettik gibi, parametreleri değil, yalnızca tüm SQL ifadeleri kaçmak istiyorum.

Eğer SQL parametrize eğer, o zaman sadece parametreleri kaçış olabilir. Bu durumda, ben SQL değil kullanıcıların parametrelerin değerlerini etkileyebilir varsayarak, ve yaşıyorum.

Bu diğer veritabanı arayüzleri uygulanmaktadır nasıl bir fikir almak için PDO'su bindParam() veya Zend_Db'nin en query() yöntemleri bir göz atın.

Me, I solved this problem by adding parameters to the query function. I found that codeigniter did it pretty nicely, so I adapted it to my own tastes.

Örnek:

$result = Database::query('INSERT INTO table (column1,column2,column3) VALUES(?,?,?)',array($value1,$value2,$value3));



public static $bind_marker = '?';
public static function query($query, $binds = FALSE)
    {
        if($binds !== FALSE)
        {
            $query = self::compile_binds($query,$binds);
        }
        // $query now should be safe to execute
}

private static function compile_binds($query, $binds)
    {
        if(strpos($query, self::$bind_marker) === FALSE)
        {
            return $query;
        }

        if(!is_array($binds))
        {
            $binds = array($binds);
        }

        $segments = explode(self::$bind_marker, $query);

        if(count($binds) >= count($segments))
        {
            $binds = array_slice($binds, 0, count($segments)-1);
        }

        $result = $segments[0];
        $i = 0;
        foreach($binds as $bind)
        {
            if(is_array($bind))
            {
                $bind = self::sanitize($bind);
                $result .= implode(',',$bind);
            }
            else
            {
                $result .= self::sanitize($bind);
            }

            $result .= $segments[++$i];
        }

        return $result;
    }

public static function sanitize($variable)
{
    if(is_array($variable))
    {
        foreach($variable as &$value)
        {
            $value = self::sanitize($value);
        }
    }
    elseif(is_string($variable))
    {
        mysql_real_escape_string($variable);
    }
    return $variable;
}

Ben CodeIgniter'ın sürümünden eklenen önemli bir yanı ben "IN" kullanmak için yararlı bir parametre olarak bir dizi kullanabilirsiniz olduğunu:

$parameters = array
    (
        'admin',
        array(1,2,3,4,5)
    );

$result = Database::query("SELECT * FROM table WHERE account_type = ? AND account_id IN (?)",$parameters);