sql >> Databasteknik >  >> RDS >> Mysql

Är mysql_real_escape_string() och mysql_escape_string() tillräckliga för appsäkerhet?

@Charles har extremt rätt!

Du utsätter dig själv för risk för flera typer av kända SQL-attacker, inklusive, som du nämnde

  • SQL-injektion:Ja! Mysql_Escape_String håller dig förmodligen fortfarande mottaglig för SQL-injektioner, beroende på var du använder PHP-variabler i dina frågor.

Tänk på detta:

$sql = "SELECT number FROM PhoneNumbers " .
       "WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value);  

Kan det säkert och korrekt undkomma på det sättet? NEJ! Varför? eftersom en hacker mycket väl fortfarande kan göra detta:

Upprepa efter mig:

mysql_real_escape_string() är endast menat att undkomma variabel data, INTE tabellnamn, kolumnnamn och speciellt inte LIMIT-fält.

  • LIKE-exploateringar:SOM "$data%" där $data kan vara "%" vilket skulle returnera ALLA poster ... vilket mycket kan vara ett säkerhetsexploat... tänk dig bara en uppslagning med de fyra sista siffrorna på ett kreditkort... OOPs! Nu kan hackarna potentiellt få alla kreditkortsnummer i ditt system! (BTW:Att lagra hela kreditkort rekommenderas knappast!)

  • Charset Exploits:Oavsett vad hatarna säger, är Internet Explorer fortfarande , 2011, sårbar för Character Set Exploits, och det är om du har utformat din HTML-sida korrekt, med motsvarigheten till <meta name="charset" value="UTF-8"/> ! Dessa attacker är MYCKET otäcka då de ger hackaren lika mycket kontroll som raka SQL-injektioner:t.ex. full.

Här är lite exempelkod för att demonstrera allt detta:

// Contains class DBConfig; database information.
require_once('../.dbcreds');                       

$dblink = mysql_connect(DBConfig::$host, DBConfig::$user, DBConfig::$pass);
mysql_select_db(DBConfig::$db);
//print_r($argv);

$sql = sprintf("SELECT url FROM GrabbedURLs WHERE %s LIKE '%s%%' LIMIT %s",
               mysql_real_escape_string($argv[1]),
               mysql_real_escape_string($argv[2]),
               mysql_real_escape_string($argv[3]));
echo "SQL: $sql\n";
$qq = mysql_query($sql);
while (($data = mysql_fetch_array($qq)))
{
        print_r($data);
}

Här är resultatet av den här koden när olika ingångar skickas:

$ php sql_exploits.php url http://www.reddit.com id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://www.reddit.com%'
               ORDER BY id;
Returns: Just URLs beginning w/ "http://www.reddit.com"

$ php sql_exploits.php url % id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE '%%' 
               ORDER BY id;
Results: Returns every result Not what you programmed, ergo an exploit --

$ php sql_exploits.php 1=1'http://www.reddit.com ' id-resultat:Returnerar varje kolumn och varje resultat.

Sedan finns det de VERKLIGEN otäcka LIMIT-operationerna:

$ php sql_exploits.php url 
> 'http://www.reddit.com'
> "UNION SELECT name FROM CachedDomains"
Generated SQL: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://reddit.com%' 
               LIMIT 1 
               UNION
               SELECT name FROM CachedDomains;
Returns:  An entirely unexpected, potentially (probably) unauthorized query
          from another, completely different table. 

Om du förstår SQL i attackerna eller inte är irrelevant. Vad detta har visat är att mysql_real_escape_string() är lätt kringgås av även de mest omogna hackare. Det beror på att det är en REAKTIV försvarsmekanism. Det fixar bara mycket begränsade och KÄNDA exploateringar i databasen.

All flykt kommer ALDRIG att räcka för att säkra databaser. Faktum är att du uttryckligen kan REAGERA på varje KÄNT utnyttjande och i framtiden kommer din kod med största sannolikhet att bli sårbar för attacker som upptäcks i framtiden.

Det korrekta och enda (egentligen) försvaret är ett PROAKTIVT:Använd förberedda uttalanden. Förberedda satser är utformade med särskild omsorg så att ENDAST giltig och PROGRAMMERAD SQL exekveras. Detta innebär att, när det görs på rätt sätt, minskar oddsen för att oväntad SQL ska kunna köras dramatiskt.

Teoretiskt sett skulle förberedda uttalanden som implementeras perfekt vara ogenomträngliga för ALLA attacker, kända och okända, eftersom de är en SERVER SIDE-teknik, som hanteras av DATABASSERVRNA SJÄLV och biblioteken som gränsar till programmeringsspråket. Därför är du ALLTID garanterad att vara skyddad mot ALLA KÄNDA HACK, som ett minimum.

Och det är mindre kod:

$pdo = new PDO($dsn);

$column = 'url';
$value = 'http://www.stackoverflow.com/';
$limit = 1;

$validColumns = array('url', 'last_fetched');

// Make sure to validate whether $column is a valid search parameter.
// Default to 'id' if it's an invalid column.
if (!in_array($column, $validColumns) { $column = 'id'; }


$statement = $pdo->prepare('SELECT url FROM GrabbedURLs ' .
                           'WHERE ' . $column . '=? ' .
                           'LIMIT ' . intval($limit));
$statement->execute(array($value));
while (($data = $statement->fetch())) { }

Nu var det väl inte så svårt? Och det är fyrtiosju procent mindre kod (195 tecken (PDO) mot 375 tecken (mysql_). Det är vad jag kallar "full av vinst".

EDIT:För att ta itu med all kontrovers som detta svar väckte, låt mig upprepa det jag redan har sagt:

Genom att använda förberedda uttalanden kan man utnyttja skyddsåtgärderna för själva SQL-servern, och därför är du skyddad från saker som SQL-servern människor känner till. På grund av denna extra skyddsnivå är du mycket säkrare än att bara använda att fly, oavsett hur noggrant det är.



  1. Hur man distribuerar Percona XtraDB Cluster 8 för hög tillgänglighet

  2. Hur skickar man värden till IN-operatören dynamiskt?

  3. Lägg till ledande och efterföljande nollor i SQL Server

  4. Sök efter ett värde i kolumnvärdet som lagrar kommaseparerade värden