<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>The Taiji of Web Development</title>
	<atom:link href="http://webdeveloping.com.ua/feed" rel="self" type="application/rss+xml" />
	<link>http://webdeveloping.com.ua</link>
	<description>Шлях до сонця темним лісом веб-розробки</description>
	<pubDate>Wed, 21 Jan 2009 18:11:30 +0000</pubDate>
	<generator>http://wordpress.org/?v=abc</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>З новорічними святами!</title>
		<link>http://webdeveloping.com.ua/various/happy-holidays-2009</link>
		<comments>http://webdeveloping.com.ua/various/happy-holidays-2009#comments</comments>
		<pubDate>Sun, 28 Dec 2008 20:21:58 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[різне]]></category>

		<category><![CDATA[новий рік]]></category>

		<category><![CDATA[подія]]></category>

		<category><![CDATA[різдво]]></category>

		<category><![CDATA[свято]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=63</guid>
		<description><![CDATA[Наближаються новорічні свята і це найкращий час нагадати про те, що блоґ живий як і його автор, підвівши підсумки та окресливши плани. Пост трохи не тематичний, але на честь свят - можна  Цією заміткою приймаю чарівну новорічну палицю від kitich&#8216;а. Отже, моя невеличка ретроспектива у рік, що ми залишаємо та погляд у майбутній.
2008ий рік [...]]]></description>
			<content:encoded><![CDATA[<p><img class="size-medium wp-image-68 alignleft" title="З новим роком!" src="http://webdeveloping.com.ua/wp-content/uploads/2008/12/8-300x225.jpg" alt="З новим роком!" width="300" height="225" />Наближаються новорічні свята і це найкращий час нагадати про те, що блоґ живий як і його автор, підвівши підсумки та окресливши плани. Пост трохи не тематичний, але на честь свят - можна <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Цією заміткою приймаю чарівну новорічну палицю від <a title="kitich" href="http://kitich.in.ua/">kitich</a>&#8216;а. Отже, моя невеличка ретроспектива у рік, що ми залишаємо та погляд у майбутній.</p>
<p>2008ий рік став для мене часом перемін як професійних так і життєвих. Перемін на краще. Я вірю, що всі зміни, якими б вони не були, приносять лиш позитивний досвід, нехай навіть не одразу, а у довготривалій перспективі. Від кожної події ми виносимо щось нове, дещо таке, яке пізніше обов&#8217;язково прийде у нагоді.</p>
<p>Отже, визначні події за цей рік:</p>
<ol>
<li>Побував на першій українській конференції веб-розробників <a title="UAWeb-2008" href="http://uaweb.in.ua/">UAWeb-2008</a>, яка я сподіваюсь, стане щорічною традицією з не менш цікавими доповідями й людьми <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Саме ця подія стала рушієм для мого подальшого професійного розвитку і надала мені напрями у ньому.</li>
<li>Започаткував цей блоґ <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Спочатку я був сповнений ентузіазму для його розвитку, але деякі інші події нажаль пригальмували цей порив.</li>
<li>Змінив кілька місць роботи. Не хочу казати нічого поганого про колективи, часткою яких я був - вони були чудові. Бажаю їм лиш найкращого у майбутньому!</li>
<li>Дещо змінив свої професійні орієнтири. Пропрацювавши більше двох років переважно із PHP та стеком технологій пов&#8217;язаних із цією мовою (<abbr title="Linux Apache MySQL PHP">LAMP</abbr>), мене переклинило і я тепер дуже зацікавлений у клієнтських технологіях у вигляді JavaScript (прекрасна мова, якщо хто не в курсі <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ) та серверних в особі Python та фреймворку <a title="Django" href="http://www.djangoproject.com/">Django</a>. В будь-якому випадку я досі цікавлюсь PHP та всім, що із ним пов&#8217;язано. Встиг навіть із Erlang досить близько познайомитись. На мою думку, розробник має завжди дізнаватись щось нове, пробувати себе у різних технологіях і ніколи не зупинятись - не боятись щось змінювати, адже застій веде до упадку.</li>
<li>Спробував працювати на себе (фрілансером). Часом у вільний від основної роботи час, часом в якості основної роботи. Розвіяв багато міфів про щастя роботи на себе - це значно важче. І як виявилось, поки не для мене, принаймні у якості основної роботи. Щоправда, і зараз приймаю замовлення, якщо мене хтось знаходить і має суттєвий запас часу <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
<li>Нарешті отримав місце для життя, яке можу назвати власним (нехай і не назавжди) у чудовому районі Києва - Троєщині (кияни мене зрозуміють <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ). Нехай воно не в найліпшому місці, невелике й мабуть не назавжди, але воно найкраще адже я його поділяю із коханою людиною.</li>
<li>Розпочав власний проект. Так-так, черговий план захоплення світу <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Ну най не всього, а хоч його часточки. Він зараз в процесі розробки у вільний час, якого не так багато, як хотілося б. Скажу лиш, що це тематичний портал з блоговою платформою, деякими специфічними фічами. Поки планів з монетизації нема - лиш деякі ідеї, але це поки не суттєво.</li>
</ol>
<p>Що ж стосується планів на новий рік, то їх у мене є.</p>
<ol>
<li>Пожвавити цей блоґ. Дуже хочу писати багато і цікаво&#8230; ну хоча б періодично. Думаю слід зменшити об&#8217;єм постів в обмін на частішу публікацію, адже великі глибокі статті вимагають дуже багато часу і мабуть не всім цікаво читати занадто багато.</li>
<li>На початку весни закінчити свій проект. Повністю випустити мінімальну версію. Не певний, чи це вийде, одна з причин - не знаю навіть, що робити із дизайном. Дизайнерських навичок не маю, а відверту лажу робити не хочу. Отже, якщо хтось має бажання допомогти або має знайомих, які можуть допомогти, керуючись лиш ентузіазмом - прошу, звертайтесь.</li>
<li>Поліпшити свої знання у Python, спробувати себе поза вебом.</li>
<li>Зайнятись спортом. Маючи сидячу роботу прогягом кількох років, починаєш розуміти, що фізчна форма поступово деградує, і постійні проблеми зі здоров&#8217;ям про це однозначно нагадують.</li>
<li>З&#8217;їздити кудись за кордон. Жодного разу не був поза Україною і вже давно не їздив кудись відпочівати. Хочу на <a title="Грандіозний музичний фестиваль у Будапешті" href="http://www.sziget.hu/festival_english">Sziget</a> <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Хоча українські фестивалі мене все так само цікавлять.</li>
</ol>
<p>Зараз стало модним писати про кризу і її наслідки, а я не буду <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Лиш побажаю, щоб ніхто не зважав ні на які махінації, політиків та провокаторів, а вірив у власний здоровий глузд та близьких людей. Все інше - не суттєве.<br />
Чарівна палиця прямує до <a title="virua" href="http://vispyanskiy.name/">virua</a> та <a title="GrAndSE" href="http://grandse.org.ua/">GrAndSE</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/various/happy-holidays-2009/feed</wfw:commentRss>
		</item>
		<item>
		<title>XSS: Що це таке і як правильно боротись? Частина І.</title>
		<link>http://webdeveloping.com.ua/php/preventing-cross-site-scripting-1</link>
		<comments>http://webdeveloping.com.ua/php/preventing-cross-site-scripting-1#comments</comments>
		<pubDate>Wed, 16 Jul 2008 00:07:31 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[безпека]]></category>

		<category><![CDATA[xss]]></category>

		<category><![CDATA[фільтрація]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=36</guid>
		<description><![CDATA[Після більш ніж місяця тиші маю честь представити вам чергову статтю про веб-безпеку.  
Міжсайтовий скриптинг (XSS) - це найчастіше використовуваний тип атак на веб-сайти (нещодавню статистику можна побачити у першій замітці із серії про безпеку у PHP). Для такої атаки, зловмисник зберігає у базі даних сайта, спеціально створений CSS, HTML або JavaScript. Пізніше, коли [...]]]></description>
			<content:encoded><![CDATA[<p>Після більш ніж місяця тиші маю честь представити вам чергову статтю про веб-безпеку. <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><strong>Міжсайтовий скриптинг</strong> (XSS) - це <em>найчастіше використовуваний</em> тип атак на веб-сайти (нещодавню статистику можна побачити у <a href="http://webdeveloping.com.ua/security/security-vs-php">першій замітці</a> із серії про безпеку у PHP). Для такої атаки, зловмисник зберігає у базі даних сайта, спеціально створений CSS, HTML або JavaScript. Пізніше, коли цей контент виводиться користувачу, скажімо у якості коментаря у блозі чи повідомлення на форумі, він змінює відображення сторінки чи виконує код, мета якого - вкрасти дані користувача, передати конфіденційні дані на лівий сервер чи якимось чином змінити функціонування сайта.</p>
<p>XSS - дуже популярний вид атаки та його часто <em>дуже легко</em> застосувати, адже неймовірно велика кількість сайтів <strong>просто виводить ввід користувача</strong>, без будь-якої попередньої фільтрації. Фактично, вдала XSS атака є результатом <em>неякісного</em> коду програми.</p>
<p>Два основних види XSS атак це:</p>
<ol>
<li><strong>Пряма дія</strong> - вставлений контент відображується тому користувачу, який його власне вставив;</li>
<li><strong>Збережена дія</strong> - будь-яка кількість користувачів побачить (і, можливо, відчує) вставлений контент.</li>
</ol>
<p>Метою <em>прямої дії</em> зазвичай є вивчення методів фільтрації вводу програмою, якщо такий взагалі є для того, щоб сконструювати більш значиму атаку. <em>Збережена ж дія</em> є <strong>найбільш небезпечним</strong> типом XSS, адже у результаті можуть бути вкрадені конфіденційні дані користувачів чи порушене функціонування самого сайту.</p>
<p>У цій частині ми розглянемо деякі методи боротьби із XSS (інші будуть розглянуті у наступних частинах).<br />
<span id="more-36"></span></p>
<h2>Закодувати все!</h2>
<p>Отже, як же нам убезпечити увесь ввід від XSS? На щастя, PHP має <em>вдосталь</em> вбудованих функцій, які видаляють або кодують спеціальні HTML символи.</p>
<p>Перша з них - <a href="http://ua2.php.net/manual/ru/function.htmlspecialchars.php">htmlspecialchars</a>. Вона приймає строку із введеними даними та кодує символи &amp; (амперсанд), &lt; (менш ніж), &gt; (більш ніж), &#8221; (подвійні лапки) та (необов&#8217;язково) &#8216; (одинарні лапки). Всі вони перетворюються у відповідні HTML-сутності такі, як &amp;lt; (&lt;), &amp;amp; (&amp;) і т.п. Це <strong>змушує</strong> браузер трактувати ці символи виключно як <em>літеральні</em> (тобто такі, які не мають ніякого спеціального змісту).</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3610">
        <div class="code" id="p36code10">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$input</span> <span class="sy0">=</span> <span class="st0">'&lt;a href=&quot;very.bad.com&quot;&gt;&lt;img src=&quot;click.gif&quot; /&gt;&lt;/a&gt;'</span><span class="sy0">;</span>
    <span class="re1">$encoded</span> <span class="sy0">=</span> <span class="kw3">htmlspecialchars</span><span class="br0">&#40;</span><span class="re1">$input</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//&amp;lt;a href=&quot;http://very.bad.com&quot;&amp;gt;&amp;lt;img src=&quot;click.gif&quot; /&amp;gt;&amp;lt;/a&amp;gt;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Тож краще кодувати навіть найпростіший ввід.</p>
<h3>Вгамування атрибутів</h3>
<p>Якщо необхідність кодування теґів HTML очевидна, то не всі усвідомлюють цю необхідність і для <strong>атрибутів теґів</strong>.</p>
<p>Значна кількість користувацького вводу врешті-решт опиняється у атрибутах теґів, які можуть надавати елементу стиль чи навіть виконувати JavaScript. Для ясності розглянемо приклад. Користувач відправляє URL, щоб вказати на цікаву сторінку. Цей ввід використовується для того, щоб сформувати теґ &lt;a&gt;: &lt;a href=&#8221;http://webdeveloping.com.ua&#8221;&gt;Блоґ про веб-розробку українською&lt;/a&gt;. Наче нормальна ситуація, а тепер уявімо, що користувач вставить у свій ввід лапки. І як тільки закриваючі лапки будуть знайдені, браузер закриє цей атрибут і відкриє новий. І тут зловмисник може зробити <em>все</em>, що завгодно.</p>
<p>Також слід пам&#8217;ятати, що типово одинарні лапки <strong>не будуть закодовані</strong>, а тільки подвійні. Тож, якщо ви використовуєте одинарні лапки у вашому HTML, це <strong>треба</strong> вказати функції htmlspecialchars другим параметром таким чином: htmlspecialchars(&#8221;&#8216;&#8221;, ENT_QUOTES). Інакше можливий такий сценарій:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3611">
        <div class="code" id="p36code11">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$input</span> <span class="sy0">=</span> <span class="kw3">htmlspecialchars</span><span class="br0">&#40;</span><span class="st0">&quot;#' real_url='http://mailicious.com'
    fake_url='http://php.net'
    onmouseover='window.status=this.attributes.fake_url.value; return true'
    onclick='window.location=this.attributes.real_url.value'&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw3">echo</span> <span class="st0">&quot;&lt;a href='$input'&quot;</span><span class="sy0">&gt;</span>Корисне посилання<span class="sy0">&lt;/</span>a<span class="sy0">&gt;;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Отже, що робить цей ввід? А він користується саме тим, що htmlspecialchars типово не кодує одинарні лапки, а сторінка їх використовує. Вводячи два атрибути real_url та fake_url, які є відповідно справжньою адресою та підробленою, цей код вставляє <em>підроблену</em> у строку статусу браузера при наведенні на посилання, та переходить на <em>справжню</em> при натисканні. Таким чином наш користувач <em>навіть не помітить</em>, що його хочуть відправити кудись не туди.</p>
<h3>HTML-сутності та фільтри</h3>
<p>Символ амперсанда часто використовується як початок HTML-сутності, але також може бути <em>задіяним для обходу</em> різних фільтрів. Нехай у нас існує фільтр, що знаходить всі строки &#8220;PERL&#8221; та видаляє їх. Користувач може легко закодувати всі символи цієї строки у їх відповідні HTML-сутності, таким чином обходячи фільтр стороною:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3612">
        <div class="code" id="p36code12">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$input</span> <span class="sy0">=</span> <span class="st0">'&amp;#80;&amp;#69;&amp;#82;&amp;#76;'</span><span class="sy0">;</span> <span class="co1">// PERL в закодованому вигляді </span>
    <span class="kw3">echo</span> str_ireplace<span class="br0">&#40;</span><span class="st0">'perl'</span><span class="sy0">,</span> <span class="st0">''</span><span class="sy0">,</span> <span class="re1">$input</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="co1">// Виведе незмінену строку, адже str_ireplace не знайде того, що треба</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Отже, перед фільтром нам треба закодувати ввід, щоб розбити сутності. Всі амперсанди будуть закодовані у &amp;amp;, тож спеціально створений ввід не пройде у запланованому вигляді:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3613">
        <div class="code" id="p36code13">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$input</span> <span class="sy0">=</span> <span class="st0">'&amp;#80;&amp;#69;&amp;#82;&amp;#76;'</span><span class="sy0">;</span> <span class="co1">// PERL в закодованому вигляді</span>
    <span class="re1">$input</span> <span class="sy0">=</span> <span class="kw3">htmlspecialchars</span><span class="br0">&#40;</span><span class="re1">$input</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// &amp;amp;#80;&amp;amp;#69;&amp;amp;#82;&amp;amp;#76;</span>
    <span class="kw3">echo</span> str_ireplace<span class="br0">&#40;</span><span class="st0">'perl'</span><span class="sy0">,</span> <span class="st0">''</span><span class="sy0">,</span> <span class="re1">$input</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//Все так само нічого не відфільтрує</span>
    <span class="co1">// Тепер браузер виведе &amp;#80;&amp;#69;&amp;#82;&amp;#76;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Це вже краще, але насправді, кодування амперсандів - це <strong>не завжди добре</strong> та в деяких випадках може навіть <strong>зашкодити</strong>. Це актуально наприклад для застарілих сайтів, які не використовують Unicode. Уявімо таку ситуацію (поширену на сайтах, що не думають про інтернаціоналізацію): є сторінка із формою у кодуванні ISO-8859-1 (типова латинська), а користувач вводить дані у кодуванні CP-1251 (типова кирилічна). Браузер автоматично переведе всі символи із вводу у HTML-сутності задля їх правильного виводу. Ми ж на стороні сервера кодуємо всі амперсанди із вводу, які в свою чергу втрачають початковий зміст, та в результаті отримуємо нечитабельний варіант.</p>
<p>Тож як нам передбачити цю ситуацію? Правильніше за все - використовувати Unicode <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Але, якщо це з деяких причин неможливо - найліпшим варіантом є задіяти регулярний вираз, щоб перевести у дійсні сутності лише подвійно-закодовані символи:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3614">
        <div class="code" id="p36code14">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">preg_replace</span><span class="br0">&#40;</span><span class="st0">'!&amp;amp;#([0-9]+);!'</span><span class="sy0">,</span> <span class="st0">'&amp;#<span class="es0">\1</span>;'</span><span class="sy0">,</span> <span class="kw3">htmlspecialchars</span><span class="br0">&#40;</span><span class="re1">$input</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Цим виразом ми захоплюємо подвійно-закодовані літери (адже шукаємо лише чисельні значення після &amp;amp;#) та замінюємо їх на дійсні сутності. Ми вирішуємо проблему із подвійно-закодованими літерами, але знову повертаємо попередню проблему. В такому випадку спеціально закодоване користувачем &#8220;PERL&#8221; буде <em>знову</em> виведене браузером, оминаючи фільтр.</p>
<p>Отже, нам потрібна краща логіка обробки HTML-сутностей. Для цього існує функція <a href="http://ua2.php.net/manual/ru/function.preg-replace-callback.php">preg_replace_callback</a> - вона задіює, передану їй функцію до <em>кожного</em> знайденого набору, що задовольняє регулярному виразу. Цій функції передається один масив, першим елементом якого є набір, що задовольняє виразу, а всіма наступними є набори, які задовольняють всім під-виразам. Тепер у нас є все, щоб дещо покращити наш фільтр:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3615">
        <div class="code" id="p36code15">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$input</span> <span class="sy0">=</span> <span class="kw3">htmlspecialchars</span><span class="br0">&#40;</span><span class="st0">'&amp;#80;&amp;#69;&amp;#82;&amp;#76;'</span><span class="br0">&#41;</span><span class="sy0">;</span> 
    <span class="kw2">function</span> decode<span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> 
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">&gt;</span> <span class="nu0">255</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="co1">// не-ASCII</span>
                <span class="kw1">return</span> <span class="st0">'&amp;#'</span><span class="sy0">.</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="sy0">.</span><span class="st0">';'</span><span class="sy0">;</span> <span class="co1">// переводимо у дійсну сутність</span>
        <span class="br0">&#125;</span> 
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">&gt;=</span> <span class="nu0">65</span> <span class="sy0">&amp;&amp;</span> <span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">&lt;=</span> <span class="nu0">90</span><span class="br0">&#41;</span> <span class="sy0">||</span> <span class="co1">// A - Z </span>
            <span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">&gt;=</span> <span class="nu0">97</span> <span class="sy0">&amp;&amp;</span> <span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">&lt;=</span> <span class="nu0">122</span><span class="br0">&#41;</span> <span class="sy0">||</span> <span class="co1">// a - z </span>
            <span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">&gt;=</span> <span class="nu0">48</span> <span class="sy0">&amp;&amp;</span> <span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">&lt;=</span> <span class="nu0">57</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="co1">// 0 - 9 </span>
                <span class="kw1">return</span> <span class="kw3">chr</span><span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// переводимо у літеральну форму</span>
        <span class="br0">&#125;</span> 
        <span class="kw1">return</span> <span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="sy0">;</span> <span class="co1">// все інше залишаємо без змін</span>
    <span class="br0">&#125;</span> 
    <span class="kw3">echo</span> <span class="kw3">preg_replace_callback</span><span class="br0">&#40;</span><span class="st0">'!&amp;amp;#([0-9]+);!'</span><span class="sy0">,</span> <span class="st0">'decode'</span><span class="sy0">,</span> <span class="re1">$input</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// PERL</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>У цьому прикладі функцію <em>decode</em> викликає preg_replace_callback та використовує під-вираз, який має чисельне значення для порівняння. Якщо це значення більше за 255 (тобто виходить за межі таблиці ASCII), то воно має бути перетворене у дійсну сутність, змінюючи &amp;amp; на &amp;. Для значень іншого діапазону ми робимо <em>додаткову</em> перевірку. Якщо це спеціальний символ (такий як &#8216; чи &lt;), то залишаємо все без змін, інакше (цифра або латинська літера) - переводимо у звичайну літеральну форму.</p>
<p>Однак навіть цього <strong>недостатньо</strong>, адже залишається декілька моментів із HTML-сутностями. Наприклад, сутність <em>не потребує</em> точки із комою в кінці. Тож &amp;#45 - цілком дійсна сутність, яку браузери чудово зрозуміють. Але в такому випадку наші регулярні вирази <em>проваляться</em> та нічого не знайдуть. Більше того, числове значення сутності може бути представлене у вигляді <em>шістнадцяткового числа</em>, тож &amp;#x05A - теж цілком дійсна сутність. А наші попередні регулярні вирази це також <em>не зрозуміють</em>. Отже, вдосконалюємо далі:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3616">
        <div class="code" id="p36code16">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">preg_replace_callback</span><span class="br0">&#40;</span><span class="st0">'!&amp;amp;#((?:[0-9]+)|(?:x(?:[0-9A-F]+)));?!i'</span><span class="sy0">,</span> <span class="st0">'decode'</span><span class="sy0">,</span> <span class="re1">$input</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Трошки ускладнили наш вираз. Тепер з одного під-виразу утворилось двоє: перший спрацьовує, коли ми маємо одну або більше цифру, другий - якщо є символ &#8220;x&#8221;, за яким йде одна або більше цифра чи літера латинського алфавіту з діапазону [A-F]. Окрім того, ми більше не вимагаємо символ &#8220;;&#8221; в кінці. Тут слід зазначити, що квантор &#8220;?:&#8221; означає те, що ми <em>не зберігаємо</em> результат під-запиту, таким чином у результуючому масиві буде так само два елементи. Модифікатор &#8220;i&#8221; в кінці виразу повідомляє про те, що порівняння відбувається <em>незалежно</em> від регістру символів.<br />
Тепер розглянемо відповідні доповнення до функції розкодування:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3617">
        <div class="code" id="p36code17">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw2">function</span> decode<span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> 
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">is_int</span><span class="br0">&#40;</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#123;</span><span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> 
                <span class="re1">$val</span> <span class="sy0">=</span> <span class="st0">'0'</span><span class="sy0">.</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span> <span class="sy0">+</span> <span class="nu0">0</span><span class="sy0">;</span> 
        <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span> 
                <span class="re1">$val</span> <span class="sy0">=</span> <span class="br0">&#40;</span>int<span class="br0">&#41;</span> <span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="sy0">;</span> 
        <span class="br0">&#125;</span> 
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$val</span> <span class="sy0">&gt;</span> <span class="nu0">255</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> 
                <span class="kw1">return</span> <span class="st0">'&amp;#'</span><span class="sy0">.</span><span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="sy0">.</span><span class="st0">';'</span><span class="sy0">;</span> 
        <span class="br0">&#125;</span> 
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="re1">$val</span> <span class="sy0">&gt;=</span> <span class="nu0">65</span> <span class="sy0">&amp;&amp;</span> <span class="re1">$val</span> <span class="sy0">&lt;=</span> <span class="nu0">90</span><span class="br0">&#41;</span> <span class="sy0">||</span> 
            <span class="br0">&#40;</span><span class="re1">$val</span> <span class="sy0">&gt;=</span> <span class="nu0">97</span> <span class="sy0">&amp;&amp;</span> <span class="re1">$val</span> <span class="sy0">&lt;=</span> <span class="nu0">122</span><span class="br0">&#41;</span> <span class="sy0">||</span> 
            <span class="br0">&#40;</span><span class="re1">$val</span> <span class="sy0">&gt;=</span> <span class="nu0">48</span> <span class="sy0">&amp;&amp;</span> <span class="re1">$val</span> <span class="sy0">&lt;=</span> <span class="nu0">57</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> 
                <span class="kw1">return</span> <span class="kw3">chr</span><span class="br0">&#40;</span><span class="re1">$val</span><span class="br0">&#41;</span><span class="sy0">;</span> 
        <span class="br0">&#125;</span> 
        <span class="kw1">return</span> <span class="re1">$matches</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="sy0">;</span> 
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Тут ми додали перевірку і приведення для шістнадцяткових чисел. Спочатку перевіряємо чи перший символ результату є числом. Якщо це так, то маємо десяткове число, приводимо його до цілочислового типу (int), інакше (перший символ - &#8220;x&#8221;) - дописуємо 0 спочатку (таким чином PHP починає розуміти, що йому згодували шістнадцяткове число, тобто маємо щось типу 0&#215;5F) та неявно переводимо до десяткового формату, додаючи до нашого числа 0. Далі код не змінився.<br />
Ось такий буде результат:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3618">
        <div class="code" id="p36code18">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$input</span> <span class="sy0">=</span> <span class="st0">'&amp;#80&amp;#69&amp;#82&amp;#76;&amp;#60;&amp;#X041;&amp;#1103;&amp;#x30C9;'</span><span class="sy0">;</span> 
    <span class="co1">// результат обробки:</span>
    <span class="co1">// PERL&amp;amp;#60;A&amp;#1103;&amp;#x30C9;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Ось тепер все добре, і після цієї обробки <em>нарешті</em> можна використовувати літеральну фільтрацію <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>To be continued&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/php/preventing-cross-site-scripting-1/feed</wfw:commentRss>
		</item>
		<item>
		<title>Mozilla збирається поставити світовий рекорд</title>
		<link>http://webdeveloping.com.ua/firefox/mozilla-and-guinness-world-record</link>
		<comments>http://webdeveloping.com.ua/firefox/mozilla-and-guinness-world-record#comments</comments>
		<pubDate>Thu, 29 May 2008 23:43:24 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[firefox]]></category>

		<category><![CDATA[microsoft]]></category>

		<category><![CDATA[mozilla]]></category>

		<category><![CDATA[популяризація Firefox]]></category>

		<category><![CDATA[Світовий Рекорд Гіннесса]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=37</guid>
		<description><![CDATA[Нещодавно з&#8217;явився соціальний проект від компанії Mozilla, пов&#8217;язаний із скорим виходом нової версії браузера Firefox 3. Суть у тому, щоб зусиллями суспільства здобути Світовий Рекорд Гіннесcа самої завантажуваної програми за 24 години. Все, що потрібно зробити аби допомогти у цьому розробнику Firefox, компанії Mozilla - це зайти на сайт проекту, дати обіцянку завантажити Firefox 3 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.spreadfirefox.com/worldrecord"><img class="alignleft" style="border: 0pt none; float: left;" title="День Загрузки 2008" src="http://www.spreadfirefox.com/sites/all/themes/spreadfirefox_RCS/images/download-day/buttons/ru/dday_badge_fox.png" border="0" alt="День Загрузки 2008" /></a>Нещодавно з&#8217;явився соціальний проект від <a href="http://www.mozilla.com/">компанії Mozilla</a>, пов&#8217;язаний із скорим виходом нової версії <a href="http://www.mozilla.com/en-US/firefox/">браузера Firefox</a> 3. Суть у тому, щоб зусиллями суспільства здобути <a href="http://www.guinnessworldrecords.com/default.aspx">Світовий Рекорд Гіннесcа</a> самої завантажуваної програми за 24 години. Все, що потрібно зробити аби допомогти у цьому розробнику Firefox, компанії Mozilla - це зайти на <a title="Світовий Рекорд Гіннеса найбільш завантажуваної програми за 24 години" href="http://www.spreadfirefox.com/en-US/worldrecord">сайт проекту</a>, дати обіцянку завантажити Firefox 3 у День Завантаження та не забути її виконати у цей день. Точна дата поки що невідома, але в разі, якщо ви дасте обіцянку, то вам буде вчасно надіслано запрошення від проекту.</p>
<p>У цій нелегкій справі проектом прийнято на озброєння повний арсенал соціальних інструментів: створено групи присвячені цій події у популярних соціальних мережах (MySpace, Facebook та Beebo), спеціальний аккаунт у Twitter, значки для блогів, навіть <a title="Організація тематичних вечірок Mozilla" href="http://mozillaparty.com/">ось такий</a> цікавий проект для організації тематичних вечірок <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Наразі набільш багаті на обіцянки США (звичайно ж), Польща, <del>Албанія(!)</del> <ins>Італія</ins>, Бразилія, Німеччина та Японія. В Україні дано більш ніж 1,5 тисячі обіцянок, а всього близько 200 тисяч і стрімко зростає. На мою думку, це чудова ідея піарщиків Mozilla з популяризації цього вільного браузера.</p>
<p>Наостанок хочу побажати проекту успіхів у здобутті рекорду та популяризації Firefox, адже, чим більше людей користуватиметься Firefox та іншими, альтернативними до ІЕ, браузерами - тим більше буде мотивації у Microsoft робити кращі (за поточні ІЕ) баузери, та тим легше житиметься багатьом веб-розробникам <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/firefox/mozilla-and-guinness-world-record/feed</wfw:commentRss>
		</item>
		<item>
		<title>Ви JavaScript-гуру? Тоді вам необхідний власний логотип!</title>
		<link>http://webdeveloping.com.ua/various/javascript-gurus-contest</link>
		<comments>http://webdeveloping.com.ua/various/javascript-gurus-contest#comments</comments>
		<pubDate>Thu, 22 May 2008 02:05:27 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[різне]]></category>

		<category><![CDATA[javascript]]></category>

		<category><![CDATA[unobtrusive JavaScript]]></category>

		<category><![CDATA[гуру]]></category>

		<category><![CDATA[конкурс]]></category>

		<category><![CDATA[кросбраузерність]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=35</guid>
		<description><![CDATA[На блозі Нікіти Селецького нещодавно почався конкурс для програмістів на JavaScript. Суть задачі не складна та цілком практична (стосується мінімальної перевірки введених у форму даних) - мені знадобилось близько півтори години на її вирішення, хоча я й ніякий не гуру  Головні вимоги:

Кросбраузерність
Універсальність - скрипт має вірно працювати навіть при зміні/додаванні нових елементів
Неможна використовувати фреймворки [...]]]></description>
			<content:encoded><![CDATA[<p>На <a title="Блоґ Нікіти Селецького" href="http://seleckis.lv/">блозі Нікіти Селецького</a> нещодавно <a title="Конкурс для JavaScript-гуру" href="http://seleckis.lv/journal/fun/konkurs-dlya-javascript-guru">почався конкурс для програмістів на JavaScript</a>. Суть задачі не складна та цілком практична (стосується мінімальної перевірки введених у форму даних) - мені знадобилось близько півтори години на її вирішення, хоча я й ніякий не гуру <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Головні вимоги:</p>
<ul>
<li>Кросбраузерність</li>
<li>Універсальність - скрипт має вірно працювати навіть при зміні/додаванні нових елементів</li>
<li>Неможна використовувати фреймворки та бібліотеки JavaScript</li>
<li>Неможна змінювати HTML чи CSS, всі зміни лиш у відповідному JS-файлі. Отже практикуємо підхід &#8220;unobtrusive JavaScript&#8221; (ненав&#8217;язливий JS).</li>
</ul>
<p>Сам конкурс триватиме до кінця цього місяця (травня) тож ще є вдосталь часу. Ну і найголовніше - переможець отримує логотип свого блоґу від автора. У будь-якому випадку, непогана вправа для вільного часу.</p>
<p>Тож щасти вам, якщо бажаєте спробувати!</p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/various/javascript-gurus-contest/feed</wfw:commentRss>
		</item>
		<item>
		<title>CakePHP 1.2 - незабаром стабільний</title>
		<link>http://webdeveloping.com.ua/php/cakephp/cakephp-12-stable-soon</link>
		<comments>http://webdeveloping.com.ua/php/cakephp/cakephp-12-stable-soon#comments</comments>
		<pubDate>Wed, 14 May 2008 01:15:46 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[cakephp]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=33</guid>
		<description><![CDATA[Один з найпопулярніших фреймворків для розробки веб-програм на PHP під назвою CakePHP дуже скоро вийде у світ із новою стабільною версією! Цей довгоочікуваний момент для великого комьюніті веб-розробників незабаром настане. MVC фреймворк із відкритим кодом CakePHP 1.2 принесе розробникам багато нових корисних речей, описаних далі


Модулі поведінки моделей (Model Behavior)
Три стандартних модулі поведінки: Acl, Translate, Tree
Підтримка [...]]]></description>
			<content:encoded><![CDATA[<p>Один з <em>найпопулярніших</em> фреймворків для розробки веб-програм на PHP під назвою <a title="Веб-фреймворк CakePHP" href="http://cakephp.org/">CakePHP</a> дуже скоро вийде у світ із новою стабільною версією! Цей довгоочікуваний момент для великого комьюніті веб-розробників незабаром настане. <a title="Опис паттерну Модель-Вид-Контроллер" href="http://uk.wikipedia.org/wiki/%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C-%D0%B2%D0%B8%D0%B4-%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%B5%D1%80">MVC</a> фреймворк із відкритим кодом CakePHP 1.2 принесе розробникам багато нових корисних речей, описаних далі</p>
<p><span id="more-33"></span></p>
<ul>
<li>Модулі поведінки моделей (Model Behavior)</li>
<li>Три стандартних модулі поведінки: Acl, Translate, Tree</li>
<li>Підтримка DB2, Oracle та Sybase</li>
<li>Підтримка інтернаціоналізації та локалізації</li>
<li>Три нових компоненти ядра: Auth, Cookie, Email</li>
<li>Нові класи ядра: Set, Debugger, HttpSocket, Socket</li>
<li>Іменовані агрументи для дій</li>
<li>Більше можливостей для кешування - підтримка: APC, memcache, xcache, файлового та кеша, основаного на базі даних</li>
<li>Більше можливостей валідації даних</li>
<li>Чотири нових допоміжних класів: Js, Paginator, Rss, Xml</li>
<li>Підтримка тем</li>
<li>Інтегровані тести</li>
<li>Багато-багато інших нових можливостей та виправлень</li>
</ul>
<p>Я розробляю деякі проекти (серед них власна CMS для веб-студії) за допомогою цього фремворка вже близько року, всі були зроблені на нестабільних версіях гілки 1.2. Мушу сказати, що навіть <em>альфа</em> версії були <strong>стабільніші за більшість релізів</strong> іншого програмного забезпечення <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Реліз CakePHP 1.2 має стати чимось на стільки стабільним, наскільки стійкою та непорушною є скеля.</p>
<p>Зміни у репозиторії коду зараз відбуваються <em>кожні кілька хвилин</em>. Наразі розробники цього open source проекту шукають добровольців, які б могли допомогти із наступними речами:</p>
<ul>
<li>Долучення патчів та тестів</li>
<li>Надання інформації про відтворення багів</li>
<li>Пошук нових багів</li>
<li>Допомога команді документування</li>
</ul>
<p>Як пише один з розробників фреймворку, <a title="Маріано Іглесіас про скорий вихід CakePHP 1.2" href="http://bakery.cakephp.org/articles/view/cakephp-1-2-stable-coming-soon">Маріано Іглесіас</a>, допомога проекту - хороший шанс долучитись до одного з найдинамічніших та найцікавіших PHP проектів із відкритим кодом.</p>
<p>Від себе хочу сказати&#8230; нарешті!:) Гілка 1.2 перебуває у нестабільній стадії вже більше року і дуже багато змінилось за цей час у кращу сторону. Думаю, багато людей чекають на цей реліз. У одній з наступних заміток чекайте на огляд фреймворку CakePHP та порівняння із іншими популярними бібліотеками. А якими фреймворками користуєтесь <strong>ви</strong>?</p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/php/cakephp/cakephp-12-stable-soon/feed</wfw:commentRss>
		</item>
		<item>
		<title>SQL Injection або як зробити вашу базу даних безпечнішою</title>
		<link>http://webdeveloping.com.ua/php/sql-injection-securing-your-database-2</link>
		<comments>http://webdeveloping.com.ua/php/sql-injection-securing-your-database-2#comments</comments>
		<pubDate>Wed, 07 May 2008 01:46:47 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[безпека]]></category>

		<category><![CDATA[caching]]></category>

		<category><![CDATA[linux]]></category>

		<category><![CDATA[MySQL]]></category>

		<category><![CDATA[PostgreSQL]]></category>

		<category><![CDATA[SQL]]></category>

		<category><![CDATA[SQL Injection]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=31</guid>
		<description><![CDATA[SQL Injection (вставка SQL) - це одна з найпоширеніших на ряду із XSS (міжсайтовим скриптингом, про який буде мова у наступній статті) уразливостей, яка є наслідком недостаньої (чи навіть відсутньої) перевірки вводу. Ціль цього типу атак є безпосередньо база даних вашого сайту. Суть - вставка сторонніх даних (найчастіше - запиту до БД) у строку, яка [...]]]></description>
			<content:encoded><![CDATA[<p><em>SQL Injection</em> (вставка SQL) - це одна з найпоширеніших на ряду із <em>XSS</em> (міжсайтовим скриптингом, про який буде мова у наступній статті) уразливостей, яка є наслідком недостаньої (чи навіть відсутньої) перевірки вводу. Ціль цього типу атак є <strong>безпосередньо база даних</strong> вашого сайту. Суть - вставка сторонніх даних (найчастіше - запиту до БД) у строку, яка в результаті буде виконана базою даних. Такий запит може призвести до великої кількості небажаних дій: починаючи від <em>отримання</em> зловмиником будь-яких даних до їх <em>зміни</em> чи <em>видалення</em>. Тож важливо знати, як захистити свою базу даних від таких уразливостей.<br />
<span id="more-31"></span></p>
<p>Для оцінки маштабів цієї проблеми приведемо приклад:<br id="cflp0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3135">
        <div class="code" id="p31code35">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="st0">&quot;4'; DELETE FROM users;&quot;</span><span class="sy0">;</span> <span class="co1">//можливий ввід</span>
    <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st0">&quot;SELECT * FROM users WHERE id='$userId'&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="oykg3" />Цей шмат коду має на меті вибрати з бази даних дані користувача за переданим у скрипт ідентифікатором. І, якщо ви використовуєте MySQL, то власне так і зробить <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Адже ця БД не підтримує декілька запросів у одному виклику. А якщо ж ваша БД - SQLite чи PostgreSQL, то вони, не довго думаючи, спочатку виберуть потрібні дані, а потім <em>видалять</em> всі дані з таблиці &#8220;users&#8221;.<br id="z0yv0" /><br id="z0yv1" /></p>
<h3 id="z0yv2">Екранування вводу</h3>
<p>Проблемою у даному випадку є <em>недостатнє екранування</em> і <em>перевірка вводу</em>. Про це вже було достатньо сказано у статті про <a rel="nofollow" href="http://webdeveloping.com.ua/php/php-user-input-data-validation" title="Стаття про перевірку вхідних даних">перевірку вхідних даних</a>. Давайте розглянемо це у контексті SQL Injection.  Найпростіший захист від цього надає вбудований у PHP механізм <em>magic_quotes_gpc</em>. Коли він увімкнений, перед лапками (одинарними чи подвійними) та іншими загрозливими символами автоматично буде вставлено зворотній слеш (&#8221;\&#8221;).</p>
<p>Однак це <em>недостатнє</em> рішення, оскільки не екранує деякі спеціальні до SQL символи та й не завжди цей механізм увімкнено. Тож різні розширення для роботи із БД мають свої спеціалізовані механізми екранування. У MySQL це функція <a rel="nofollow" href="http://ua2.php.net/manual/ru/function.mysql-real-escape-string.php">mysql_real_escape_string</a> та <a rel="nofollow" href="http://ua2.php.net/manual/ru/function.mysql-escape-string.php">mysql_escape_string</a>. Різниця між ними лиш у тому, що перша екранує відносно указаного їй у параметрах <em>кодування</em>, а друга до цього <em>не чутлива</em>. Приклад використання:<br id="ncap0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3136">
        <div class="code" id="p31code36">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">get_magic_quotes_gpc</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re1">$userId</span> <span class="sy0">=</span> <span class="kw3">stripslashes</span><span class="br0">&#40;</span><span class="re1">$userId</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="kw3">mysql_real_escape_string</span><span class="br0">&#40;</span><span class="re1">$userId</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st0">&quot;SELECT * FROM users WHERE id='$userId'&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="q53o1" />У цьому коді спочатку перевіряється наявність розширення magic_quotes_gpc, в разі чого його дія реверсується, щоб не виникло ситуації з подвійним екрануванням. Також специфічні до БД функції є незамінними при зберіганні двійкових даних. Якщо залишити їх неекранованими - вони можуть <em>конфліктувати</em> із форматом збереження даних самої БД. Функції екранування про це знають і <em>не допускають можливої втрати даних</em>.</p>
<p>У деяких БД, наприклад, PostgreSQL - окремі функції відповідають за екранування двійкових даних. Наприклад, функція <a rel="nofollow" href="http://ua2.php.net/manual/ru/function.pg-escape-bytea.php">pg_escape_bytea</a> застосовує Base64-подібне кодування до вхідних даних. Щоправда, таке кодування <em>накладає обмеження на пошук</em> - до них неможна застосовувати такі запити як &#8220;LIKE &#8217;str%&#8217;&#8221;, оскільки значення, збережене у БД, не обов&#8217;язково буде сходитись із тим, яке шукається за запитом. Звичайно, це не проблема для більшості веб-додатків, адже збереження у двійковому вигляді зазвичай необхідно для таких даних як картинки та зтиснені файли, а вони не є предметом глибинного пошуку.<br id="rlxc0" /><br id="rlxc1" /></p>
<h3 id="rlxc2">Підготовлені вирази</h3>
<p>На жаль, не для всіх БД існують специфічні функції екранування. Фактично, вони є лиш для: MySQL, PortgreSQL, SQLite, Sybase та MaxDB. Для інших популярних баз даних як Oracle, MS SQL Server та інших необхідно знайти альтернативу.</p>
<p>На перший погляд може підійти кодування даних у <em>Base64</em>, що ефективно екранує всі спеціальні символи. Але це створює кілька проблем: <em>розмір даних</em>, закодованих Base64 <em>зростає рівно на 33%</em> та <em>унеможливлює вибірку даних</em> за допомогою LIKE. Тож потрібен кращий метод.</p>
<p>Цей метод називається <strong>підготовленими виразами</strong> (або запитами). Такі запити фактично є <em>шаблонами</em> з визначеною структурою та місцями для реальних даних. Часто ці місця суворо типізовані для чисельних та текстових даних. Якщо тип даних <em>не збігається</em> із визначеним типом, то буде видана помилка, що додає ще один ступінь перевірки даних. Також підготовлені вирази додають швидкодію, адже вони компілюються <em>лиш один раз</em> і можуть використовуватись після цього із будь-якими даними. Наприклад, для PostgreSQL це може виглядати так:<br id="h62e0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3137">
        <div class="code" id="p31code37">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">pg_query</span><span class="br0">&#40;</span><span class="re1">$db</span><span class="sy0">,</span> <span class="st0">'PREPARE stmt_id (int) AS SELECT * FROM users WHERE id=$1'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw3">pg_query</span><span class="br0">&#40;</span><span class="re1">$db</span><span class="sy0">,</span> <span class="st0">&quot;EXECUTE stmt_id ($userId)&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw3">pg_query</span><span class="br0">&#40;</span><span class="re1">$db</span><span class="sy0">,</span> <span class="st0">'DEALLOCATE stmt_id'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="styk1" />Якщо ви працюєте із MySQL, то підготовлені запити можна використовувати лише із розширенням mysqli чи PDO. Приклад:<br id="t:wh0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3138">
        <div class="code" id="p31code38">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$stmt</span> <span class="sy0">=</span> <span class="re1">$mysqli</span><span class="sy0">-&gt;</span><span class="me1">stmt_init</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$stmt</span><span class="sy0">&gt;</span>prepare<span class="br0">&#40;</span><span class="st0">'SELECT * FROM users WHERE id=?'</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">bind_param</span><span class="br0">&#40;</span><span class="st0">'i'</span><span class="sy0">,</span> <span class="re1">$userId</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//&quot;i&quot; означає integer</span>
        <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">bind_result</span><span class="br0">&#40;</span><span class="re1">$user</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">fetch</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="re1">$stmt</span><span class="sy0">-&gt;</span><span class="me1">close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="yv.d0" />Використання трохи різне, але принцип однаковий. Спочатку створюється шаблон запиту, потім приєднуються реальні дані за їх типом та виконується сам запит. Підготовлені вирази - дуже класний інструмент, але дійсний не для всіх БД. У цих випадках слід використовувати екранування.<br id="evu30" /><br id="evu31" /></p>
<h3 id="r8x-0">Коли екранування не допомагає</h3>
<p>Існують випадки, коли екранування даних <em>не убезпечить</em> від зловмисних дій. Уявімо таку ситуацію:<br id="v7.i0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3139">
        <div class="code" id="p31code39">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="st0">'0; DELETE FROM users'</span><span class="sy0">;</span>
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="kw3">pg_escape_string</span><span class="br0">&#40;</span><span class="re1">$userId</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw3">pg_query</span><span class="br0">&#40;</span><span class="re1">$db</span><span class="sy0">,</span> <span class="st0">&quot;SELECT * FROM users WHERE id=$id&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="p:la1" />Запит у даному випадку цілком дійсний, адже ми очікуємо чисельні дані і їх не обов&#8217;язково брати у лапки. Цим і наражаємо себе на небезпеку у вигляді <em>SQL Injection</em>. Перший вихід з цієї ситуації - <em>абсолютно всі</em> вхідні дані брати у лапки при використанні у запиті, незалежно від їх типу. Але як ми зараз побачимо, це також не дуже добре:<br id="u_9e0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3140">
        <div class="code" id="p31code40">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="st0">&quot;0'; DELETE FROM users&quot;</span><span class="sy0">;</span>
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="kw3">pg_escape_string</span><span class="br0">&#40;</span><span class="re1">$userId</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw3">pg_query</span><span class="br0">&#40;</span><span class="re1">$db</span><span class="sy0">,</span> <span class="st0">&quot;SELECT * FROM users WHERE id='$id'&quot;</span><span class="br0">&#41;</span>
             or <span class="kw3">die</span><span class="br0">&#40;</span><span class="kw3">pg_last_error</span><span class="br0">&#40;</span><span class="re1">$db</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="bzt42" />У цьому випадку звичайно запит не буде виконано, натомість буде видана помилка і це призведе до краху вашої програми. Але ми можемо цього легко і просто уникнути звичайним приведенням типу. Таким чином запит буде вдало виконано і нічого не зупиниться:<br id="bq.e0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3141">
        <div class="code" id="p31code41">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="st0">'22; DELETE FROM users'</span><span class="sy0">;</span>
    <span class="re1">$userId</span> <span class="sy0">=</span> <span class="br0">&#40;</span>int<span class="br0">&#41;</span><span class="re1">$userId</span><span class="sy0">;</span> <span class="co1">// 22</span>
    <span class="kw3">pg_query</span><span class="br0">&#40;</span><span class="re1">$db</span><span class="sy0">,</span> <span class="st0">&quot;SELECT * FROM users WHERE id=$id&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="up6o1" />Це буде абсолютно безпечно, адже до запиту прийдуть <em>лише чисельні дані</em>. До того ж це підвищить швидкодію, оскільки приведення типу - дуже швидка операція, яка убирає необхідність виклику функції екранування.<br id="v2l60" /><br id="v2l61" /></p>
<h3 id="oc2l0">Оператор LIKE</h3>
<p>Оператор LIKE у SQL запитах є дуже корисним. Він дозволяє робити вибірку в залежності від знайдених у полі підстрок. Це робиться за допомогою його складових &#8220;<em>%</em>&#8221; та &#8220;<em>_</em>&#8220;, які дозволяють відповідно знаходити строки із 0 чи більшою кількістю будь-яких символів та один будь-який символ. Проблема ж у тому, що ані функції екранування, ані magic quotes не впливають на ці символи. Тож із деякими комбінаціями можна <em>змінити запит</em>, <em>ускладнити вибірку</em> та у багатьох випадках <em>не дозволити використання індексів</em>, що значно вповільнить запит і надасть ґрунт до запуску DoS атаки на ваш сервер БД. Ось надзвичайно простий приклад:<br id="l70j0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3142">
        <div class="code" id="p31code42">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$string</span> <span class="sy0">=</span> <span class="kw3">mysql_real_escape_string</span><span class="br0">&#40;</span><span class="st0">'%substring'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// так само %substring</span>
    <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st0">&quot;SELECT * FROM posts WHERE title LIKE '{$string}%'&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="ymo71" />Суть запиту у тому, щоб вибрати ті записи, у яких назва починається з деякої строки. Такий запит мав би виконатись досить швидко за наявності індексації колонки &#8220;title&#8221;. Але, якщо у строку буде вставлено ще один символ &#8220;%&#8221; на початок, то це унеможливить використання індекса і значно сповільнить запит, при чому, чим більше записів у таблиці, тим повльніше буде здійснюватись пошук.</p>
<p>Символ &#8220;_&#8221; представляє схожу але дещо іншу проблему. Використання цього символу перед закінченням строки пошуку унеможливить використання індекса, а наявність його у кінці - дасть інший результат. Окрім того, символ &#8220;_&#8221; є досить часто вживаним, тож може стати проблемним зовсім випадково без злих намірів. Тож нам потрібно <em>розширити множину екранованих символів</em>. Для цього існує вбудована функція <a rel="nofollow" href="http://ua2.php.net/manual/ru/function.addcslashes.php">addcslashes</a>, яка приймає строку із символами для екранування другим параметром:<br id="pmvi0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3143">
        <div class="code" id="p31code43">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$str</span> <span class="sy0">=</span> <span class="kw3">addcslashes</span><span class="br0">&#40;</span><span class="kw3">mysql_real_escape_string</span><span class="br0">&#40;</span><span class="st0">'%something_'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st0">'%_'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="co1">//$str = '\%something\_'</span>
    <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st0">&quot;SELECT * FROM posts WHERE title LIKE '{$str}%'&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="du6m1" />Тож функція addcslashes є таким собі нестандартним <a rel="nofollow" href="addslashes">addslashes</a> і є <em>досить ефективною</em> - значно швидша за str_replace чи еквівалентний регулярний вираз.<br id="odt50" /><br id="odt51" /></p>
<h3 id="ek230">Слідкуємо за помилками</h3>
<p>Один із звичайних шляхів для зловмисника при пошуку уразливого до SQL Injection коду - <em>використання інструментів розробників проти них самих</em>. Наприклад, для полегшення відлагодження SQL-запитів, розробники часто виводять запит, що призвів до помилки і власне саму помилку бази даних:<br id="pkxm0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3144">
        <div class="code" id="p31code44">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="re1">$query</span><span class="br0">&#41;</span> or <span class="kw3">die</span><span class="br0">&#40;</span><span class="st0">&quot;Помилка запиту: $query&lt;br /&gt;&quot;</span><span class="sy0">.</span><span class="kw3">mysql_error</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="qhv71" />Часто після процесу розробки такий код потрапляє на робочий варіант сайту і тоді, окрім того, що звичайні користувачі бачать ваш сайт в агонії, ще й зловмисники отримують <em>купу інформації</em> про вашу програму. Наприклад, можливо проаналізувати SQL-запит на предмет колонок використаних таблиць та змайструвати GET чи POST запити, які можуть призвести до ін&#8217;єкції SQL. Більше того, помилка може стати результатом спроби SQL-вставки і послужить зловмиснику <em>інструкцією зі створення більш хитрих запитів</em>.<br id="pjmm0" />Найпростіший шлях уникнути цього - написати власний обробник помилок SQL:<br id="pjmm1" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3145">
        <div class="code" id="p31code45">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw2">function</span> sql_failure_handler<span class="br0">&#40;</span><span class="re1">$query</span><span class="sy0">,</span> <span class="re1">$error</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re1">$msg</span> <span class="sy0">=</span> <span class="kw3">htmlspecialchars</span><span class="br0">&#40;</span><span class="st0">&quot;Невдалий запит: $query&lt;br /&gt;Помилка SQL: $error&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw3">error_log</span><span class="br0">&#40;</span><span class="re1">$msg</span><span class="sy0">,</span> <span class="nu0">3</span><span class="sy0">,</span> <span class="st0">'/path/to/site/logs/sql_error.log'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">defined</span><span class="br0">&#40;</span><span class="st0">'debug'</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw1">return</span> <span class="re1">$msg</span><span class="sy0">;</span>
        <span class="br0">&#125;</span>
&nbsp;
        <span class="kw1">return</span> <span class="st0">'Ця сторінка тимчасово недоступна. Будь ласка, повторіть спробу пізніше.'</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="re1">$query</span><span class="br0">&#41;</span> or <span class="kw3">die</span><span class="br0">&#40;</span>sql_failure_handler<span class="br0">&#40;</span><span class="re1">$query</span><span class="sy0">,</span> <span class="kw3">mysql_error</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="bxuo0" />Ось і все. Обробник приймає строки із запитом і помилкою, виводить інформацію про збій у файл журналу, та, якщо увімкнено режим відлагодження - виводить повідомлення на екран. У робочому ж середовищі, користувач нічого, окрім стандартного повідомлення про недоступність ресурсу, не побачить.<br id="ohqo0" /><br id="ohqo1" /></p>
<h3 id="nr_20">Збігання даних про доступ до БД</h3>
<p>Дуже важливим у створенні безпечного середовища є те, де ви зберігаєте дані про доступ до БД, тобто <em>ваші логін та пароль</em>. Більшість веб-програм використовують маленький PHP-файл, у якому логін та пароль присвоюються деяким змінним чи константам. На цей файл часто встановлюються права на читання для будь-кого, щоб веб-сервер міг отримати до нього доступ. Але це означає, що будь-хто на цій системі чи експлоіт може прочитати цей файл та <em>вкрасти ваші дані</em> про доступ до БД. Більше того, іноді цей файл кладуть у директорії, доступній для будь-кого по ту сторону сервера та дають йому яке-небудь розширення, не асоційоване із PHP. Наприклад, популярним вибором є &#8220;<em>.inc</em>&#8220;. Такі розширення типово не налаштовані на інтерпретацію як PHP-скрипти і веб-сервер просто <strong>віддає їх як звичайний текст</strong>, який будь-хто може побачити.</p>
<p>Вирішень цієї проблеми декілька з різною ступінню безпечності. По-перше, можна використати можливості веб-сервера, такі як файл налаштування <em>.htaccess</em> у Apache для того, щоб заборонити доступ до деяких файлів. Наприклад ось директива, яка забороняє доступ до будь-яких файлів із розширенням .inc:<br id="xjfh0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3146">
        <div class="code" id="p31code46">
            <pre class="apache">&lt;Files ~ <span class="st0">&quot;<span class="es0">\.</span>inc$&quot;</span>&gt;
    <span class="kw1">Order</span> <span class="kw1">allow</span>,<span class="kw1">deny</span>
    <span class="kw1">Deny</span> <span class="kw1">from</span> <span class="kw1">all</span>
&lt;/Files&gt;</pre>
        </div>
    </div>
</div>

<p><br id="xjfh6" />Або ж, наприклад, можна змусити PHP інтерпретувати .inc файли, як скрипти або змінити розширення на <em>.php</em> або <em>.inc.php</em>. Щоправда, перейменування файлів - не завжди найбезпечніший варіант. Найкращий і найпростіший спосіб - <em>не зберігати</em> такі файли у директоріях, доступних для веб-сервера. Але це все-одно <em>не убезпечує</em> від використання експоітів локальними користувачами (актуально, якщо ви на віртуальному хостингу).</p>
<p>Одне, на перший погляд, гарне вирішення проблеми - <em>шифрування</em> важливих даних. Але це робить крадіжку лиш дещо складнішою і <em>не вирішує проблеми</em>, адже ключ до шифру все-одно має бути доступним для PHP-скрипта, який запускається із користувачем веб-сервера, що означає те, що ключ все-одно є доступним для читання будь-ким. Повертаємось до того, з чого і почали.</p>
<p>Правильне вирішення має давати гарантію, що інші користувачі системи <em>не мають жодного шансу</em> побачити дані доступу до БД. На щастя, Apache надає такий інструмент. Файл налаштування Apache, <em>httpd.conf</em>, може включати в себе сторонні файли налаштування у момент запуску процесу веб-сервера, тобто доки він ще працює як <em>root</em>. Оскільки <em>root</em> має доступ до будь-яких файлів, ви можете покласти важливу інформацію у файлі в своїй домашній директорії (/home/user/) і надати права на читання лиш для себе (<em>0600</em>). Таким чином тільки ви та root зможуть читати цей файл. Робиться це таким чином:<br id="fmcn0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3147">
        <div class="code" id="p31code47">
            <pre class="apache">&lt;VirtualHost webdeveloping.com.ua&gt;
    <span class="kw1">Include</span> /home/webdeveloping/sql.conf
&lt;/VirtualHost&gt;</pre>
        </div>
    </div>
</div>

<p><br id="e68q3" />Сам же <em>sql.con</em>f встановлює наступні змінні середовища:<br id="b5h00" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3148">
        <div class="code" id="p31code48">
            <pre class="apache"><span class="kw1">SetEnv</span> DB_LOGIN <span class="st0">&quot;login&quot;</span>
<span class="kw1">SetEnv</span> DB_PASS <span class="st0">&quot;password&quot;</span>
<span class="kw1">SetEnv</span> DB_NAME <span class="st0">&quot;my_database&quot;</span>
<span class="kw1">SetEnv</span> DB_HOST <span class="st0">&quot;127.0.0.1&quot;</span></pre>
        </div>
    </div>
</div>

<p><br id="px_o2" />Після запуску Apache, ці змінні будуть доступні PHP через суперглобальний масив <em>$_SERVER</em> або функцію <a rel="nofollow" href="http://ua2.php.net/manual/ru/function.getenv.php">getenv</a>:<br id="jfz60" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3149">
        <div class="code" id="p31code49">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">echo</span> <span class="re1">$_SERVER</span><span class="br0">&#91;</span><span class="st0">'DB_LOGIN'</span><span class="br0">&#93;</span><span class="sy0">;</span> <span class="co1">//login</span>
    <span class="kw3">echo</span> <span class="kw3">getenv</span><span class="br0">&#40;</span><span class="st0">'DB_LOGIN'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//login</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="jfz66" />А ще краще заховати ці дані навіть від скрипта, якому вони потрібні <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Можна використати директиви <em>php.ini</em> для визначення типових даних для зв&#8217;язку із БД. Це також можна встановити у захищеному файлі налаштувань Apache:<br id="w8s80" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p3150">
        <div class="code" id="p31code50">
            <pre class="apache">php_admin_value mysql.default.host <span class="st0">&quot;127.0.0.1&quot;</span>
php_admin_value mysql.default.<span class="kw1">user</span> <span class="st0">&quot;login&quot;</span>
php_admin_value mysql.default.password <span class="st0">&quot;password&quot;</span></pre>
        </div>
    </div>
</div>

<p><br id="w8s85" />Тепер, функція <a rel="nofollow" href="http://ua2.php.net/manual/ru/function.mysql-connect.php">mysql_connect</a> може працювати без параметрів, адже вони будуть взяті автоматично з налаштувань. Єдине, що потрібно буде встановити - це назва бази. Але не слід використовувати цей метод, якщо ваша версія PHP &lt; 4.3.5. У цих старих версіях був глюк, який дозволяв протікати налаштуванням PHP від одного віртуального хоста до іншого, і таким чином, користувачі інших хостів могли побачити чужі налаштування.<br id="wehb0" /><br id="uk3q0" /></p>
<h3 id="jjdl0">Вказівки до швидкодії</h3>
<p>На останок хочу надати кілька вказівок, які можуть <em>поліпшити швидкодію</em> ваших веб-проектів. Це має безпосереднє відношення і до безпеки, адже повільні запити можуть призвести до інших типів атак, як це було показано із оператором LIKE. Ось кілька простих правил, які слід пам&#8217;ятати:<br id="luws0" /></p>
<ol id="luws1">
<li id="luws2"><strong>Отримуйте лише ті дані, які вам необхідні</strong>. Часто розробники використовують символ &#8220;*&#8221; у запитах для того, щоб дістати всі колонки результату, що може бути величезною кількістю даних, особливо, якщо запит складається з багатьох приєднаних таблиць. Це означає більше використання пам&#8217;яті для сортування у БД, більше часу для передачі до PHP, більше часу і пам&#8217;яті для обробки даних. <br id="vj4k0" /></li>
<li id="luws2">Спробуйте використовувати <strong>небуферизовані запити</strong>, які отримують дані невеличкими порціями. Але їх треба використовувати обережно, адже одночасно ви можете працювати лише із одним запитом. У випадку MySQL, ви не зможете навіть виконати INSERT чи UPDATE, доки всі дані поточного запиту не будуть отримані.</li>
<li id="luws2">Створення з&#8217;єднання із БД - повільна операція, особливо у випадку &#8220;тяжких&#8221; систем, як Oracle, PostgreSQL чи MSSQL. Пришвидшити це можна використовуючи <strong>постійне з&#8217;єднання</strong>, яке дозволяє ресурсу залишатись дійсним навіть після припинення роботи скрипта, що дозволяє використовувати одне і те саме з&#8217;єднання для багатьох процесів веб-сервера. Для цього існує функція <a rel="nofollow" href="http://ua2.php.net/manual/ru/function.mysql-pconnect.php">mysql_pconnect</a> (у випадку MySQL, інші БД мають схожі функції). Але це має свої мінуси. Пул з&#8217;єднань у PHP працює на основі процесів, а не веб-серверів, тому кожен процес веб-сервера <em>має свій пул з&#8217;єднань</em>. Це означає, що 50 запущених процесів Apache призведуть до 50 відкритих з&#8217;єднань із БД. Якщо БД не налаштована на те, щоб приймати одночасно стільки з&#8217;єднань, то зайві будуть відкинуті, що призведе до збою вашої програми.</li>
<li id="luws2">Часто веб-сервер та сервер БД працюють на одній і тій самій машині, що дозволяє оптимізувати передачу даних. Навіщо використовувати повільний і громіздкий TCP/IP, коли ви можете використати <strong>Unix Domain Sockets</strong> (UDG) - <em>найвшидший</em> засіб передачі даних після Inter Process Communication (IPC). Цим ви можете значно пришвидшити зв&#8217;язок між двома серверами. Для цього потрібно змінити хост у налаштуваннях з&#8217;єднання: &lt;?php mysql_connect(&#8217;:/tmp/mysql.sock&#8217;, &#8216;login&#8217;, &#8216;password&#8217;); ?&gt;</li>
<li id="luws2">Використовуйте <strong>кешування запитів</strong>. Це збереже результат запиту на деякий час, продовж якого можна не повторювати запит, а натомість - брати готові дані із кешу. Після того, як кеш стане недійсним - повторити запит і зберегти у кеш.</li>
</ol>
<p><br id="ycep0" />Сподіваюсь, стаття була вам цікава і допоможе зробити вашу роботу із базами даних швидшою і безпечнішою.
</pre>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/php/sql-injection-securing-your-database-2/feed</wfw:commentRss>
		</item>
		<item>
		<title>Firebug та Firefox 3 beta 5</title>
		<link>http://webdeveloping.com.ua/firefox/firebug-and-firefox-3-beta-5</link>
		<comments>http://webdeveloping.com.ua/firefox/firebug-and-firefox-3-beta-5#comments</comments>
		<pubDate>Mon, 05 May 2008 10:14:45 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[firefox]]></category>

		<category><![CDATA[beta]]></category>

		<category><![CDATA[debug]]></category>

		<category><![CDATA[firebug]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=28</guid>
		<description><![CDATA[Нова версія популярного браузера Firefox 3 є надзвичайно швидкою та приносить багато змін і виправлень (як наприклад, часті витоки пам&#8217;яті). Нажаль, поки що із новою версією працюють далеко не всі розширення - це і не дивно, адже Firefox 3 досі має статус beta. У цій замітці зайде мова про встановлення популярного серед веб-розробників додатку Firebug. [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignnone size-medium wp-image-29" style="float:left;" title="firebug" src="http://webdeveloping.com.ua/wp-content/uploads/2008/05/firebug.png" alt="" width="152" height="154" />Нова версія популярного браузера <a title="Завантажити Firefox 3" href="http://www.mozilla.com/en-US/firefox/all-beta.html">Firefox 3</a> є надзвичайно швидкою та приносить багато змін і виправлень (як наприклад, <em>часті витоки пам&#8217;яті</em>). Нажаль, поки що із новою версією працюють далеко не всі розширення - це і не дивно, адже Firefox 3 досі має статус <strong>beta</strong>. У цій замітці зайде мова про встановлення популярного серед веб-розробників додатку <a title="Додаток Firebug до Firefox" href="http://www.getfirebug.com">Firebug</a>. Цей додаток дозволяє <em>на ходу </em>змінювати параметри CSS, вносити зміни у DOM документа, відлагоджувати JavaScript та спостерігати за часом завантаження веб-сторінки та її складових. Незамінна річ для тих, хто займається розробкою із використанням AJAX. Сергій Винниченко написав <a title="Огляд додатку Firebug" href="http://www.sergiy.vynnychenko.com/blog/?p=20">невеликий огляд Firebug</a> у своєму блозі.</p>
<p>Отже, до 5ої бети браузера можна було використовувати версію Firebug 1.1beta. Але, не всім відомо, що існує також версія 1.2alpha. Так, судячи із статусу, вона ще має достатньо проблем, але є цілком придатною до використання із новим браузером. Також вона вводить ряд нових можливостей, серед них:</p>
<ol>
<li>Програмне вимкнення журналу Firebug через JavaScript</li>
<li>Відображення HTTP статуса у вкладці Net (мережа)</li>
<li>Нова можливість відлагоджування JavaScript: перервати виконання через один брейпоінт.</li>
<li>Вдосконалений монітор заванаження мережі з урахуванням часу оброки запиту на стороні сервера</li>
<li>Новий API консолі</li>
</ol>
<p>Firebug 1.2 можна скачати <a href="http://getfirebug.com/releases/firebug/1.2/">звідси</a> - просто оберіть останній за номером реліз. Або можна взяти свіжий код з <a href="http://code.google.com/p/fbug/source/browse/branches/firebug1.2/">SVN</a>. Приємного використання!</p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/firefox/firebug-and-firefox-3-beta-5/feed</wfw:commentRss>
		</item>
		<item>
		<title>Експерименти із Kubuntu 8.04 Hardy Heron</title>
		<link>http://webdeveloping.com.ua/linux/kubuntu-hardy-heron-experiments</link>
		<comments>http://webdeveloping.com.ua/linux/kubuntu-hardy-heron-experiments#comments</comments>
		<pubDate>Mon, 21 Apr 2008 15:03:12 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[linux]]></category>

		<category><![CDATA[adept]]></category>

		<category><![CDATA[firefox]]></category>

		<category><![CDATA[kde]]></category>

		<category><![CDATA[kubuntu]]></category>

		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=22</guid>
		<description><![CDATA[Kubuntu Linux є одним із дистрибутивів родини Ubuntu. Його основною відмінністю є орієнтованість на графічне середовище KDE, замість GNOME (на який орієнтовано Ubuntu). Я користуюсь Kubuntu Linux вже досить давно (більше року) і вважаю його одним із найкращих дистрибутивів Linux, користувачем котрого я є вже більше 5 років. Офіційний випуск нової версії 8.04 з кодовою [...]]]></description>
			<content:encoded><![CDATA[<div id="ggww" style="text-align: left;"><a title="Kubuntu Linux" href="http://www.kubuntu.org/">Kubuntu</a> Linux є одним із дистрибутивів родини <a title="Ubuntu Linux" href="http://ubuntu.com/">Ubuntu</a>. Його основною відмінністю є орієнтованість на графічне середовище <a title="Про середовище KDE4" href="http://ru.wikipedia.org/wiki/KDE"><strong>KDE</strong></a>, замість <a title="Про середовище GNOME" href="http://ru.wikipedia.org/wiki/GNOME">GNOME</a> (на який орієнтовано Ubuntu). Я користуюсь Kubuntu Linux вже досить давно (більше року) і вважаю його одним із найкращих дистрибутивів Linux, користувачем котрого я є вже більше 5 років. Офіційний випуск нової версії 8.04 з кодовою назвою <em>Hardy Heron</em> (у перекладі &#8220;стійка чапля&#8221;) назначений на 24 квітня. Але я не витримав чекати ще кілька днів і вирішив спробувати <em>release candidate</em> <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Отже, у цій замітці буде мій досвід з встановлення і користування новою версією.</div>
<p><span id="more-22"></span></p>
<h2 id="mwra">Що нового?</h2>
<p><strong>Kubuntu</strong>:</p>
<ul id="oqn1">
<li id="iof:">KDE 4.0.3 (на вибір)</li>
<li id="i9td">KDE 3.5.9</li>
<li id="p_h4">Ефекти стільниці для KDE 3.5.9<br />
<h2 id="mwra"><a href="http://webdeveloping.com.ua/wp-content/uploads/2008/04/desktopeffects.png"><img class="alignright size-thumbnail wp-image-25" style="float: right;" title="desktopeffects" src="http://webdeveloping.com.ua/wp-content/uploads/2008/04/desktopeffects-150x150.png" alt="Налаштування ефектів стільниці" width="150" height="150" /></a></h2>
</li>
<li>Amarok 1.4.9.1</li>
<li>Автоматичне встановлення кодеків (в тому числи DVD) при відкритті відео за допомогою Kaffeine</li>
<li>Налашування шифрування файлової системи при встановленні</li>
<li> Монтування NTFS-розділів користувачем</li>
<li>Безпечне налаштування X-сервера та дисплея збереже від несправного налаштування</li>
<li>Відображення споживання електроенергії при використанні акамулятора</li>
</ul>
<p><br id="pc7u" />Загальне для <strong>Ubuntu</strong>:<br id="ygs1" /></p>
<ul id="lv7s">
<li>Ядро Linux 2.6.24</li>
<li>Xorg 7.3</li>
<li>Firefox 3 beta 5</li>
<li>OpenOffice 2.4<br id="ruqo" /></li>
<li>ufw - простий у налаштуванні фаєрвол</li>
<li>Inkscape 0.46</li>
<li>Інтеграція у ActiveDirectory через Likewise Open</li>
<li>Підтримка iSCSI</li>
<li>Покращений захист пам&#8217;яті</li>
<li>Підтримка SELinux</li>
<li>Віртуалізація через KVM</li>
<li>Інтеграція драйверів бездротових пристроїв серії Rt2&#215;00<br id="tptz" /></li>
<li>Wubi - інсталяція Linux у Windows з використанням завантажувача останньої</li>
</ul>
<p><br id="f7ef" /></p>
<h2 id="x7_b">Встановлення</h2>
<p>Оскільки у мене вже стояв Kubuntu 7.10 я спробував оновитись через мережу. Для цього необхідно виконати наступну команду (у консолі чи через Alt-F2):<br id="nxli" /></p>
<pre style="font-size:120%;">kdesu "adept_manager --dist-upgrade-devel"</pre>
<p>Звичайно, у фінальній версії <em>не потрібно</em> буде цього робити, адже Adept <em>сам</em> запропонує вам оновити дистрибутив, як тільки вийде реліз. Але перед тим як продовжувати зауважте, якщо ви використовували <strong>PPA-репозиторій</strong> для KDE4, то вам треба буде його вимкнути та <em>видалити все</em>, що відноситься до KDE4.</p>
<p><a href="http://webdeveloping.com.ua/wp-content/uploads/2008/04/adept.jpeg"><img class="alignleft size-thumbnail wp-image-27" style="float: left;" title="Adept" src="http://webdeveloping.com.ua/wp-content/uploads/2008/04/adept-150x150.jpg" alt="Менеджер пакунків Adept" width="150" height="150" /></a>Далі, переконайтесь, що у вас встановлені <em>всі оновлення</em> до поточної версії, якщо ні, то самий час це зробити. Також, переконайтесь, що у вас встановлений один із наступних мета-пакунків: kubuntu-desktop, ubuntu-desktop, edubuntu-desktop, edubuntu-desktop-kde, xubuntu-desktop. Інакше, оновлювач <em>не зможе</em> визначити начинку дистрибутиву.</p>
<p>Отже, у меню Adept має з&#8217;явитись додаткова кнопка для оновлення дистрибутиву. Після її натиснення все має пройти гладко (принаймні у мене все було OK). Можете піти погуляти кілька годинок (в залежності від швидкості вашого з&#8217;єднання із Мережею), адже будуть завантажені пакунки загальним розміром приблизно у 800-900Мб. Якщо трафік вам дорогий, то краще подумайте про альтернативу - диск Kubuntu 8.04 Alternative для оновлення з нього.</p>
<p>Якщо ви не маєте встановленого Kubuntu Linux, то використайте <a title="Сторінка з посиланнями на встановлювачі Kubuntu 8.04 RC" href="https://wiki.kubuntu.org/HardyHeron/RC/Kubuntu#head-7278e1b8b1ee7a1bfff91a03418e4ee371ddbcdb">диск для встановлення</a> - процедура буде простою та інтуїтивною. В разі, якщо хочете використовувати новий (але поки що не дуже стабільний) KDE4 - вам потрібен диск <em>Kubuntu 8.04 KDE4 Remix</em>.</p>
<p>Отже, якщо все пройшло добре і ви оновили свою ОС, то після перезавантаження у Adept побачите новий мета-пакунок <strong>kubuntu-kde4-desktop</strong>. Саме його необхідно встановити, якщо вам цікавий <em>новий</em> KDE. Це одним махом встановить <em>всі</em> необхідні пакунки для базової функціональності нового графічного середовища. Всі інші ви можете знайти та встановити самотужки, набравши у полі пошуку &#8220;kde4&#8243;.<br id="n2oy" /><br id="osfl" /></p>
<h2 id="g8su">Огляд</h2>
<p>В плані графічного інтерфейсу нічого нового, окрім дещо змінених тем я не помітив. Тут слід зазначити, що всі налаштування повністю зберіглись і запрацювало все без проблем. З позитивного, у KDE4 перестали пропадати деякі іконки з системного лотка (system tray) <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Але, що дійсно сподобалось, то це нова <strong>бета Firefox 3</strong>. Вона тут встановлюється <em>автоматично</em>, тобто є типово обраним браузером. Не дивлячись на статус &#8220;<em>бета</em>&#8220;, працює досить стабільно. А головне, значно, значно, швидше та використовуючи менше пам&#8217;яті ніж 2га версія - я думаю, тепер є ще один аргумент на користь цього браузера. З мінусів - поки що підтримуються не всі розширення. <br id="lcue" />Серед моїх <strong>працюють</strong>:<br id="na2n" /></p>
<ul id="q6j9">
<li id="w5c0">ColorZilla</li>
<li id="h4v4">Download Statusbar</li>
<li id="bu_6">DownThemAll!</li>
<li id="bu_6">Live HTTP Headers</li>
<li id="bu_6">Operator</li>
<li id="bu_6">Web Developer</li>
</ul>
<p>А <strong>не працюють</strong> дуже корисні:<br id="es10" /></p>
<ul id="z48i">
<li id="b.lm">Fasterfox</li>
<li id="hbxu">Firebug</li>
<li id="qtb-">Google Toolbar</li>
<li id="tpcf">HTML Validator</li>
<li id="lacx">Locationbar</li>
</ul>
<p>Сподіваємось, що скоро вийдуть оновлення для цих розширень. А поки, є можливість використовувати поруч 2гу версію Firefox. У Adept ці пакунки називаються відповідно <em>firefox-3.0</em> та <em>firefox-2</em>.<a href="http://webdeveloping.com.ua/wp-content/uploads/2008/04/amarok.png"><img class="alignright size-thumbnail wp-image-24" style="float: right;" title="amarok" src="http://webdeveloping.com.ua/wp-content/uploads/2008/04/amarok-150x150.png" alt="Програвач звуку Amarok" width="150" height="150" /></a><br id="jmkg" />Окрім Вогнелиса, корисна для поціновувачів продукції Apple фіча у новому програвачі <a title="Вільний програвач Amarok" href="http://amarok.kde.org/"><strong>Amarok</strong></a> - тепер є підтримка iPhone та iPod Touch плюс оновлений сервіс отримання картинок альбомів з Amazon. А користувачі KDE3 тепер без зайвих танців із бубном можуть вмикати <strong>ефекти стільниці</strong> через спеціальну секцію у системних налаштуваннях. <strong>OpenOffice</strong> тепер може порадувати підтримкою баз даних у форматі MS Access.</p>
<p>Звичайно, дуже багато нових речей всередині ОС. Більшість оновлень стосуються <strong>стабільн</strong><strong>ості та безпеки</strong> системи - це і не дивно, адже цей реліз Ubuntu (не Kubuntu) є <em>LTS</em>, тобто має довгий термін офіційної підтримки (3 роки для desktop-версії та 5 років для серверної) і цим розрахований на корпоративних користувачів. Хоча Kubuntu це і не стосується (через нестабільність KDE4), але ці оновлення звичайно ж присутні. <br id="y9r-" /><br id="lokq" />У висновок скажу, що чергове оновлення приносить велику кількість внутрішніх змін (не надто помітних користувачу), нові версії софта і підтримку KDE4 з основних репозиторіїв. Вже зараз встановлення проходить гладко і без проблем. Тому, не бачу змісту не оновитись, коли вийде реліз <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<div style="text-align:center;"><a href="http://webdeveloping.com.ua/wp-content/uploads/2008/04/mydesktop.jpg"><img class="size-thumbnail wp-image-26" style="float:none;" title="Моя стільниця" src="http://webdeveloping.com.ua/wp-content/uploads/2008/04/mydesktop-150x150.jpg" alt="Моя стільниця" width="150" height="150" /></a></div>
<p><strong>UPD: </strong>Якщо ви маєте проблеми із звуком у KDE4 - спробуйте перевстановити всі пакунки, які відносяться до звуку (alsa, phonon, kmix і т.п.), після чого у &#8220;Системних параметрах&#8221; -&gt; &#8220;Звук&#8221; видаліть ваш звуковий пристрій. Далі перейдіть до вкладки &#8220;Серверна програма&#8221; і позначте пункт &#8220;Показати пристрої OSS&#8221;, натисність &#8220;Застосувати&#8221;. Вийдіть з графічного середовища і увійдіть у нього знов. Тепер звук має з&#8217;явитись. Але будуть також з&#8217;являтись повідомлення про перехід до іншого пристрою при кожній спробі використати звук. Для того, щоб позбавитись цього пересуньте ваші пристрої догори у системних параметрах. Поекспериментуйте, який саме пристрій підходить.</p>
<p>Сподіваємось, що до релізу це виправлять.</p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/linux/kubuntu-hardy-heron-experiments/feed</wfw:commentRss>
		</item>
		<item>
		<title>Обробка зовнішніх даних. Частина ІІ: Валідація</title>
		<link>http://webdeveloping.com.ua/php/php-user-input-data-validation</link>
		<comments>http://webdeveloping.com.ua/php/php-user-input-data-validation#comments</comments>
		<pubDate>Fri, 18 Apr 2008 01:18:04 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[безпека]]></category>

		<category><![CDATA[pcre]]></category>

		<category><![CDATA[валідація]]></category>

		<category><![CDATA[зовнішні дані]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=21</guid>
		<description><![CDATA[У попередньому записі я розповідав про джерела вводу і їх убезпечення. У цій статті я хочу показати як можна перевірити на дійсність дані, що вже потрапили у скрипт. Якщо ви не будете перевіряти дані, то рівень безпеки ані трохи не підвищиться, навіть, якщо ви зробили сам ввід даних більш безпечним.У минулій статті було згадано, що [...]]]></description>
			<content:encoded><![CDATA[<p>У <a title="Обробка зовнішніх даних. Частина І: Ввід" href="http://webdeveloping.com.ua/php/php-user-input-data-processing">попередньому записі</a> я розповідав про джерела вводу і їх убезпечення. У цій статті я хочу показати як можна <strong>перевірити на дійсність</strong> дані, що вже потрапили у скрипт. Якщо ви не будете перевіряти дані, то рівень безпеки ані трохи <em>не підвищиться</em>, навіть, якщо ви зробили сам ввід даних більш безпечним.<br id="n53a" />У минулій статті було згадано, що всі дані приходять як строки чи масиви строк, але у різних ситуаціях ми можемо очікувати різні види даних, наприклад, числа  чи які-небудь шаблонні дані (e-mail&#8217;и, URL&#8217;и, телефонні номери і т.п.). Тож розглянемо ці види даних і методи їх перевірки.<span id="more-21"></span></p>
<h3 id="k56-">Чисельні дані</h3>
<p>Найшвидший і найпростіший метод валідації чисельних даних - <strong>приведення типу</strong> до необхідного чисельного типу (<em>int</em>, <em>float):</em></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2170">
        <div class="code" id="p21code70">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$_GET</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="br0">&#40;</span>int<span class="br0">&#41;</span><span class="re1">$_GET</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span><span class="sy0">;</span>
    <span class="re1">$_POST</span><span class="br0">&#91;</span><span class="st0">'price'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="br0">&#40;</span>float<span class="br0">&#41;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="st0">'price'</span><span class="br0">&#93;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="ojl3" /> Приведення типу переводить чисельні дані у строках - це дає впевненість, що у скрипт потраплять <strong>лише чисельні дані</strong>, незалежно від того, що було введено. Якщо введена строка містить лише нечисельні дані, то приведення поверне число 0. У більшості випадків це буде <em>небажаним</em>, тож проста умова після приведення типу дасть змогу ефективно перевірити дані, і у разі необхідності видати помилку:<br id="p5n7" /> <br id="r7m:" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2171">
        <div class="code" id="p21code71">
            <pre class="php"><span class="kw2">&lt;?php</span>
   <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re1">$value</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
       <span class="co1">//Помилка</span>
   <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Пам&#8217;ятайте, що при можливості введення занадто великого числа, краще приводити до типу із <strong>плаваючою точкою</strong> (float). Наприклад, на 32-бітних системах розмір цілочисленого типу (int) сягає 4 байт, окрім того він є знаковим, тобто <em>максимальне</em> число для змінної такого типу - 2 147 483 647 ((2^4^8)/2). Якщо більше число буде введено, то станеться переповнення і <em>втрата даних</em> як наслідок. Приведення до типу із плаваючою точкою збереже дані у форматі &#8220;знак * 2^експонента * мантиса&#8221;, таким чином ті ж 4 байт дозволяють зберігати <em>значно більші</em> числа.</p>
<p>Щоправда, таким чином можна пререводити лише десяткові числа, а наприклад, шістнадцяткові та вісімкові вже - ні. Для цього існує більш гнучка функція PHP <a href="http://ua.php.net/manual/ru/function.is-numeric.php" rel="nofollow">is_numeric</a>. Вона приймає <em>всі</em> формати даних і повертає булеве true, в разі, якщо їй передані чисельні дані та false у іншому випадку. Ви спитаєте, чому б посто завжди не використовувати цю функцію? За все треба платити - вона працює <em>дещо повільніше</em> ніж просте приведення типів і <em>не зовсім вірно</em> перевіряє вісімкові числа, адже вважає допустимими всі цифри у проміжку [0-9], що не дійсно для вісімкових чисел. Приклад:<br id="i:nv" /> <br id="mmx1" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2172">
        <div class="code" id="p21code72">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">is_numeric</span><span class="br0">&#40;</span><span class="st0">'9234'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//true</span>
    <span class="kw3">is_numeric</span><span class="br0">&#40;</span><span class="st0">'0xFF7F'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//true</span>
    <span class="kw3">is_numeric</span><span class="br0">&#40;</span><span class="st0">'0xYR3F'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//false</span>
    <span class="kw3">is_numeric</span><span class="br0">&#40;</span><span class="st0">'0999'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//true</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Ще однією проблемою у валідації чисельних даних є <strong>різниця у локалях</strong>. Справа у тому, що числа із плаваючою точкою вважаються дійсними <em>тільки</em>, якщо цілу та дробову частину розділяє знак &#8220;.&#8221; (точка). Це складає проблему для багатьох європейських локалей - наприклад, у французькій, німецькій та й українській локалях прийнято використовувати кому як розподільний знак. І <em>ніяка</em> насильна зміна локалі у цьому не допоможе. У PHP можливе <em>лише тільки</em> використання точки.</p>
<p>Тепер трохи про парадигму використання цієї перевірки. Приклад перший:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2173">
        <div class="code" id="p21code73">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="co1">//$_GET['id'] = '1; /*Бугагагага!*/ TRUNCATE users;'</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>int<span class="br0">&#41;</span><span class="re1">$_GET</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st0">&quot;DELETE FROM users WHERE id=&quot;</span><span class="sy0">.</span><span class="re1">$_GET</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Проблему бачите? <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Так, умова із приведенням типу буде успішно пройдена і поверне &#8220;1&#8243;, а потім запитом до БД буде видалений користувач із id=1, а разом із ним і <em>вся</em> таблиця &#8220;users&#8221;. Це окремий тип уразливості - <strong>ін&#8217;єкція SQL коду</strong>, який буде розглянутий більш детально у одній із наступних заміток. А тепер розглянемо <em>правильне</em> використання приведення типу:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2174">
        <div class="code" id="p21code74">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="re1">$_GET</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="br0">&#40;</span>int<span class="br0">&#41;</span><span class="re1">$_GET</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st0">&quot;DELETE FROM users WHERE id=&quot;</span><span class="sy0">.</span><span class="re1">$_GET</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Тепер все добре, у запит буде вставлено лише чисельне значення.<br id="rsuw" /></p>
<h3 id="vmp9">Строкові дані</h3>
<p>Із строковими даними не все так просто як із чисельними. Їх валідація дуже залежить від того, які власне дані передаються: URL, e-mail, ім&#8217;я, телефон і т.п.</p>
<p>Найпростійший і найшвидший шлях перевірити строкові дані у PHP - <strong>використати розширення ctype</strong>(воно типово увімкнене). Наприклад для перевірки імені користувача можна задіяти <a href="http://ua.php.net/manual/ru/function.ctype-alpha.php" rel="nofollow"> ctype_alpha</a>. Ця функція поверне true, коли <em>всі</em> символи у переданій їй строці є літерами, <em>незалежно</em> від регістру. Якщо допускаються як літери так і цифри, то слід використовувати <a href="http://ua.php.net/manual/ru/function.ctype-alnum.php" rel="nofollow">ctype_alnum</a>, щоправда дійсними будуть лише <em>цілі числа</em>. Приклади:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2175">
        <div class="code" id="p21code75">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">ctype_alpha</span><span class="br0">&#40;</span><span class="st0">'teststring'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//true</span>
    <span class="kw3">ctype_alpha</span><span class="br0">&#40;</span><span class="st0">'teststring12'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//false</span>
    <span class="kw3">ctype_alnum</span><span class="br0">&#40;</span><span class="st0">'teststring12'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//true</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Функції розширення ctype є залежними від <strong>встановленої локалі</strong>. Якщо строка містить літери, які не належать даній локалі - вона вважається <em>недійсною</em>. Тож, що працювати із необхідною вам мовою потрібно перескнути локаль за домогою функції <a href="http://ua.php.net/manual/ru/function.setlocale.php" rel="nofollow">setlocale</a>. До того ж, ці функції працюють лише із <strong>однобайтовими символами</strong>, тож можете забути про перевірку строк із багатобайтовими символами (наприклад, не-латинські літери закодовані UTF-8, японська і т.п.). Також ця функція вважає не дійсними символ &#8220;-&#8221; та всі види пробілів.</p>
<p>Щоб перекрити усі інші випадки треба використовувати <strong>регулярні вирази</strong>. У цій статті ми розглянемо <em>регулярні вирази сумісні із Perl</em> (PCRE), оскількі вони швидші, розуміють багатобайтові символи (без спеціальних розширень як <em>mbstring</em>), безпечніші та гнучкіші за розширення <em>ereg</em>. Перевірка строк за допомогою PCRE здійснюється через функцію <a href="http://ua.php.net/manual/ru/function.preg-match.php" rel="nofollow">preg_match</a>. При сходженні заданих шаблону та строки, функція повертає <em>int(1)</em> та <em>int(0)</em> у зворотньому випадку. Якщо на вході очікується строка із латинськими літерами, знаками &#8220;-&#8221;, &#8220;&#8216;&#8221; та пробілами можна використати наступний вираз:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2176">
        <div class="code" id="p21code76">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">preg_match</span><span class="br0">&#40;</span><span class="st0">&quot;/[^-'a-zA-Z <span class="es0">\t</span><span class="es0">\n</span>]/&quot;</span><span class="sy0">,</span> <span class="st0">&quot;it's a phrase that vali-dates&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//int(0)</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Квантор &#8220;^&#8221; на початку виразу означає &#8220;<em>не входить</em>&#8220;. У фразі немає символів, які б не входили у вираз, отже повернено 0.</p>
<p>Також PCRE можна використовувати разом із setlocale. У деяких випадках вводити вручну символи для перевірки досить проблематично (наприклад, ієрогліфи). Для цього існує ідентифікатор &#8220;/w&#8221;, який означає алфавіт, складений із літер поточної локалі. Також PCRE <em>повністю</em> підтримує UTF-8, тож можна використовувати Unicode коди у виразах із оператором &#8220;u&#8221;.<br id="qaim" /> <br id="ws11" /></p>
<h3 id="ywyc">Розмір даних</h3>
<p>Для безпечної і правильної роботи скрипта також часто є сенс перевіряти <strong>розмір даних</strong>, що надходять ззовні. Наприклад, у базі даних є поле для імені користувача і воно має тип VARCHAR(20). Що станеться, якщо користувач введе ім&#8217;я довжиною 25 символів? А це залежить від самої бази даних: якщо це <em>PostgreSQL</em>, то буде видана помилка, якщо ж <em>MySQL</em>, то дані запишуться у БД, але будуть автоматично обрізані до необхідної довжини. Не знаю навіть, що гірше. Отже, нам слід <em>відстежувати</em> такі випадки і попереджати користувача про помилки.</p>
<p>Для цього існує два підходи: обмежування на клієнтській стороні (у браузері) та серверній. Найкраще застосовувати обидва. Спочатку розглянемо клієнтську сторону.</p>
<p>Текстові поля у HTML-формах можна обмежувати у розмірі за допомогою атрибута &#8220;<em>maxlength</em>&#8220;, таким чином браузер самотужки попереджає ввід більшої за цей атрибут кількості символів:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2177">
        <div class="code" id="p21code77">
            <pre class="html">    <span class="sc2"><span class="kw2">&lt;input</span> <span class="kw3">type</span><span class="sy0">=</span><span class="st0">&quot;text&quot;</span> <span class="kw3">name</span><span class="sy0">=</span><span class="st0">&quot;uname&quot;</span> <span class="kw3">maxlength</span><span class="sy0">=</span><span class="st0">&quot;20&quot;</span> <span class="sy0">/</span><span class="kw2">&gt;</span></span></pre>
        </div>
    </div>
</div>

<p>Нажаль, <em>maxlength</em> можна задіяти лише для полів типу &#8220;<em>text</em>&#8221; та &#8220;<em>password</em>&#8220;. Для &#8220;<em>textarea</em>&#8221; наприклад, вже потрібно відстежувати це за допомогою <em>JavaScript</em>.</p>
<p>Але це ні в якому разі <strong>не є панацеєю</strong>. Проблема у тому, що зловмисник може просто скопіювати вашу форму і змінити атрибути чи надіслати відповідний POST-запит до вашого скрипта, який оброблює ці дані. Найпростіше, що можна зробити для того, щоб запобігти цьому - створити масив з необхідними вам обмеженнями і перевіряти їх при вводі у скрипті.</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2178">
        <div class="code" id="p21code78">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$validate</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span>
        <span class="st0">'uname'</span> <span class="sy0">=&gt;</span> <span class="nu0">20</span><span class="sy0">,</span>
        <span class="st0">'address'</span> <span class="sy0">=&gt;</span> <span class="nu0">200</span><span class="sy0">,</span>
        <span class="coMULTI">/* ... */</span>
    <span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re1">$validate</span> <span class="kw1">as</span> <span class="re1">$input</span> <span class="sy0">=&gt;</span> <span class="re1">$rule</span><span class="br0">&#41;</span>
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">empty</span><span class="br0">&#40;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="re1">$input</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw3">strlen</span><span class="br0">&#40;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="re1">$input</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="sy0">&gt;</span> <span class="re1">$rule</span><span class="br0">&#41;</span>
            <span class="kw3">exit</span><span class="br0">&#40;</span><span class="st0">'Поле '</span><span class="sy0">.</span><span class="re1">$input</span><span class="sy0">.</span><span class="st0">' більше за дозволену довжину '</span><span class="sy0">.</span><span class="re1">$rule</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Таким чином, для кожного визначеного поля перевіряється його довжина за допомогою функції <a href="http://ua.php.net/manual/ru/function.strlen.php" rel="nofollow">strlen</a>. Якщо довжина <em>перевищує</em> задане для цього поля значення - користувач отримує помилку. Сама перевірка дуже швидка, адже strlen не буде щоразу підраховувати довжину строки, натомість ця функція бере вже визначене значення із спеціальної внутрішньої структури PHP. Незважаючи на це, ми все-одно маємо виклик функції, який можна дещо оптимізувати. Окрім того слід пам&#8217;ятати, що для використання із <strong>багатобайтовими кодуваннями</strong> символів треба використовувати функцію <a href="http://ua.php.net/manual/ru/function.mb-strlen.php" rel="nofollow">mb_strlen</a> із вказанням кодування, якщо увімкнене розширення <em>mbstring</em>. Якщо ж цього розширення нема - то такий спосіб не підійде, адже наприклад, 2-байтові кодування будуть підраховуватись відповідно за по 2 байти і довжина строки буде у 2 рази більшою.</p>
<p>Починаючи із <em>PHP 4.3.10</em>, можна використати одну маловідому можливість вбудованої конструкції <a href="http://ua.php.net/manual/ru/function.isset.php" rel="nofollow">isset</a>. Вона може перевіряти наявність даних <strong>за зсувом</strong> (offset). Тож ми можемо це застосувати, перевіряючи, чи є щось за зсувом {$rule + 1}:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2179">
        <div class="code" id="p21code79">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$validate</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span>
        <span class="st0">'uname'</span> <span class="sy0">=&gt;</span> <span class="nu0">20</span><span class="sy0">,</span>
        <span class="st0">'address'</span> <span class="sy0">=&gt;</span> <span class="nu0">200</span><span class="sy0">,</span>
        <span class="coMULTI">/* ... */</span>
    <span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re1">$validate</span> <span class="kw1">as</span> <span class="re1">$input</span> <span class="sy0">=&gt;</span> <span class="re1">$rule</span><span class="br0">&#41;</span>
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">empty</span><span class="br0">&#40;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="re1">$input</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw3">isset</span><span class="br0">&#40;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="re1">$input</span><span class="br0">&#93;</span><span class="br0">&#123;</span><span class="re1">$rule</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="br0">&#125;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
            <span class="kw3">exit</span><span class="br0">&#40;</span><span class="st0">'Поле '</span><span class="sy0">.</span><span class="re1">$input</span><span class="sy0">.</span><span class="st0">' більше за дозволену довжину '</span><span class="sy0">.</span><span class="re1">$rule</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Оскільки isset не є функцією, а натомість <em>конструкцією</em> мови, PHP інтерпретує це у <em>одну</em> інструкцію, а вона буде виконано просто миттєво. Мінус - ні в якому разі <em>не підходить</em> для багатобайтових кодувань строк.<br id="icu1" /></p>
<h3 id="e-52">Білий список</h3>
<p>Велика кількість розробників занадто сильно покладається на одне <strong>вкрай невірне припущення</strong> - виринаючі списки, чекбокси, кнопки вибору та приховані поля не потребують перевірки, думаючи, що вони все-одно мають лише попередньо визначені варанти даних. Насправді ж, зловмисник може <em>легко</em> передати будь-які інші дані, скопіювавши форму або створивши свій запит до вашого скрипта. У першій частині було сказано, що неможна довіряти будь-яким зовнішнім даним. Все потрібно перевіряти.</p>
<p>На щастя, перевірити такі поля <em>дуже легко</em>. Достатньо просто створити масив із значеннями цей полів і перевіряти введені дані на наявність у цьому масиві:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2180">
        <div class="code" id="p21code80">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$jobTypes</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st0">'fulltime'</span><span class="sy0">,</span> <span class="st0">'parttime'</span><span class="sy0">,</span> <span class="st0">'contract'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">empty</span><span class="br0">&#40;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="st0">'jobType'</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="sy0">||</span> <span class="sy0">!</span><span class="kw3">in_array</span><span class="br0">&#40;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="st0">'jobType'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re1">$jobTypes</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
        <span class="kw3">exit</span><span class="br0">&#40;</span><span class="st0">'Обламайся, у нас все гаразд із безпекою :-P '</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p><br id="ifl:" /></p>
<h3 id="e1oh">Завантаження файлів</h3>
<p>Так історично склалось, що одна з найбільших проблем у PHP - <strong>завантаження файлів</strong>. І найкращим вирішенням проблеми є <em>відключити</em> цю можливість у налаштуваннях PHP, якщо це реально у вашому середовищі. Адже можна створити запит, який буде завантажувати велику кількість файлів, чим легко завалити сервер. Якщо ви все ж таки дозволяєте завантаження файлів, то необхідно <em>прийняти міри</em> для того, щоб зменшити ризики.<br id="d5cj" /> <br id="y.sc" /> Налаштування:<br id="gop-" /></p>
<ul id="acgz">
<li id="ioz0">upload_max_size - це потрібно зробити <em>якнайменшим</em>, щоб попередити завантаження надто великих файлів, завантажуючи сервер. Типово, це  налаштування має значення 2Мб. Часто можна зробити його меншим.</li>
<li id="znu3">post_max_size - обмежує розмір POST-запиту. Якщо ваша програма може завантажувати лише один файл за раз, то слід поставити його трохи більшим за upload_max_size. Якщо ж багато, то трохи більше ніж сумарний розмір всіх файлів. Типово, це має немаленьке значенняу 8Мб. В більшості випадків треба <em>зменшити</em>.</li>
<li id="znu3">upload_tmp_dir - директорія для розміщення тимчасових файлів. Типово, PHP використовує системну тимчасову директорію (наприклад, /tmp/ на *nix). Ця директорія може бути прочитана будь-ким та у деяких випадках ще й писати туди може хто-завгодно. Вона доступна для будь-якого користувача та процесу. Зазвичай правильно <em>змінити</em> це налаштування для кожної вашої програми.</li>
</ul>
<p><br id="nhlh" /></p>
<p>Тепер власне про <strong>завантаження файлів</strong>.</p>
<p>Коли приходить запит, який включає у себе файли, то суперглобальний масив <em>$_FILES</em> наповнюється масивом для кожного файлу:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2181">
        <div class="code" id="p21code81">
            <pre class="php">    <span class="co1">//print_r($_FILES);</span>
    <span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span> <span class="sy0">=&gt;</span> <span class="kw3">Array</span>
    <span class="br0">&#123;</span>
        <span class="br0">&#91;</span>name<span class="br0">&#93;</span> <span class="sy0">=&gt;</span> picture<span class="sy0">.</span>jpg <span class="co1">//назва файла</span>
        <span class="br0">&#91;</span>type<span class="br0">&#93;</span> <span class="sy0">=&gt;</span> image<span class="sy0">/</span>jpeg <span class="co1">//MIME-тип</span>
        <span class="br0">&#91;</span>tmp_name<span class="br0">&#93;</span> <span class="sy0">=&gt;</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>phpsdf34ss <span class="co1">//тимчасова назва і місце збереження</span>
        <span class="br0">&#91;</span>error<span class="br0">&#93;</span> <span class="sy0">=&gt;</span> <span class="nu0">0</span> <span class="co1">//код помилки</span>
        <span class="br0">&#91;</span>size<span class="br0">&#93;</span> <span class="sy0">=&gt;</span> <span class="nu0">213951</span> <span class="co1">//розмі</span>
    <span class="br0">&#125;</span></pre>
        </div>
    </div>
</div>

<p>Параметр &#8220;name&#8221; за специфікацією <a href="http://www.w3.org/">W3C</a> має містити лише оригінальну назву файла без директорії. Але, нажаль, не всі браузери дотримуються цієї специфікації (звичайно ж ви здогадались, які саме &#8220;<em>не всі</em>&#8221; браузери маються на увазі <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Так, це <em>Internet Explorer</em>, який <em>порушаючи</em> специфікацію та приватність користувача, надсилає повний шлях до файлу). Такі браузери стають <em>інструментом</em> для зловмисних дій і, якщо не валідувати ввід файлів, то кінця проблем не буде видно. Приклад такого використання:<br id="j-lh" /> <br id="n0m0" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2182">
        <div class="code" id="p21code82">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="co1">//$_FILES['file']['name'] = '../../config.php';</span>
&nbsp;
    <span class="kw3">move_uploaded_file</span><span class="br0">&#40;</span><span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'tmp_name'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="st0">'/home/www/app/uploads/'</span><span class="sy0">.</span><span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'name'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Таким чином, якщо існує файл /home/www/config.php і на нього у веб-сервера є права запису - він буде <em>перезаписаний</em> завантаженим. В принципі, PHP намагається автоматично захистити від таких дірок, обрізаючи все до назви файла, але це не завжди вірно працює. Так, наприклад, у версії для Windows до 4.3.10 роздільники шляху &#8220;\&#8221; могли потрапити у шлях (про це трохи далі). Щоб убезпечити програму від дірок у старих версіях і передбачити нові слід обрізати все непотрібне <em>вручну</em>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2183">
        <div class="code" id="p21code83">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="co1">//$_FILES['file']['name'] = '../../config.php';</span>
&nbsp;
    <span class="kw3">move_uploaded_file</span><span class="br0">&#40;</span><span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'tmp_name'</span><span class="br0">&#93;</span><span class="sy0">,</span>
             <span class="st0">'/home/www/app/uploads/'</span><span class="sy0">.</span><span class="kw3">basename</span><span class="br0">&#40;</span><span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'name'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Функція <a href="http://ua.php.net/manual/ru/function.basename.php" rel="nofollow">basename</a> дозволяє пройти лиш імені файла.<br id="vkkc" /> <br id="te:t" /> Тепер щодо <strong>вмісту файлу</strong>. Пам&#8217;ятайте, що тип файлу, який передається у $_FILES['file']['type'] є таким, яким його вважає <em>браузер</em>. Це абсолютно <strong>ненадійна інформація</strong>, якій ніколи і нізащо неможна довіряти. Вся проблема у тому, що браузер визначає MIME-тип файлу просто тупо, судячи з його <em>розширення</em> у файловій системі, а не з його заголовків (як мав би). Тож, якщо файл <em>bloody_virus.exe</em> буде перейменований у <em>beautiful_flowers.jpg</em>, то браузер <em>не задумуючись</em> відправить MIME-тип <em>image/jpeg</em>.</p>
<p>Для найбільш часто завантажуваних файлів, картинок, існує функція <a href="http://ua.php.net/manual/ru/function.getimagesize.php" rel="nofollow">getimagesize</a>. Вона перевіряє <strong>заголовки файла</strong> та повертає інформацію про зображення, або false, якщо це не картинка. Для більш складних файлів існує розширення PECL <em>fileinfo</em>. Воно може оброблювати майже будь-які формати файлів через функцію <a href="http://ua.php.net/manual/ru/function.finfo-file.php" rel="nofollow">finfo_file</a>. Спочатку треба створити контекст за допомогою функції <a href="http://ua.php.net/manual/ru/function.finfo-open.php" rel="nofollow">finfo_open</a> (якщо передати їй <em>FILEINFO_MIME</em>, то у видачі буде використовуватись MIME-тип замість текстової інформації), далі можна використовувати його скільки завгодно раз для будь-яких файлів.</p>
<p>От ми і підготувались до власне <strong>доступу до завантаженого файлу</strong>. Тут треба запам&#8217;ятати дві функції: <a href="http://ua.php.net/manual/ru/function.move-uploaded-file.php" rel="nofollow">move_uploaded_file</a> та <a href="http://ua.php.net/manual/ru/function.is-uploaded-file.php" rel="nofollow">is_uploaded_file</a>. Перша <em>переміщає</em> завантажений файл із тимчасової директорії у постійну, а друга перевіряє, чи дійсно вказаний файл <em>був завантажений</em>. Обидві функції <em>безпечні</em>, адже перевіряють шлях до файлу через внутрішній хеш завантажених файлів. Коли файл пересунутий у постійне місце за допомогою move_uploaded_file, то його тимчасова назва <em>видаляється</em> із цього хешу, унеможливлюючи таким чином будь-які подальші операції із тимчасовим файлом.</p>
<p>І, нарешті, останнє, що можна сказати про файли - <strong>перевірка розміру</strong>. Цю інформацію можна вважати <em>безпечною</em>. Інформація про розмір файлу передана браузерем <em>завжди</em> збігається із розміром тимчасового файлу, в разі <em>безперешкодного</em> завантаження. Тож, щоб не засмічувати пошкодженими файлами директорію для постійних файлів слід зробити ще одну невеличку перевірку:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2184">
        <div class="code" id="p21code84">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">move_uploaded_file</span><span class="br0">&#40;</span><span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'tmp_name'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="re1">$dest</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
        <span class="kw3">exit</span><span class="br0">&#40;</span><span class="st0">'Ой...'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">filesize</span><span class="br0">&#40;</span><span class="re1">$dest</span><span class="br0">&#41;</span> <span class="sy0">!=</span> <span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'size'</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw3">unlink</span><span class="br0">&#40;</span><span class="re1">$dest</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw3">exit</span><span class="br0">&#40;</span><span class="st0">'Страшний глюк! Рятуйтесь, хто може!!'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Це врятує від збереження файлів, що були передані не повністю, або пошкодженні якимось ворожим процесом, поки файл знаходився у незахищеній тимчасовій директорії.<br id="hefp" /> </p>
<h3 id="r1vt">Чорна магія &#8220;Magic Quotes&#8221;</h3>
<p>Суть механізму PHP <em>magic_quotes_gpc</em> у тому, що ввід користувача може включати у себе різні види спеціальних символів, як лапки (&#8217; та &#8220;), NUL (\0) та зворотній слеш (\). Якщо залишити їх <em>не відфільтрувавши</em>, можна наразитись на небезпеку, передаючи їх у різні функції. Тож розробники PHP вирішили <em>автоматично</em> їх екранувати, фактично здійснюючи функцію <a href="http://ua.php.net/manual/ru/function.addslashes.php" rel="nofollow">addslashes</a> до <em>кожного</em> джерела вводу даних, і, навіть, до <em>назви файлів</em>. З першого погляду, що тут поганого? Розробники турботливо захистили від небезпеки лінивих програмістів (а ми всі такі <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ), давши їм чарівну таблетку. Але не попередили, що вона має деякі побічні ефекти.</p>
<p>Основна проблема &#8220;<em>магічних лапок</em>&#8221; є <strong>бездумна залежність</strong> від них. А факт у тому, що цей механізм може бути вимкнутим через <em>php.ini</em>, <em>httpd.conf</em> та й <em>.htaccess</em>. Тому, якщо ви вважаєте, що <em>magic quotes</em> завжди увімкнено і не екрануєте ввід вручну, то ви самі собі вороги, оскільки залишаєте свою програму на беззастережне ураження великою кількістю експлоітів, як <em>ін&#8217;єкція SQL</em>, <em>ін&#8217;єкція команд</em> операційної системи та багато іншого.</p>
<p>По-друге, <em>не завжди</em> цей механізм продукує вірний вивід. Наприклад при використанні із спеціальними функціями для екранування можна отримати небажаний результат у вигляді <strong>подвійного екранування</strong> (а отже відміненого екранування). І нарешті, все це не надто ефективно. Функція <em>addslashes</em> пожирає вдвічі більше пам&#8217;яті ніж необхідно на збереження переданої строки і для нормалізації має бути реверсовано, поглинаючи вдвічі більше процесорного часу.</p>
<p>Очевидно, <em>вірним</em> рішенням є <strong>вимкнення magic_quotes_gpc</strong> та ручне екранування у необхідній мірі. Але для коректної роботи вашої програми у будь-якому середовищі потрібно <strong>нормалізувати</strong> дані. Ось шматок коду, який це виконує:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2185">
        <div class="code" id="p21code85">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">get_magic_quotes_gpc</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="kw2">function</span> stripQuotes<span class="br0">&#40;</span><span class="sy0">&amp;</span><span class="re1">$val</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">is_array</span><span class="br0">&#40;</span><span class="re1">$val</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
            <span class="kw3">array_walk</span><span class="br0">&#40;</span><span class="re1">$val</span><span class="sy0">,</span> <span class="st0">'stripQuotes'</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="kw1">else</span>
            <span class="re1">$val</span> <span class="sy0">=</span> <span class="kw3">stripslashes</span><span class="br0">&#40;</span><span class="re1">$val</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span>
&nbsp;
        <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="st0">'GET'</span><span class="sy0">,</span> <span class="st0">'POST'</span><span class="sy0">,</span> <span class="st0">'COOKIE'</span><span class="br0">&#41;</span> <span class="kw1">as</span> <span class="re1">$superGlobal</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">empty</span><span class="br0">&#40;</span>$<span class="br0">&#123;</span><span class="st0">'_'</span><span class="sy0">.</span><span class="re1">$superGlobal</span><span class="br0">&#125;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
                <span class="kw3">array_walk</span><span class="br0">&#40;</span>$<span class="br0">&#123;</span><span class="st0">'_'</span><span class="sy0">.</span><span class="re1">$superGlobal</span><span class="br0">&#125;</span><span class="sy0">,</span> <span class="st0">'stripQuotes'</span><span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Цей код <em>реверсує</em> діяльність магічних лапок, якщо вони увімкнені, враховуючи будь-який рівень вкладеності у суперглобальних масивах через рекурсію. Все начебто непогано, але є одна проблема, суть якої є у одній <em>обмеженості</em> PHP. У рекурсивних функціях PHP використовує <strong>системний стек</strong> для відстежування викликів. Але цей стек обмежений у глибині і цілком можливо переповнити стек та цим <em>завалити</em> PHP. У даному прикладі це тривіально зробити:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2186">
        <div class="code" id="p21code86">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$str</span> <span class="sy0">=</span> <span class="kw3">str_repeat</span><span class="br0">&#40;</span><span class="st0">'[]'</span><span class="sy0">,</span> <span class="nu0">1000000</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw3">file_get_contents</span><span class="br0">&#40;</span><span class="st0">'http://vulnerable.site.com/insecure.php?badvar='</span><span class="sy0">.</span><span class="re1">$str</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Ми передаємо GET-запитом масив із мільйонною вкладеністю. Таким чином наш скрипт просто завалиться на рекурсії разом із PHP. Тож маємо придумати більш безпечний засіб. А це просто - ми <em>уникнемо</em> рекурсії, якщо &#8220;розрівняємо&#8221; багатовимірний масив у одновимірний:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2187">
        <div class="code" id="p21code87">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">get_magic_quotes_gpc</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re1">$inputs</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="sy0">&amp;</span><span class="re1">$_GET</span><span class="sy0">,</span> <span class="sy0">&amp;</span><span class="re1">$_POST</span><span class="sy0">,</span> <span class="sy0">&amp;</span><span class="re1">$_COOKIE</span><span class="sy0">,</span> <span class="sy0">&amp;</span><span class="re1">$_ENV</span><span class="sy0">,</span> <span class="sy0">&amp;</span><span class="re1">$_SERVER</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//масив із суперглобальними, приєднними за посиланнями, щоб виловити усі зміни до них</span>
&nbsp;
        <span class="kw1">while</span> <span class="br0">&#40;</span><span class="kw3">list</span><span class="br0">&#40;</span><span class="re1">$input</span><span class="sy0">,</span> <span class="re1">$data</span><span class="br0">&#41;</span> <span class="sy0">=</span> <span class="kw3">each</span><span class="br0">&#40;</span><span class="re1">$inputs</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re1">$data</span> <span class="kw1">as</span> <span class="re1">$key</span> <span class="sy0">=&gt;</span> <span class="re1">$val</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">is_array</span><span class="br0">&#40;</span><span class="re1">$val</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="co1">//якщо під-елемент не є масивом - реверсувати екранування</span>
                    <span class="re1">$inputs</span><span class="br0">&#91;</span><span class="re1">$input</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re1">$key</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw3">stripslashes</span><span class="br0">&#40;</span><span class="re1">$val</span><span class="br0">&#41;</span><span class="sy0">;</span>
                    <span class="kw1">continue</span><span class="sy0">;</span>
                <span class="br0">&#125;</span>
                <span class="re1">$inputs</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=&amp;</span> <span class="re1">$inputs</span><span class="br0">&#91;</span><span class="re1">$input</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re1">$key</span><span class="br0">&#93;</span><span class="sy0">;</span> <span class="co1">//розрівнювання - якщо під-елемент сам є масивом, приєднуємо його за посиланням в кінець масиву із суперглобальними</span>
            <span class="br0">&#125;</span>
        <span class="br0">&#125;</span>
        <span class="kw3">unset</span><span class="br0">&#40;</span><span class="re1">$inputs</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">//вивільнюємо використаний масив</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Ми позбавились рекурсії та зайвої функції, цим зробили наш код <em>безпечнішим</em> і <em>швидшим</em>.<br id="q_zw" /> <br id="ek4a" /> І ще трохи інформації про <strong>імена файлів</strong> та <em>magic_quotes_gpc</em>. Це зовсім цікавий випадок. Нехай у нас є файл із іменем <em>bad&#8217;file.txt</em>. Він включає в себе апостроф - це цілком нормально, хоча і не дуже звично. Отже екранована версія цього імені буде <em>bad\&#8217;file.txt</em>. А тепер уявімо, що наш скрипт працює у ОС Windows (фєє) із PHP &lt; 4.3.10 &amp; &lt; 5.0.3 (для 5ої гілки). А наскільки відомо, ця ОС інтерпретує &#8220;\&#8221; як розподільник директорій. У таких умовах файл буде мати директорію &#8220;<em>bad</em>&#8221; і назву &#8220;<em>&#8216;file.txt</em>&#8220;. Щодо більш свіжих версій PHP, то частина до останнього символу лапок буде обрізана, що призведе до <em>втрати даних</em>. На *nix системах це не актуально, але все-одно може призвести до втрати даних. Тож давайте розберемось із цим:<br id="uy-l" /> <br id="ib-w" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p2188">
        <div class="code" id="p21code88">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">get_magic_quotes_gpc</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
        <span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'name'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw3">stripslashes</span><span class="br0">&#40;</span><span class="re1">$_FILES</span><span class="br0">&#91;</span><span class="st0">'file'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'name'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>От і все. Якщо ж ви самостійно робите екранування, то краще просто <em>оминайте</em> ім&#8217;я файлів.</p>
<p>На останок хочу дати посилання на <a href="http://pixelated-dreams.com/archives/231-Filtering-Escaping-Cheat-Sheet.html" title="Filtering &#038; Escaping Cheat Sheet">шпаргалку</a> з убезпечення та фільтрації даних.</p>
<p>На цьому завершую другу частину статті про <strong>обробку зовнішніх даних</strong>. Сподіваюсь, вона була вам цікава та корисна. Будь ласка, коментуйте, якщо побачите неточності, маєте щось доповнити чи просто виразити свою думку.<br />
Наступною заміткою із серії про безпеку буде стаття про <a href="http://webdeveloping.com.ua/php/sql-injection-securing-your-database-2" title="SQL Injection або як зробити вашу базу даних безпечною">ін&#8217;єкції SQL</a>. Підписуйтесь на <a title="RSS стрічка webdeveloping" href="http://feeds.feedburner.com/webdeveloping">RSS-стрічку</a>, якщо ви вважаєте мої замітки корисними. Їх буде більше! <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/php/php-user-input-data-validation/feed</wfw:commentRss>
		</item>
		<item>
		<title>Обробка зовнішніх даних. Частина І: Ввід</title>
		<link>http://webdeveloping.com.ua/php/php-user-input-data-processing</link>
		<comments>http://webdeveloping.com.ua/php/php-user-input-data-processing#comments</comments>
		<pubDate>Sun, 13 Apr 2008 21:11:15 +0000</pubDate>
		<dc:creator>MoD</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[безпека]]></category>

		<category><![CDATA[ввід даних]]></category>

		<guid isPermaLink="false">http://webdeveloping.com.ua/?p=19</guid>
		<description><![CDATA[Перше правило, яке слід запам&#8217;ятати будь-якому веб-розробнику (не тільки на PHP): ніколи не довіряй даним, які надійшли ззовні. Абсолютно завжди необхідно перевіряти всі дані користувача. У цій статті мова піде про перевірку і убезпечення вхідних даних. Спробуємо відповісти на ці три питання:

Як визначити методи вводу даних?
Яким чином кожен метод може бути використаним зловмисником?
Яким чином кожен [...]]]></description>
			<content:encoded><![CDATA[<p>Перше правило, яке слід запам&#8217;ятати будь-якому веб-розробнику (не тільки на PHP): <span id="e_o2"><strong id="r-2j">ніколи не довіряй даним, які надійшли ззовні</strong></span>. Абсолютно завжди необхідно перевіряти <strong>всі</strong> дані користувача. У цій статті мова піде про перевірку і убезпечення вхідних даних. Спробуємо відповісти на ці три питання:<br id="l05." /></p>
<ol id="doyh">
<li id="g-kl">Як визначити методи вводу даних?</li>
<li id="g-kl">Яким чином кожен метод може бути використаним зловмисником?</li>
<li id="g-kl">Яким чином кожен тип вводу можна перевірити з метою запобігання проблем із безпекою?</li>
</ol>
<p>У цій частині розглянемо перші два питання.<br id="qydy" /><br />
<span id="more-19"></span></p>
<h3 id="xk_3">Проблема вводу</h3>
<p>З покон віків програмісти на PHP мали доступ до вхідних даних за допомогою вбудованого механізму &#8220;<strong>register globals</strong>&#8221; (реєстрація глобальних змінних). Цей  логічний засіб дозволяє отримати легкий доступ через <em>глобальні</em> змінні до однойменних полів форм чи параметрів, переданих через адресну строку. Наприклад: URL script.php?foo=bar утворює у глобальному просторі імен змінну $foo ініціалізовану значенням &#8216;bar&#8217;.<br id="xfea" />Проблема ж тут полягає у можливому конфлікті імен. Дані до скрипта можуть надходити із декількох джерел: <em>GET</em>-запити, <em>POST</em>-запити, <em>cookies</em>, змінні <em>середовища сервера</em>, змінні <em>системного середовища</em>. Якщо надходить декілька змінних із однаковим іменем з різних джерел - це призведе до <strong>втрати даних</strong>, адже PHP просто <em>об&#8217;єднає</em> дані із всіх цих джерел у один простір - <em>глобальний</em>. Щоправда, є можливість контролювати порядок пріоритетності цих джерел. Для цього існує 2 директиви у php.ini:<br id="f2al" /></p>
<ol id="af7z">
<li id="bqrr">старий <span id="csuh" style="font-weight: bold;">gpc_order</span> - визначає порядок для GET, POST та cookie.</li>
<li id="bqrr">новий <span id="qujx" style="font-weight: bold;">variables_order</span> - визначає порядок для всіх джерел: системне середовище, GET, POST, cookie та середовище сервера.</li>
</ol>
<p>Типово, порядок є визначений таким чином - <span id="eo6m" style="font-style: italic;">EGPCS</span> (system Environment, GET, POST, cookie, Server environment), тобто, якщо прийде дві змінні з однаковим іменем з GET та cookie - перевага буде надана даним із cookie. При чому, немає можливості знати про втрату даних із скрипта. Можливий вихід - призначати для змінних із різних джерел різні префікси, наприклад, за допомогою функції <a id="ihev" title="Імпортування зовнішніх змінних у глобальний простір імен" href="http://ua.php.net/manual/ru/function.import-request-variables.php" rel="nofollow">import_request_variables</a>. Але це можливо лише тоді, коли ви маєте повний контроль над процесом написання коду.<br id="h3i1" />Для розуміння проблеми ознайомимось із наступним прикладом:<br id="q.i2" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p1995">
        <div class="code" id="p19code95">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>isAdmin<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re1">$admin</span> <span class="sy0">=</span> <span class="kw2">true</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$admin</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="co1">//контент для авторизованого користувача</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Коли увімкнена директива <span id="m8wk" style="font-style: italic;">register_globals</span>, і скрипту буде переданий параметр <span id="bx.y" style="font-style: italic;">&#8220;admin</span>&#8221; наприклад через GET - PHP створить у глобальному просторі імен змінну $admin, ініціалізовану переданим значенням, оскільки у другій умові немає суворого порівняння (оператор ===), зловмисник зможе легко отримати доступ до закритої частинисайту. У випадку, якщо немає можливості вимкнути register_globals, треба щонайменше ініціалізувати всі необхідні змінні перед їхнім використанням. <br id="yoey" />Нажаль, у PHP немає ніякого &#8220;суворого&#8221; (strict) режиму як у Perl чи зауважень компілятора як у С. Такі засоби могли б показати розробнику всі проблемні місця у коді. Єдиний шлях побачити  місця використання неініцілізованих змінних - перемкнути рівень виведених помилок на E_ALL. Наприклад так: <br id="dehb" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p1996">
        <div class="code" id="p19code96">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">error_reporting</span><span class="br0">&#40;</span><span class="kw2">E_ALL</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>у коді чи<br id="k5.e" /></p>
<pre>error_reporting=E_ALL</pre>
<p>у php.ini<br id="corn" />Це може дуже допомогти у процесі розробки, однак не відловить всі можливі проблемні місця. Наприклад, не побачить неініціалізованих масивів, оскільки PHP автоматично створює масив при спробі записати туди нового елемента (незалежно від його порядку). Тому масиви також слід ініціалізовувати перед їх використанням. Наприклад так:<br id="pi2-" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p1997">
        <div class="code" id="p19code97">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="re1">$vector</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re1">$vector</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st0">'25'</span><span class="sy0">;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<h3 id="x7wq">Альтернатива</h3>
<p>Фактично, <strong>register<span id="mooh" style="font-style: italic;">_</span>globals</strong> - найпоширеніша причина дірок у безпеці програм на PHP. У самому PHP цей механізм вже досить багато часу має статус застарілого (deprecated). Але не дивлячись на це, дуже багато програмістів продовжують її використовувати замість нового кращого засобу доступу до даних вводу - <strong>суперглобальні масиви</strong>.<br id="rbic" />У PHP 4.1 з&#8217;явились масиви <em><span id="hb-d" style="font-style: italic;">$_</span>GET</em>, <em><span id="d583" style="font-style: italic;">$_</span>POST</em>, <em><span id="mtav" style="font-style: italic;">$_</span>COOKIE</em>, <em><span id="d77x" style="font-style: italic;">$_</span>SERVER</em> та <em><span id="dff0" style="font-style: italic;">$_</span>ENV</em><span id="ea7p" style="font-style: italic;">.</span> Вони надають глобальний доступ до всіх вхідних даних без конфлікту імен у будь-якій частині скрипта та дають однозначну відповідь на питання про походження даних. Нажаль, багато хостингів досі вмикають register_globals для підтримки старих програм, тому розробники мають створювати свої програми, пам&#8217;ятаючи про це.<br id="vkri" /> <br id="q1cl" />Найпростіший метод захисту від register_globals - <span id="vxf4" style="font-weight: bold;">використання констант</span>, оскільки механізм реєстрації глобальних змінних не має прямого впливу на константи. Ось таким чином можна уникнути проблем у поганому серверному середовищі: <br id="bpfn" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p1998">
        <div class="code" id="p19code98">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw3">define</span><span class="br0">&#40;</span><span class="st0">'ADMIN'</span><span class="sy0">,</span> isAdmin<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span>ADMIN<span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="co1">//показати прихований контент</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Але при деяких обставинах константи самі можуть стати причиною небезпеки. Справа в тому, що неініціалізована константа є <strong>строкою</strong>, що зберігає ім&#8217;я константи (це витікає з того, що PHP є мовою із не суворою типізацією). Таким чином можливий наступний сценарій:<br id="z1.e" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p1999">
        <div class="code" id="p19code99">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>isAdmin<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw3">define</span><span class="br0">&#40;</span><span class="st0">'ADMIN'</span><span class="sy0">,</span> <span class="kw2">true</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span>ADMIN<span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="co1">//умова завжди буде істинною - або bool(true), коли визначене, або string('ADMIN'), коли - ні.</span>
        <span class="co1">//показати прихований контент</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Кращим за константи підходом є вже загадане <span id="zcad" style="font-weight: bold;">суворе порівняння</span> (===) - окрім рівності, воно перевіряє, чи однакові типи даних зліва і зправа. <span id="qz6n" style="font-style: italic;">Всі</span> вхідні дані мают строковий тип або є масивами строк. Тому порівняння наприклад із булівським типом завжди буде невдалим при спробі підміни даних:<br id="z331" /></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p19100">
        <div class="code" id="p19code100">
            <pre class="php"><span class="kw2">&lt;?php</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>isAdmin<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="re1">$admin</span> <span class="sy0">=</span> <span class="kw2">true</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$admin</span> <span class="sy0">===</span> <span class="kw2">true</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="co1">//показати прихований контент</span>
    <span class="br0">&#125;</span>
<span class="kw2">?&gt;</span></pre>
        </div>
    </div>
</div>

<p>Попри все сказане, найкращий метод захисту від <em>register_globals</em> - <span id="dvjk" style="font-weight: bold;">вимкнути</span> цей механізм, якщо можливо. Це можна зробити у трьох місцях: <em>php<span id="pb9t" style="font-style: italic;">.</span>ini</em>, <em>httpd<span id="wxsj" style="font-style: italic;">.</span>conf</em>, <em><span id="pv7:" style="font-style: italic;">.</span>htaccess</em>. Найгнучкіший метод - останній, адже його можна включити у дистрибуцію вашої програми, але слід пам&#8217;ятати, що не всі використовують Apache та не всі його версії це підтримують. Тому необхідно намагатись писати універсально-безпечний код. <br id="qhm4" /><br id="jo2m" />Ще кілька слів про останній суперглобальний масив <strong><span id="lv.o" style="font-weight: bold;">$_</span>REQUEST</strong>. Його було додано разом із введенням інших масивів з метою більш легкого переходу зі старого коду. Цей масив комбінує у собі дані з GET, POST та cookie заради легкості використання. Але його проблема знову ж таки у можливій втраті даних, адже їх походження ніяк не відокремлюється.<br id="s82j" />Фактично, для безпечного використання цього масиву необхідно перевіряти, з якого суперглобального масиву прийшли дані, але це нівелює всю зручність засобу. Тож легше використовувати звичайні суперглобальні масиви.<br id="f23z" /><br id="hlno" />На сьогодні це все. У <a href="http://webdeveloping.com.ua/php/php-user-input-data-validation" title="Обробка зовнішніх даних. Частина ІІ: Валідація">наступній частині</a> я розповім про найважливіше - перевірку різних типів зовнішніх даних, тож чекайте скоро на новий запис. <img src='http://webdeveloping.com.ua/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://webdeveloping.com.ua/php/php-user-input-data-processing/feed</wfw:commentRss>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 4.441 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2010-03-27 18:28:48 -->
