@Charles er ekstremt korrekt!
Du udsætter dig selv for risiko for flere typer kendte SQL-angreb, herunder, som du nævnte
- SQL-injektion:Ja! Mysql_Escape_String holder dig sandsynligvis STADIG modtagelig for SQL-injektioner, afhængigt af hvor du bruger PHP-variabler i dine forespørgsler.
Overvej dette:
$sql = "SELECT number FROM PhoneNumbers " .
"WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value);
Kan det sikkert og præcist undslippe på den måde? INGEN! Hvorfor? fordi en hacker meget vel stadig kan gøre dette:
Gentag efter mig:
mysql_real_escape_string()
er kun beregnet til at undslippe variable data, IKKE tabelnavne, kolonnenavne og især ikke LIMIT felter.
-
LIKE udnytter:SOM "$data%", hvor $data kunne være "%", hvilket ville returnere ALLE poster ... hvilket udmærket kan være en sikkerhedsudnyttelse... forestil dig bare et opslag med de sidste fire cifre på et kreditkort... OOPs! Nu kan hackerne potentielt modtage hvert kreditkortnummer i dit system! (BTW:Det anbefales næppe nogensinde at gemme fulde kreditkort!)
-
Charset-udnyttelse:Lige meget hvad haterne siger, er Internet Explorer stadig , i 2011, sårbar over for Character Set Exploits, og det er hvis du har designet din HTML-side korrekt med det, der svarer til
<meta name="charset" value="UTF-8"/>
! Disse angreb er MEGET grimme, da de giver hackeren lige så meget kontrol som direkte SQL-injektioner:f.eks. fuld.
Her er et eksempel på kode til at demonstrere alt dette:
// 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);
}
Her er resultaterne af denne kode, når forskellige input sendes:
$ 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-resultater:Returnerer hver kolonne og hvert resultat.
Så er der de VIRKELIG grimme LIMIT-benyttelser:
$ 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 forstår SQL'en i angrebene eller ej, er irrelevant. Hvad dette har vist er, at mysql_real_escape_string() er let omgås af selv de mest umodne hackere. Det er fordi det er en REAKTIV forsvarsmekanisme. Det retter kun meget begrænsede og KENDTE udnyttelser i databasen.
Al escape vil ALDRIG være tilstrækkelig til at sikre databaser. Faktisk kan du udtrykkeligt REAGERE på enhver KENDT udnyttelse, og i fremtiden vil din kode højst sandsynligt blive sårbar over for angreb, der opdages i fremtiden.
Det korrekte, og eneste (virkelig), forsvar er et PROAKTIVT:Brug forberedte erklæringer. Forberedte sætninger er designet med særlig omhu, så der KUN udføres gyldig og PROGRAMMERT SQL. Dette betyder, at når det gøres korrekt, reduceres chancerne for, at uventet SQL kan udføres dramatisk.
Teoretisk set ville forberedte udsagn, der er implementeret perfekt, være uigennemtrængelige for ALLE angreb, kendte og ukendte, da de er en SERVER SIDE-teknik, håndteret af DATABASE-SERVERNE SELV og bibliotekerne, der interfacer med programmeringssproget. Derfor er du ALTID garanteret at være beskyttet mod ALLE KENDTE HACK, som minimum.
Og det er mindre kode:
$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())) { }
Det var nu ikke så svært var det? Og det er syvogfyrre procent mindre kode (195 tegn (PDO) vs 375 tegn (mysql_). Det er, hvad jeg kalder, "fuld af gevinst".
EDIT:For at løse al den kontrovers, dette svar vakte, tillad mig at gentage, hvad jeg allerede har sagt:
Ved at bruge forberedte sætninger kan man udnytte de beskyttende foranstaltninger på selve SQL-serveren, og derfor er du beskyttet mod ting, som SQL-serverens folk kender til. På grund af dette ekstra beskyttelsesniveau er du langt mere sikker end ved blot at bruge undslippe, uanset hvor grundigt det er.