Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web development, networking and server security. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Нечаканы SQL-ін'екцыя

Нечаканы SQL-ін'екцыя
Калі ўцёкаў Is Not Enough

Аляксандр Andonov (SIR (кропка) Мордред (на) Gmail)
Версія 1.0
Апошняе абнаўленне: 2007/01/09


Анатацыя:

Мы будзем глядзець на некалькі сцэнараў, у якіх SQL-ін'екцыя можа адбыцца, нават калі mysql_real_escape_string () была выкарыстана. Ёсць два асноўных кроку ў пісьмовым SQL ін'екцый ўстойлівы код: правільна і пазбегнуць праверкі ўваходных і належнае выкарыстанне сінтаксісу SQL. Незахаванне любога з іх можа прывесці да кампрамісу. Многія з канкрэтных пытанняў, якія ўжо вядомыя, але ні адзін дакумент згадвае іх усё.
Хоць прыклады пабудаваны на PHP / MySQL, тыя ж прынцыпы прымяняюцца да ASP / MSSQL і іншыя камбінацыі моў і баз дадзеных.

Змест:

  1. Увядзенне і абгрунтаванне
  2. Ўстаноўка
  3. Як дабрацца да mysql_real_escape_string ()
  4. Цэлыя значэння
  5. Вяртанне цэлых значэннях
  6. Прывітанне, як цябе імя слупкі?
  7. Табе падабаецца мой% WildCard%?
  8. Іншыя базы дадзеных і іншыя пытанні
  9. Рэзюмэ ў тэкставым фармаце
  10. Аб
  11. Спіс літаратуры

1. Увядзенне і абгрунтаванне

Ёсць шмат работ ( [1], [2], [3], [4] ) і артыкулаў ( [5], [6] ) аб SQL-ін'екцыя ў дынамічна пабудаваны запытаў SQL (у адрозненне ад падрыхтаваных заяў або захоўваемых працэдур), і яшчэ больш аб тым, як абараніць ад яго ( [7], [8] ). І ўсё ж вэб-праграмістаў і надалей ўносіць вырашальны памылкі пры напісанні сваіх SQL-код, звязаны, на самай справе Ёсць выпадках прама не згадваецца ў папулярных падручнікаў, якія павінны быць апрацаваны з асцярогай. Як дакументацыі PHP ( [9] ) на mysql_real_escape_string () кажа: "Гэтая функцыя павінна заўсёды (за некалькімі выключэннямі) выкарыстоўвацца, каб зрабіць дадзеныя ў бяспецы перад адпраўкай запыту да MySQL.". Дадзенай артыкуле робіцца спроба пералічыць сказаў выключэння і даць поўны спіс кантрольна-прапускным пункце ахоўных мер. Мы вызначым некаторыя агульныя кропкі адмовы і даць права (а ў некаторых выпадках, звычайна прымяняецца няправільна) рашэнні ў надзеі, што маладыя праграмісты і ручка-тэстараў будзе навучыцца распазнаваць іх у свой уласны код, калі яны адбудуцца.

Прыведзеныя прыклады закліканы выглядаць некаторыя агульныя вэб-праграмаванне задач, звязаных з пошукам і адлюстраваннем інфармацыі пра карыстальніка. З іншага боку, яны некалькі штучна, у тым сэнсе, што яны празмерна спрошчанай. Некаторыя з прыкладаў SQL-ін'екцый не будзе працаваць з больш складаных запытаў (напрыклад, з дапамогай ORDER BY і UNION патрабуе спецыяльнага сінтаксісу дужках, якое звычайна цяжка дамагчыся пры спробе ўкараніць SQL). Тым не менш, калі пункту ўпырску існуе ў кодзе, добра разбіраецца зламыснік будзе атрымаць з іх выгаду, незалежна ад таго, як "варожыя" да SQL-ін'екцыі навакольных запыт.

Мы не будзем абмяркоўваць магчымыя стратэгіі па змякчэнні наступстваў, калі SQL-ін'екцыі адбываецца, такіх як PHP і MySQL параметры канфігурацыі, шыфраванне дадзеных і г.д., але чытачу прапануецца даведацца больш аб прадмеце.

Мы спадзяемся, што чытач знаёмы з асновамі PHP / MySQL і ўзаемадзеяння, па меншай меры схематычна ідэя аб тым, што SQL ін'екцыя. Даведачным раздзеле прыводзяцца некаторыя добрыя паказальнікі на апошнім.

Варта таксама адзначыць, што многія з прадстаўленых пытанняў было асобна згадана даследчыкамі ў галіне бяспекі, перш чым, напрыклад, [14] і [15] як дзяржаўных, так мае патрэбу ў двукоссі вакол значэнняў, кіраўніцтва PHP ( [9] ) згадваецца падабаецца маскі, і [16] пералічваюцца; [17] коратка раіць не выкарыстоўваць дынамічныя імёны табліцы.

2. Ўстаноўка

http://www.webappsec.org/projects/articles/091007.zip
або
http://mordred.logris.org/articles/091007.zip

Артыкул суправаджаецца калекцыю прыкладаў, кожны з якіх прапануе шэраг тэстаў, якія могуць быць запушчаныя на лакальны сервер. Практыкі адлюстроўваецца ў прыкладах рэкамендуецца ўжываць толькі ў частцы, звязанай з SQL-ін'екцый. Астатняй код паспешна (і ляніва) залатаць на "проста працу". Яна адлюстроўвае SQL памылкі ў HTML, не пазбегнуць значэння, узятыя з базы дадзеных перад высновай іх альбо, і HTML сабе з'яўляецца найбольш мінімалістычны выродлівыя вуліцы HTML, якія будуць атрымліваць паслугі. Гэта не добры прыклад стылю праграмавання, так што не імітаваць яго (я таксама не разумею). Гэта зроблена наўмысна, каб не адцягваць чытача ад важных кода. Паспрабуйце зразумець і прымяняць меры ў дачыненні да ін'екцыі SQL, хоць, і калі вы зацікаўлены ў ручцы-тэсціравання, а таксама вэб-праграмаваньне, гуляць з вектарамі пры ўмове ін'екцыі.

Адкрыць [db.inc.php] у сваім каханым рэдактары і пісаць правільна Інфармацыя злучэння з базай дадзеных. У базе дадзеных, наадварот для выпрабаванняў, імпарт [sql_injection_test.sql] файл, які ўтрымлівае простую схему табліцы з трыма радамі інфармацыі. Нагрузка index.php ў вашым браўзэры і наступных прыкладах, як яны каментуюцца ў артыкуле. Калі вы жадаеце гуляць з URL параметраў за межы названых выпадках тэст, націсніце кнопку [новыя] спасылкі для загрузкі прыкладаў у новых вокнах.

УВАГА! Ўсталяваць гэтыя сцэнары толькі на мясцовую навакольнае асяроддзе тэсціравання. Калі вы пакінеце іх на агульнадаступным гаспадара, яны (і, у залежнасці ад канфігурацыі, гаспадар сам) будзе эксплуатавацца. Мы вас папярэдзілі.

Дзеля аўтаномнасць, істотныя фрагменты кода таксама будуць убудаваныя ў артыкуле. Тэставай табліцы мае наступныя дадзеныя ў ім:

mysql> select * from sql_injection_test;
+--------+-------------+----------+
| userid | username    | password |
+--------+-------------+----------+
|      1 | Winnie      | Pooh     |
|      2 | Edward      | Sanders  |
|      3 | Christopher | Robin    |
+--------+-------------+----------+

RunQuery ($ запытаў) з'яўляецца функцыяй карыснасці, што адлюстроўваецца і працуе дадзены запыт і адлюстроўваецца альбо вярнуў памылку або ўсё вернутыя радка.

3. Як дабрацца да mysql_real_escape_string ()

---------[ Example 1. (finduser.php) ]------------
$username = isset($_GET['u']) ? $_GET['u']: '';
RunQuery("SELECT userid, username FROM sql_injection_test WHERE username='$username'");
---------[ Tests: ]------------------------------
1. u=Winnie
2. u=Edward
3. u=Christopher
4. u=Schneier
5. u='
6. u=' OR ''='
7. u=' UNION ALL SELECT userid, CONCAT(username, ' ', password) 
	FROM sql_injection_test WHERE ''='
--------------------------------------------------

Прыклад 1 дэманструе асноўныя (няправільна) спосаб апрацоўкі дынамічных запытаў SQL. 'U' URL параметр будзе змяшчаць імя карыстальніка, і першыя чатыры спасылкі тэст прадэманстраваць, як ён працуе з існуючымі і неіснуючымі імёны. Пятае выпрабаванне, адзінарная дужка ("парушэнне цытаты"), з'яўляецца асноўнай тэст, калі сцэнар уразлівымі (ці, па меншай меры, калі ён зламаецца), і, як мы бачым, наш прыклад сапраўды спрабуе выканаць несапраўднымі SQL. Тэст 6 маніпулюе запыт на атрыманне спісу ўсіх магчымых карыстальнікаў, а не толькі адзін. І, нарэшце запыту на аб'яднанне ў тэставым 7 пералічаны ўсе карыстальнікі разам з іх паролямі.

---------[ Example 2. (finduser_fixed.php) ]---------
$username = isset($_GET['u']) ? $_GET['u']: '';
$username = mysql_real_escape_string($username);
RunQuery("SELECT `userid`, `username` FROM `sql_injection_test` 
	WHERE `username` = '$username'");
-----------------------------------------------------

Уразлівасць тут узнікае з здольнасці зламысніку ўкараніць адну цытату, замыкаючы знакавая радок і інтэрпрэтацыі астатняй ўвод карыстальніка як сінтаксіс SQL. Вядома, што сродкі прававой абароны з'яўляецца пазбегнуць ўсіх зменных, якія будуць уключаны ў дынамічны запыт з mysql_real_escape_string (). Прыклад 2 паказвае, што ж напады больш не працаваць.

З гэтага моманту мы вызваліцца ад нашых параметраў з mysql_real_escape_string () і будзе назіраць, як і калі гэта не зможа абараніць нас ад SQL-ін'екцый. Звярніце ўвагу, што нават няправільна ( [10] ) ўцёкі з такімі функцыямі як addslashes () (або magic_quotes функцыянальнасці ў PHP) мае аналагічныя наступствы.

4. Цэлыя значэння

---------[ Example 3. (viewprofile.php) ]------------
$userid = isset($_GET['id']) ? $_GET['id']: 0;
$userid = mysql_real_escape_string($userid);
RunQuery("SELECT userid, username FROM sql_injection_test 
	WHERE userid=$userid");
---------[ Tests: ]------------------------------
1. id=0
2. id=1
3. id=2
4. id=3
5. id='
6. id=0 or 1
7. id=0 UNION ALL SELECT userid, CONCAT(username, ' ', password) 
	FROM sql_injection_test WHERE 1
8. id=0 UNION ALL SELECT userid, CONCAT(username, CHAR(32), password) 
	FROM sql_injection_test WHERE 1
--------------------------------------------------

Прыклад 3 прымае лікавай параметр, ідэнтыфікатар карыстальніка, і адлюстроўвае інфармацыю аб гэтым карыстальніку. Першыя чатыры выпрабаванні, перш чым паказаць, як сцэнар, як чакаецца, вядуць сябе, і пяты тэст паказвае, што нават бег параметр можа выклікаць памылку. Праблема тут у тым, што запыт мяркуе, што параметр будзе цэлае і так напісана без двукоссяў. Зламыснік, аднак, не мае патрэбу ў "парушэньні цытаты", як усё, што ён уваходзіць ідзе непасрэдна ў запыце варта тлумачыць як сінтаксіс SQL. Тэст 6 маніпулюе ИНЕКЕ да вяртання ўсіх карыстацкіх запісаў. Як мы бачым з 7, mysql_real_escape_string () прадухіляе поспех ўводзяць запыты, якія ўключаюць двукоссі. Любы такі запыт можа быць перапісаны ў шлях, каб не выкарыстоўваць двукоссі, хоць, так выпрабаванне 8 паспяхова дзе 7 не ўдалося.

---------[ Example 4. (viewprofile_fixed_1.php) ]------------
$userid = isset($_GET['id']) ? $_GET['id']: 0;
$userid = mysql_real_escape_string($userid);
RunQuery("SELECT `userid`, `username` FROM `sql_injection_test` 
	WHERE `userid` = '$userid'");
------------------------------------------------------------

---------[ Example 5. (viewprofile_fixed_2.php) ]------------
$userid = isset($_GET['id']) ? $_GET['id']: 0;
userid = intval($userid);
//...
$userid = mysql_real_escape_string($userid);
RunQuery("SELECT `userid`, `username` FROM `sql_injection_test` 
	WHERE `userid` = '$userid'");
------------------------------------------------------------

Ёсць два рашэнні гэтай праблемы, альбо выкарыстоўваць двукоссі ў запыце ў якасці прыкладу 4 робіць, або ператвараць уваходнае значэнне Int. Найбольш надзейны выбар паказана ў прыкладзе 5, дзе два рашэнні, разам узятых. Калі лікавае значэнне патрабаванне змяніць у нейкі момант у будучыні і ўваходны параметр больш не вымушаны быць INT, запыт будзе па-ранейшаму абаронены.

Чытач павінен разумець, што праверкі ўваходных дадзеных (у нашым выпадку пераканайцеся, што мы чакаем, будзе Int сапраўды Int) і пазбегнуць параметру перш, чым даць запыт дзве розныя меры бяспекі. У дадзеным канкрэтным выпадку ні адзін будзе дастаткова, але ў цэлым, для паглыбленага бяспекі, вы заўсёды павінны зрабіць абодва. Таксама, гэтых двух задач будзе найбольш верагодна ажыццяўляецца на два рознымі падсістэмамі ў кодзе рэальнай, так праверкі і ўцёкаў коды не будзе суседніх, як адлюстроўваюцца ў дадзеным спрошчаным выпадку. Гэтая тэма настолькі шырока асвятлялася іншымі аўтарамі ( [8] ), таму мы не будзем працягваць яе далей.

5. Вяртанне цэлых значэннях

---------[ Example 6. (members.php) ]------------
$offset = isset($_GET['o']) ? $_GET['o']: 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");
---------[ Tests: ]------------------------------
1. o=0
2. o=1
3. o=2
4. o=3
5. o='
6. o=999999, 10 UNION ALL SELECT username, password FROM sql_injection_test LIMIT 0
--------------------------------------------------

Прыклад 6 дэманструе іншай сітуацыі, калі цэлыя значэння выкарыстоўваюцца - зрушэнне і колькасць параметраў прапанову LIMIT. Скрыпт рэалізуе простую функцыю разбіцця на старонкі - ён адлюстроўвае спіс усіх сяброў на старонках 10, прымае пачатковае зрушэнне ў URL параметр.

Маючы на ўвазе папярэднія прыклады, то не дзіўна, што "парушэнне цытаты" у пяты тэст сапраўды парушае запыт, і што ў шосты тэст САЮЗ аснове ін'екцыя можа даць зламысніку спіс імёнаў карыстальнікаў і звязаных з імі паролі.

---------[ Example 7 (members_fixed.php) ]------------
$offset = isset($_GET['o']) ? $_GET['o']: 0;
$offset = intval($offset);
RunQuery("SELECT `userid`, `username` FROM `sql_injection_test` LIMIT $offset, 10");
------------------------------------------------------

---------[ Example 8. (members_not_fixed.php) ]------------
$offset = isset($_GET['o']) ? $_GET['o']: 0;
if (is_numeric($offset))
    RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");
---------[ Tests: ]------------------------------
...
7. o=0.0001
8. o=0x53514c
--------------------------------------------------

Розніца з папярэдняй сітуацыі з'яўляецца тое, што гэта цэлае значэнне адыгрывае іншую ролю, синтактико-мудрым. Дзве гранічныя параметры патрабуюць цэлыя значэння, таму мы не можа выкарыстоўваць любыя цытаты або ўцёкаў механізму, акрамя INTVAL () (або прывядзення да INT), як у прыкладзе 7. Варта адзначыць, што выкарыстанне is_numeric () (як, напрыклад, старыя версіі ( [18] ) кіраўніцтва PHP рэкамендуецца) у частцы праверкі сцэнар не дастаткова праверыць, і прыклад 8 паказаны два шляхі выхаду з запыту ( Хоць няма шкоднаснага кода SQL могуць быць уведзеныя такім спосабам). Другі з тых, выпрабаванне 8, гэта цікава, таму што ў той час як "лічбавы" з пункту гледжання PHP, гэта радковы літэрал ("SQL") для MySQL. Гэта толькі акадэмічны інтарэс у гэтай сітуацыі, хоць, литералы, закадаваныя ў гэтай форме і больш чатырох знакаў не пройдзе is_numeric ().

Іншых адрозненняў паміж прыклады 3-5 і 6-8, што ў рэальным свеце ORDER BY хутчэй за ўсё, будзе выкарыстоўвацца нараўне з прапановай LIMIT. Гэта прывядзе да таго САЮЗ аснове ін'екцыі не працаваць, а распрацоўнік можа прыняць рашэнне код бяспечным. Гэта адначасова і наіўна і няправільна, трэба заўсёды выкарыстаць належныя уцёкаў перад устаўкай значэння ў дынамічны запыт SQL, незалежна ад таго, што запыт выглядае. У наступным раздзеле будзе растлумачыць, як такія "варожыя" на запыты SQL ін'екцыі могуць яшчэ быць выкарыстаны і па-ранейшаму трэба, каб належным чынам абаронены.

Іншы надзейны спосаб пазбегнуць падобных памылак не ўказваць "кампенсацыі" у параметры сцэнарыя, але "старонка". Сцэнар будзе мець для разліку зрушэння на аснове колькасці элементаў для кожнай старонкі, і перадаць яго запыт. Неабходнасць гэтага разліку гарантуе, што зрушэнне будзе цэлае, калі ён дасягае запыту.

6. Прывітанне, як цябе імя слупкі?

---------[ Example 9. (members2.php) ]------------
$order = isset($_GET['o']) ? $_GET['o']: 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY $order");
---------[ Tests: ]------------------------------
1. o=userid
2. o=username
3. o=password
4. o=honey_eaten
5. o='
6. o=userid ASC UNION ALL SELECT username, password 
	FROM sql_injection_test ORDER BY userid
7. o=IF ( (SELECT userid FROM sql_injection_test 
		WHERE username=0x57696e6e6965 AND password=0x506f6f68), 
	userid, username)
8. o=IF ( (SELECT userid FROM sql_injection_test 
		WHERE username=0x57696e6e6965 AND password=0x31323334), 
	userid, username)
--------------------------------------------------

Такім чынам, мы бачылі нападаў, накіраваных на зменных PHP гуляе ролі радковыя значэння, цэлыя значэння і натуральныя параметры абмежаваць. Часам гультаяваты распрацоўшчык хочаце, каб іншая частка яе дынамічных запытаў: імёны слупкоў. Прыклад 9 паказвае, "тыповыя" выкарыстанне: мы хочам, каб атрымаць спіс усіх членаў, і мы хочам карыстачу абраць слупок, па якім іх сартаваць. Тэсты 01/03 простыя (хоць бяспеку настроеных павінны падняць бровы пры выпрабаванні 3... ён выкарыстоўвае імя слупка, які не ў запыце, не пярэчу, што на дадзены момант). Тэст 4 паказвае, што адбудзецца, калі табліца не мае такога слупка, тэст 5 паказвае, што нават калі каціроўкі бег, яны яшчэ могуць "перапынак" сінтаксіс запыту. Тэст 6 спробы выкарыстаць UNION ін'екцыі, толькі каб выявіць, што правільнае выкарыстанне UNION і ORDER BY патрабуе дужкі вакол двух выбіраць, што мы хочам САЮЗ, што немагчыма тут, таму што ў нас толькі ў адной кропцы ін'екцыі.

Так што ж можа зрабіць зламыснік? Адказ: "Сляпы SQL-ін'екцыі" ( [3], [4] ). Паколькі зламыснік не зможа выкарыстоўваць двукоссі, ён павінен ці выкарыстоўваць шаснаццатковы фармат, згаданых у папярэднім раздзеле, або выкарыстаць спалучэнне CONCAT () і CHAR (). Выпрабаванне 7 правярае, ці існуе імя карыстальніка "Віні" і "Віні Пух і пароль, і калі так, замовы вынікі ідэнтыфікатар, калі няма - па імя карыстальніка. (Гэта функцыі КАЛІ () MySQL [19], не варта блытаць з КАЛІ заяву захоўваецца руціннай сінтаксіс) З відавочнай мэтай з'яўляецца ідэнтыфікатар карыстальніка, зламыснік да высновы, што заяву, якое ён правяраецца на гэта праўда. Выпрабаванне 8 спрабуе Winnie/1234, і так як вынікі адсартаваныя па імя карыстальніка, то павінна быць так.

---------[ Example A. (members2_not_fixed.php) ]------------
$order = isset($_GET['o']) ? $_GET['o']: 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");
---------[ Tests: ]------------------------------
...
9. o=userid`,
	IF ( (SELECT userid FROM sql_injection_test 
			WHERE username=0x57696e6e6965 AND password=0x506f6f68), 
		BENCHMARK(300000,MD5(1)), 
		username),
	`userid
10. o=userid`,
	IF ( (SELECT userid FROM sql_injection_test 
			WHERE username=0x57696e6e6965 AND password=0x31323334),
		BENCHMARK(300000,MD5(1)), 
		username), 
	`userid
--------------------------------------------------

Праблема тут, здаецца, падобная на што ў прыкладзе 3 - імя слупкі не правільна указаны (кіраўніцтва MySQL ( [11] ) сцвярджае, што "ідэнтыфікатар двукоссі гэта зваротная дужка (`) "). Сапраўды належнага цытаванні і ў прыкладзе прыпынку нападаў у тэстах 7 і 8. Тое, што мы не можам спыніць гэта зламыснік проста закрыццё зваротныя цытаты і ін'екцыйных SQL пасля імя слупкі, як mysql_real_escape_string () не пазбегнуць зваротныя двукоссі. Як мы ўжо адзначалі, саюз не будзе працаваць пасля ORDER BY, так што зламыснік звяртаецца да іншай сляпы метад ін'екцыі - запаволенне запыту пры ўмове. На маёй машыне тэст 9 займае каля 3 секунд (гэта значыць выпрабаванні ўмова праўдзіва), у той час як тэст 10 заканчваецца без затрымкі.

---------[ Example B. (members2_fixed.php) ]------------
$order = isset($_GET['o']) ? $_GET['o']: 'userid';
if (!in_array($order, Array('userid','username')))
    $order = 'userid';
RunQuery("SELECT `userid`, `username` FROM `sql_injection_test` ORDER BY `$order`");
--------------------------------------------------------

Актуальная праблема запыт не проста цытаванне імя слупкі (хоць гэта добры стыль заўсёды выкарыстаць належнае двукоссяў), але той факт, што дадзеных, якія ўводзяцца карыстальнікам выкарыстоўваецца як імя слупкі. Тэст 4 з неіснуючымі калонкі павінны былі сігналізацыя рост фактар нават да таго, як доказ таго, што ін'екцыі SQL можна. Прыклад B паказвае магчымае рашэнне, дзе толькі фіксаваную колькасць варыянтаў не дапускаецца.

Звярніце ўвагу, што тое ж справядліва і для іншых ідэнтыфікатараў і сінтаксічных элементаў, якія праграміст, магчыма, пажадае дынамічна копію ад уваходу да запыту, такія як імёны табліц, псеўданімаў слупкоў, ASC / DESC ключавыя словы і г.д.

7. Табе падабаецца мой% WildCard%?

MySQL (і іншых баз дадзеных) мае некалькі адпаведных аператараў ( [12], [13] ), якія прымаюць подстановочные знакі або рэгулярныя выразы, яны найбольш часта выкарыстоўваюцца пры апрацоўцы формы пошуку. Што гэта азначае, што ў запыце SQL мы частка (шаблён ці рэгулярны выраз адпаведнай паслядоўнасці), якая паводзіць сябе інакш, чым звычайны сінтаксіс SQL. Для нашых тэстаў мы зробім дзве рэчы. Па-першае, дадатковы індэкс даўжынёй 3, дададзены "Імя карыстальніка" полі. Па-другое, замест вяртання вынікаў запыту, мы будзем выкарыстоўваць сінтаксіс растлумачыць і назіраць, як наш індэкс выкарыстоўваецца.

---------[ Example C. (search.php) ]------------
$search = isset($_GET['s']) ? $_GET['s']: '';
if ($search!='')  {
    $search = mysql_real_escape_string($search);
    RunQuery("EXPLAIN SELECT userid, username FROM sql_injection_test 
		WHERE `username` LIKE '$search%'");
}
---------[ Tests: ]------------------------------
1. s=Wi
2. s=%Wi
--------------------------------------------------

Прыклад C паказвае тыповы шаблён пошуку імёнаў карыстальнікаў. Наш першы тэст паводзіць сябе, як чакалася:

Тэст 1 вынік:
ID select_type табліцы тыпу possible_keys ключ key_len спасылка радкоў Дадатковыя
1 ПРОСТА sql_injection_test Дыяпазон Імя карыстальніка Імя карыстальніка 11 1 Выкарыстаньне дзе

Як мы бачым з "possible_keys" і "ключавых" каштоўнасцяў, наш індэкс (названы "Імя карыстальніка") быў выкарыстаны для вызначэння выніковага набору (мы таксама заўважылі, што "радкоў" роўна 1, гэта азначае, што індэкс дапамаглі MySQL звузіць пошук толькі адзін радок без неабходнасці праходзіць праз усе радкі ў нашай табліцы).

Тэст 1 вынік:
ID select_type табліцы тыпу possible_keys ключ key_len спасылка радкоў Дадатковыя
1 ПРОСТА sql_injection_test УСЕ 3 Выкарыстаньне дзе

Тэст 2 радыкальна адрозніваецца, хоць - не паказчык быў выкарыстаны і ўсе 3 радкі табліцы павінны быць правераны на знак. Прычынай гэтага з'яўляецца тое, што вядучыя подстановочные знак (%) робіць наш прэфікс індэкса бескарысна, так як радкі мы шукаем можа пачынацца з любога характару. Гэта адбылося, таму што мы паспяхова ўводзяць%, што мае асаблівае значэнне ў кантэксце як аператар (і таму не падлягае mysql_real_escape_string () ўцёкаў). Гэта можа здацца не пытанне для нашага 3-х радоў дадзеных, але ўявіце сабе базу дадзеных тысячы або мільёны радкоў, якія раптам не можа выкарыстоўваць індэкс, але ён павінен прайсці праз усе гэтыя дадзеныя. У залежнасці ад таго, як сцэнар напісаны, нават адзін карыстальнік можа праводзіць атакі DOS.

---------[ Example D. (search_fixed.php) ]------------
$search = isset($_GET['s']) ? $_GET['s']: '';
if ($search!='')  {
    $search = mysql_real_escape_string($search);
    $search = addcslashes($search, "%_");
    RunQuery("EXPLAIN SELECT `userid`, `username` FROM `sql_injection_test` 
		WHERE `username` LIKE '$search%'");
}
---------[ Tests: ]------------------------------
1. s=Wi
2. s=%Wi
3. s=\%Wi
--------------------------------------------------

Рашэнне прадэманстравана ў прыкладзе D з'яўляецца зрабіць другі ўцёкі сканчэнні mysql_real_escape_string () для ўсіх зменных, якія замяняюцца на працягу як аператар. Зараз тэсты 1 і 2 паводзяць сябе правільна. Addcslashes () функцыя зручная, таму што месцаў зваротную касую рысу (\) перад дадзеныя персанажы, і зваротную касую рысу, здараецца, па змаўчанні кіраўнікі сімвалы для выражэння на правай баку як аператар.

Звярніце ўвагу, што MySQL дазваляе выбраць іншы кіраўнік сімвал (... дзе `імя карыстальніка` '*%' LIKE '*' ESCAPE будзе адпавядаць літаральным%), але калі вы гэта зробіце, вы не можаце выкарыстаць addcslashes () функцыю больш, вы спатрэбіцца замена функцыі. У сказаў замены функцыі, вам таксама неабходна, каб пазбегнуць новага персанажа бегчы падабаецца, або яна можа быць выкарыстана для подстановочные ін'екцый (хоць і не ў пачатку радка, так што ўплыў DOS не будзе гэтак вялікі).

Тэст 3 паказвае, што ў нашым выпадку нам не трэба бегчы вобразны характар бегчы, як mysql_real_escape_string () ўжо не змог пазбегнуць яе.

Пры працы з рэгулярнымі выразамі (REGEXP / RLIKE аператара) ўцёкаў метад не можа быць так проста, як addcslashes (), таму праграміст, які хоча выкарыстаць гэты аператар павінен быць ўдвая асцярожнымі, як яна ручкамі ўвод дадзеных карыстальнікам.

8. Іншыя базы дадзеных і іншыя пытанні

Ёсць магчымасці не-баз дадзеных MySQL, якая можа таксама выкарыстоўвацца пры ўвядзенні SQL, якая можа быць вырашана наступным ж кантрольны спіс, проста заменіце mysql_real_escape_string () з адпаведнай базай дадзеных канкрэтных экраніруючая функцый. Пашырэнне PostgreSQL для прыклад pg_escape_string (), і калі няма аналагічнай функцыі для базы дадзеных сістэмы, вы павінны праверыць яго дакументацыю аб тым, як зрабіць правільны ўцёкаў.

MSSQL, PostgreSQL і, магчыма, іншых баз дадзеных (у тым ліку з MySQL MySQLi пашырэння) падтрымка выдачы шматразовых запытаў у адным выкліку. З сінтаксічная знак, які выкарыстоўваецца для пастаноўкі тэрмінатар, кропка з коскай, не бег ад стандартных экраніруючая функцый, без двукоссяў параметраў у запыт SQL можа быць выкарыстаны для завяршэння бягучага заяву і пытанне іншы. Вядома, калі ўсе параметры правільна цытаваць і тыпу змушаныя, коскі ці будзе пазбаўлены, або быць часткай значэння літаральным, такім чынам, не парушаючы запыту. MSSQL таксама зарэзерваваныя словы, TOP, якое выкарыстоўваецца ў аналагічна LIMIT MySQL, у той час як PostgreSQL мае LIMIT / OFFSET. Меркаваньні падзелу 5 гэтага артыкула, павінны прымяняцца пры выкарыстанні тых.

9. Рэзюмэ ў тэкставым фармаце

Як мы бачылі, mysql_real_escape_string () з'яўляецца неабходным, але не дастатковыя меры. Вось "кантрольны спіс" правілы трэба прытрымлівацца, каб пераканацца, што дынамічнага кода SQL не з'яўляецца ўразлівым для ін'екцыі SQL:

(Падказка: Вы заўважылі, як "фіксаванай" версіі былі педантычны ў выкарыстанні зваротныя двукоссі Аўтар асабіста перакананы, што гэта павялічвае чытэльнасць запытаў, зрабіўшы выразнае адрозненне паміж ідэнтыфікатараў і значэнняў Яно таксама дапамагае ў сітуацыях, калі?. пачатковец праграміст спрабуе выкарыстаць ключавое слова для ідэнтыфікатара, скарачэнне "апісанне" на "па змяншэнні" з'яўляецца найбольш распаўсюджаны выпадак. Так як зыходны код счытваецца разы больш, чым напісана, чытальнасць напрамую звязана з бяспекай. Pedantic цытаванні такім чынам, мае падвойнае бонус пры напісанні бяспечнага кода.)

10. Аб

Аляксандр "Мордред" Andonov з'яўляецца незалежным даследчыкам бяспекі і кансультанта. Яго інтарэсы ў галіне бяспекі ляжаць у распрацоўцы метадалогій для бяспечнага вэб-праграмавання і вывучэння таго, як у рэальных дадатках здзелкі (або не займацца) з праблемамі бяспекі. Ён можа быць дасягнута пры сэр (кропка) Мордред (на) Gmail.

11. Спіс літаратуры

[1] "SQL Injection: Are your web applications vulnerable?" by Kevin Spett
http://www.spidynamics.com/support/whitepapers/WhitepaperSQLInjection.pdf
[2] "Advanced SQL Injection In SQL Server Applications" by Chris Anley
http://www.ngssoftware.com/papers/advanced_sql_injection.pdf
[3] "Blind SQL Injection: Are your web applications vulnerable?" by Kevin Spett
http://www.spidynamics.com/support/whitepapers/Blind_SQLInjection.pdf
[4] "Blind Sql-Injection in MySQL Databases" by Zeelock
http://seclists.org/bugtraq/2005/Feb/0288.html
[5] "SQL Injection Cheat Sheet" by Ferruh Mavituna
http://ferruh.mavituna.com/makale/sql-injection-cheatsheet/
[6] "SQL Injection Cheat Sheet" by David Kierznowski
http://michaeldaw.org/sql-injection-cheat-sheet/
[7] "PHP Manual: SQL Injection"
http://php.net/manual/en/security.database.sql-injection.php
[8] "Security Corner: SQL Injection" by Chris Shiflett
http://shiflett.org/articles/sql-injection
[9] "PHP Manual: mysql_real_escape_string()"
http://php.net/mysql_real_escape_string
[10] "addslashes() Versus mysql_real_escape_string()" by Chris Shiflett
http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string
[11] "MySQL Reference Manual: Identifiers"
http://dev.mysql.com/doc/refman/5.1/en/identifiers.html
[12] "MySQL Reference Manual: String Comparison Functions: LIKE"
http://dev.mysql.com/doc/refman/5.1/en/string-comparison-functions.html#operator_like
[13] "MySQL Reference Manual: Regular Expressions: REGEXP / RLIKE"
http://dev.mysql.com/doc/refman/5.1/en/regexp.html
[14] "PHP Security Guide" by Chris Shiflett
http://shiflett.org/php-security.pdf
[15] "SQL Injection Attacks" by Prof. Jim Whitehead
http://www.cse.ucsc.edu/classes/cmps183/Spring06/lectures/SQL%20Injection%20Attacks.pdf
[16] "Guide to PHP Security: Chapter 3. SQL Injection" by Ilia Alshanetsky
http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf
[17] "OWASP: Avoiding SQL Injection"
http://www.owasp.org/index.php/Avoiding_SQL_Injection#WARNING:_Escaping_table_names
[18] "PHP Manual: mysql_real_escape_string()", web archive copy from Dec 2005
http://web.archive.org/web/20051217125937/us3.php.net/mysql_real_escape_string
[19] "MySQL Reference Manual: Control Flow Functions: IF()"
http://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if

Published (Last edited): 08-03-2011 , source: http://webappsec.org/projects/articles/091007.shtml