<?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/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>m1chu.eu - another devblog &#187; MySQL</title>
	<atom:link href="http://m1chu.eu/category/webmastering/server-side/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://m1chu.eu</link>
	<description>we live, as we dream... alone - another devblog</description>
	<lastBuildDate>Sun, 14 Nov 2010 11:07:18 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>Jak zabezpieczyć skrypt PHP/MySQL? Część 1: luka Arbitrary File Download (AFD)</title>
		<link>http://m1chu.eu/2008/09/11/jak-zabezpieczyc-skrypt-phpmysql-czesc-1-luka-arbitrary-file-download-afd/</link>
		<comments>http://m1chu.eu/2008/09/11/jak-zabezpieczyc-skrypt-phpmysql-czesc-1-luka-arbitrary-file-download-afd/#comments</comments>
		<pubDate>Wed, 10 Sep 2008 23:05:50 +0000</pubDate>
		<dc:creator>m1chu</dc:creator>
				<category><![CDATA[Hacking]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Webhosting]]></category>
		<category><![CDATA[afd]]></category>
		<category><![CDATA[arbitrary file download]]></category>
		<category><![CDATA[atak]]></category>
		<category><![CDATA[bezpieczeństwo]]></category>
		<category><![CDATA[luka]]></category>
		<category><![CDATA[zabezpieczenia]]></category>

		<guid isPermaLink="false">http://m1chu.eu/?p=53</guid>
		<description><![CDATA[Postanowiłem podejść do sprawy zabezpieczeń w wielu wpisach. Pisanie o bezpieczeństwie w jednym byłoby długie i cholernie monotonne. Zaczniemy więc tym razem niestandardowo, bo od ataku typu Arbitrary File Download (szczerze większej publikacji o tym nie znalazłem) &#8211; z reguły rzadszym, ale bardzo niebezpiecznym, a to wszystko jak sama nazwa mówi z powodu możliwości ściągnięcia [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: center;"><img class="iborder" src="http://farm4.static.flickr.com/3070/2954723512_15f53621b2_o.png" alt="Luka AFD" /></p>
<p>Postanowiłem podejść do sprawy zabezpieczeń w wielu wpisach. Pisanie o bezpieczeństwie w jednym byłoby długie i cholernie monotonne. Zaczniemy więc tym razem niestandardowo, bo od ataku typu <strong>Arbitrary File Download</strong> (szczerze większej publikacji o tym nie znalazłem) &#8211; z reguły rzadszym, ale bardzo niebezpiecznym, a to wszystko jak sama nazwa mówi z powodu możliwości ściągnięcia dowolnego pliku na serwerze na którym wykonywany jest odpowiedni plik php.</p>
<p><span id="more-53"></span><br />
Tłumacząc na polski nazwę tej luki otrzymalibyśmy mniej lub bardziej dosłownie translację w stylu <strong>&#8222;Pobieranie dowolnego pliku&#8221;</strong>. Stąd moje krótkie tłumaczenie w powyższym paragrafie.</p>
<p>Z teoretycznego punktu widzenia dokładniej wygląda to tak, że za pomocą parametru w adresie pliku przeznaczonego do ściągania innych plików z serwera możemy spreparować tak adres, aby pozwolił on nam ściągnąć pliki domyślnie przez programistę nieprzeznaczone do takiego zabiegu. Wiąże się to niedostateczną, a najczęściej po prostu z brakiem filtracji elementu (np. zmiennej) odpowiedzialnego za pobieranie argumentu.</p>
<p>Pokażę prosty przykład pliku (nazwijmy go <strong>download.php</strong>) który jest podatny na tego typu atak:</p>
<pre class="brush: php; title: ;">&lt;?php

if ( isset($_GET['file']) )
{
	header('Cache-control: private');
	header('Content-Length: ' . filesize($_GET['file']));
	header('Content-Type: application/octet-stream');
	header('Content-Disposition: attachment; filename=' . basename($_GET['file'])); // nagłówek ustawiający zawartość jako załącznik

	readfile($_GET['file']); // ściągnięcie pliku
}

?&gt;</pre>
<p>I pomimo, że intencją programisty było ściąganie dzięki temu plikowi np. dokumentów PDF, to my dzięki jego niefrasobliwości bez problemu możemy wykorzystać go do&#8230; pobierania pozostałych plików PHP. Co najważniejsze &#8211; <strong>nieprzeparsowanych</strong>! <strong>Po prostu czysty kod PHP w takiej formie jaką twórca strony umieścił na serwerze</strong>. Jedyne co nas w tym wypadku ogranicza to funkcja <strong>readfile</strong> która standardowo nie nadaje się do pobierania dużych plików, a także czas wykonywania skryptu i wielkość bufora ustawiona w pliku konfiguracyjnym PHP.</p>
<p>Wracając do przykładów. Na wyimaginowanym odnośniku powiedzmy linkującego do dokumentu PDF wyglądało by to tak:</p>
<pre class="brush: plain; title: ;">www.strona_internetowa.pl/catalog/download.php?file=specyfikacja.pdf</pre>
<p>Pierwsza myśl która Wam może przyjść do głowy to podmienić po prostu <strong>specyfikacja.pdf</strong> na inny plik. Standardowo mógłby być to <strong>index.php</strong>, gdyż ściągając jego zawartość otrzymamy informację o nazwach innych powiązanych z nim plików. Niekoniecznie jednak plik ten musi się znajdować w tym samym katalogu co <strong>download.php</strong>, czy <strong>specyfikacja.pdf</strong>. </p>
<p>W pierwszym przypadku widzimy, że plik PHP którym ściągamy pozostałe źródła znajduje się w podkatalogu. Dlatego dla pewności możemy wypróbować dwa spreparowane linki:</p>
<pre class="brush: plain; title: ;">www.strona_internetowa.pl/catalog/download.php?file=index.php // plik znajdowałby się wtedy pod /catalog/index.php
www.strona_internetowa.pl/catalog/download.php?file=../index.php // plik znajdowałby się wtedy bezpośrednio w strona_internetowa.pl/index.php</pre>
<p>W drugim punkcie sprawa wygląda tak, że plik <strong>specyfikacja.pdf</strong> nie musi się znajdować w tym samym katalogu co <strong>download.php</strong> <em>pomimo</em>, że w odnośniku nie jest podana do niego inna ścieżka. Wystarczy, że twórca strony w w.w. przeze mnie kodzie zmieni linijkę z <strong>readfile</strong> (a także linię odpowiedzialną za pobieranie wielkości pliku) na:</p>
<pre class="brush: php; title: ;">&lt;?php
[...]
	header('Content-Length: ' . filesize('./podkatalog/kolejny/' . $_GET['file']));
[...]
    readfile('./podkatalog/kolejny/' . $_GET['file']); // ściągnięcie pliku

?&gt;</pre>
<p>W takim wypadku jego pliki PDF są po prostu ściągane z:</p>
<pre class="brush: plain; title: ;">www.strona_internetowa.pl/catalog/podkatalog/kolejny/</pre>
<p>I tu rodzi się kolejne rozwiązanie &#8211; używanie przejść do katalogów nadrzędnych. Jeśli w tym wypadku chcielibyśmy ściągnąć <strong>index.php</strong> z głównego katalogu to wystarczy po prostu wpisać taki adres w przeglądarce:</p>
<pre class="brush: plain; title: ;">www.strona_internetowa.pl/catalog/download.php?file=../../../index.php</pre>
<p>Spytacie może:</p>
<blockquote><p>W takim bądź razie skąd ja mam wiedzieć, gdzie wreszcie znajduje się ten mój pożądany plik?</p></blockquote>
<p>Jeśli zaczynasz się w to bawić to najprościej będzie robić to metodą prób i błędów poprzez przejścia do podkatalogów i katalogów nadrzędnych. Jeżeli któreś z kolei nie zadziała to albo skrypt jest zabezpieczony, albo po prostu nie ma dostępu do pliku którego szukasz na serwerze (tak może być niekiedy z poszukiwaniem np. systemowego <strong>/etc/passwd</strong>). Innym sposobem jest użycie jakiegoś downloadera plików w stylu <em>wget</em>, czy innych Windowsowych zamienników, po czym analiza struktury katalogów serwisu. Zabieg w miarę prosty (chodzi o użytkowanie tych narzędzi) więc chyba tłumaczyć nie muszę.</p>
<p style="text-align: center;"><a href="http://farm4.static.flickr.com/3065/2846837230_1f619ac3df_o.png"  title="Przykład działania AFD" onclick="this.target='_blank';"><img src="http://farm4.static.flickr.com/3065/2846837230_890dce249f.jpg" alt="Przykład działania AFD" /></a></p>
<p>Jak to wygląda już tak kompletnie w praktyce życiowej. Wiele osób twierdzi, że nie jest łatwo znaleźć taki błąd. Fakt, co nie zmienia faktu, że nie jest to niemożliwe. Specjalnie dla Was dzisiaj w nocy za pomocą Google wyszukałem <strong>pięć</strong> stron podatnych na ten atak. Jedną z nich wybiorę jako przykład ukazania wyszukania strony, błędu i ostatecznie jego edukacyjnego użycia.</p>
<dl>
<dt>
<ul>
<li><strong>1.</strong> Przeszukujemy Google&#8230;</li>
</ul>
</dt>
<dd>
<p>Możemy to oczywiście zrobić na różne sposoby. Osobiście użyje tejże wyszukiwarki, a stronę znajdywać będę poprzez frazy wyszukiwania, np. takie:</p>
<pre class="brush: plain; title: ;">inurl:.eu (getfile.php OR get.php OR file.php OR download.php)
inurl:home.pl (getfile.php OR get.php OR file.php OR download.php)</pre>
<p>Tłumacząc, zostaną wyszukane wszelkie zindeksowane strony w domenie <strong>.eu</strong> (europejskiej) lub <strong>home.pl</strong> z co najmniej jednym plikiem podanym w nawiasie. Oczywiście można rzucić światło imaginacji i podać więcej plików które mogłyby być podanym wyżej przeze mnie źródłem ściągania, ale nam wystarczą tylko te.</dd>
<dt>
<ul>
<li><strong>2.</strong> Znalezienie celu&#8230;</li>
</ul>
</dt>
<dd>Już na pierwszej stronie pierwszej frazy, i drugiej stronie drugiej frazy możemy znaleźć obiekty potencjalnie narażone na atak.</p>
<pre class="brush: plain; title: ;">http://anonymouse.org/cgi-bin/anon-www.cgi/http://www.ejls.eu/download.php?file=./issues/2007-12/MohrContiniUK.pdf

http://anonymouse.org/cgi-bin/anon-www.cgi/http://gfp.home.pl/www/news/file.php?file=AGP_notka_prasowa.doc</pre>
</dd>
<dt>
<ul>
<li><strong>3.</strong> Spreparowanie parametru&#8230;</li>
</ul>
</dt>
<dd>Wystarczy tylko wejść pod jeden z tych adresów i spróbować ściągnąć dla przykładu <strong>index.php</strong>. Co się rzuca od razu w oczy to fakt, że w drugim przypadku <em>prawdopodobnie</em> plik przez nas pożądany będzie znajdować się dwa katalogi wyżej niż aktualnie jesteśmy.</p>
<pre class="brush: plain; title: ;">http://anonymouse.org/cgi-bin/anon-www.cgi/http://www.ejls.eu/download.php?file=index.php

http://anonymouse.org/cgi-bin/anon-www.cgi/http://gfp.home.pl/www/news/file.php?file=../../index.php</pre>
</dd>
<dt>
<ul>
<li><strong>4.</strong> Co dalej?</li>
</ul>
</dt>
<dd>No właśnie. Nie zrobię Wam tu do końca kursu jak włamać się komuś na stronę. Bo posiadając dostęp do plików można bez problemu dostać się do bazy (o ile takowa istnieje), a co za tym idzie po prostu zostawić <strong><em>&#8222;OWNED&#8221;</em></strong> (o <a href="http://utnij.eu/phising/"  title="Phising Wiki" onclick="this.target='_blank';">phishingowym</a> wykorzystaniu chyba nie trzeba wspominać). Powiem tylko tyle, że znając PHP można bez problemu w takim momencie poznać strukturę plików, z tym co napisałem powyżej także ściągnąć je, a w tym te odpowiadające za konfiguracje czy hasła.</p>
<pre class="brush: plain; title: ;">http://anonymouse.org/cgi-bin/anon-www.cgi/http://gfp.home.pl/www/news/file.php?file=../../skins/default.skin.php // inny przykład linku wyciągniętego z źródła index.php
http://anonymouse.org/cgi-bin/anon-www.cgi/http://www.ejls.eu/download.php?file=download.php // ściągnięcie pliku download.php</pre>
</dd>
</dl>
<p>Na koniec kwestia która jest priorytetem i kulminacją tego wpisu.</p>
<blockquote><p>Jak się do ku**y zabezpieczyć przed tym?!</p></blockquote>
<p>Przede wszystkim należy filtrować dane. Zawsze należy, bo nawet jak nie trzeba to prócz większej ilości operacji nic więcej się serwisowi nie stanie z tego powodu. Taka drobna dygresja. Pytanie brzmi, jak to zrobić?</p>
<p style="text-align: center;"><a target="_blank" href="http://farm4.static.flickr.com/3139/2846061969_2c415035c7_o.png"  title="Przykład działania AFD" class="lightbox"><img src="http://farm4.static.flickr.com/3139/2846061969_d1ebeb2a33.jpg" alt="Przykład działania AFD" /></a></p>
<p>I tu już wszystko zależy od inwencji webmastera. Pokaże dwa przykłady w zależności od podawanego argumentu.</p>
<ul>
<li><strong>Ściąganie pliku poprzez ID i wykorzystanie bazy danych MySQL.</strong></li>
</ul>
<p>Przede wszystkim musimy utworzyć prostą tabelę. Zakładam, że wszystkie pliki będą się znajdować w jednym katalogu (powiedzmy <strong>download</strong>).</p>
<pre class="brush: sql; title: ;">CREATE TABLE files ( file_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, file_name VARCHAR(255) NOT NULL ) COMMENT = 'file_id; 0-65535; klucz glowny : file_name; 0-255; nie pusty', MAX_ROWS = 65535;</pre>
<p>Nasz zabugowany wyżej plik możemy zamienić na coś takiego (użyłem czystych funkcji PHP dla MySQL, jeśli ktoś zna rozszerzenie MySQLi albo moduł PEAR DB to nie będzie miał problemu z przepisaniem sobie tego):</p>
<pre class="brush: php; title: ;">&lt;?php
require_once './config.php'; // plik konfiguracyjny z tablica danych do bazy danych
require_once './mimes.inc.php'; // plik z funkcja rozpoznajaca typy mime (function getMimeType(file))

$file			= ( isset($_GET['file']) ? intval($_GET['file']) : 0 ); // filtracja parametru file zawierajacego domyslnie w sobie liczby calkowite

if ( $file ) // jesli $file jest rozne od 0 tzn ze w parametrze zostalo wpisane ID w postaci cyfry
{
	if ( $connection = mysql_connect($db['host'], $db['user'], $db['pass']) === false ) // laczenie z baza danych za pomoca danych z tablic $db pliku config.php
	{
		print 'Blad polaczenia z baza danych: ' . mysql_error();
		die();
	}
	mysql_select_db($db['base']); // wybor bazy danych

	$fetched		= mysql_fetch_assoc(
		mysql_query('SELECT count(file_id) as counted, file_name
			FROM files
			WHERE file_id = ' . $file . '
			GROUP BY file_id
				LIMIT 1')
		); // zapytanie zliczajace i zwracajace liczbe rekordow o file_id = $file oraz nazwe pliku o podanym ID
	if ( $fetched['counted'] == 1 ) // jesli powyzsze zapytanie zliczylo 1 wiersz tzn ze ID jest prawidlowe
	{
		// szybka, dodatkowo filtracja poprzez zamiane encji na znaki i usuniecie ewentualny /
		// UWAGA! filtracja dokladna wpisywanych danych powinna byc przeprowadzona przy wprowadzaniu pliku do bazy
		if ( strpos($fetched['file_name'], '../') === false &amp;&amp; file_exists('./download/' . $fetched['file_name']) === true ) // sprawdzenie istnienia pliku
		{
			// wyslanie odpowiednich naglowkow
			header('Cache-control: private');
			header('Content-Length: ' . filesize('./download/' . $fetched['file_name']));
    		header('Content-Type: ' . getMimeType($fetched['file_name'])); // wyslanie odpowiedniego typu MIME pliku zwracanego przez funkcje getMimeType (do napisania ;])
    		header('Content-Disposition: attachment; filename=' . basename($fetched['file_name']));

	    	// odczytanie pliku
    		readfile('./download/' . $fetched['file_name']);
		}
		else {
			print 'Plik o podanym ID nie istnieje!'; // nie znaleziono pliku o podanym ID na serwerze, blad!
		}
	}
	else {
		print 'Niepoprawne ID pliku!'; // nie znaleziono pliku o podanym ID, blad!
	}
	mysql_close(); // zamkniecie polaczenia
}
else {
	print 'Brak ID pliku!'; // nie znaleziono pliku o podanym ID, blad!
}
?&gt;</pre>
<p>Należy pamiętać, że ważnym aspektem zachowania bezpieczeństwa jest także odpowiednia filtracja przy dodawaniu rekordów z nazwami plików do bazy.</p>
<p style="text-align: center;"><a target="_blank" href="http://farm4.static.flickr.com/3014/2847271125_0d1894e6f0_o.png"  title="Przykład działania AFD" class="lightbox"><img src="http://farm4.static.flickr.com/3014/2847271125_ea2576202d.jpg" alt="Przykład działania AFD" /></a></p>
<ul>
<li><strong>Ściąganie pliku po jego nazwie.</strong></li>
</ul>
<p>Tym razem zabezpieczenie będzie trzeba zrobić bardziej intuicyjnie i oparte tylko na odpowiednim filtrowaniu. Wszystko zależy tutaj od tego skąd chcemy ściągać pliki. Przede wszystkim powinniśmy <strong>zabronić</strong> jakiegokolwiek pobierania z katalogów nadrzędnych, a także z katalogów podrzędnych w których znajdują się pliki będące integralną częścią strony. Najwygodniej oczywiście byłoby ustawić jeden, konkretny katalog do ściągania. Nie mniej jednak my skupimy się na ograniczeniu do <strong>n-tej</strong> ilości podkatalogów. Ostatecznie należy pamiętać także, aby w katalogach do których będzie miał dostęp użytkownik z poziomu skryptu pobierającego nie było plików innych niż zdatne do takiego ściągnięcia, no i ewentualnie <strong>index.html</strong> z pustą stroną.</p>
<pre class="brush: php; title: ;">&lt;?php
function afdFiltration($file, $a_extns, $pattern = '')
{
	$file					= html_entity_decode(urldecode($file)); // usunięcie postaci procentowej znaków i zdekodowanie encji na znaki

	// sprawdzenie, czy wystepuje w ciagu ../ i ..\
	if ( strpos($file, '../') !== false || strpos($file, '..\\') !== false )
	{
		return '';
	}

	if ( $pattern != '' )
	{
		if ( !preg_match($pattern, $file) ) // jesli podano wzor to sprawdzenie czy pokrywa sie on z podanym plikiem
		{
			return '';
		}
	}

	$extension				= strtolower(end(explode(&quot;.&quot;, $file))); // rozszerzenie pliku
	if ( in_array($extension, $a_extns) === false ) // sprawdzenie czy rozszerzenie pliku pokrywa sie z dozwolonymi rozszerzeniami w skrypcie
	{
		return '';
	}

	if ( file_exists($file) === false ) // sprawdzenie istnienia pliku
	{
		return '';
	}

	return $file; // zwrocenie przefiltrowanego adresu pliku
}

require_once './mimes.inc.php'; // plik z funkcja rozpoznajaca typy mime (function getMimeType(file))

$allowed_extensions		= array('pdf', 'txt', 'c'); // dozwolone rozszerzenia plikow
$pattern				= ''; // opcjonalny wzor do sprawdzenia poprawnosci wprowadzonego adresu pliku

$file					= ( isset($_GET['file']) ? afdFiltration($_GET['file'], $allowed_extensions, $pattern) : '' ); // wywolanie funkcji filtrujacej, gdy podano argument file

if ( $file != '' ) // w wypadku pozytywnego przejscia filtracji
{
	header('Cache-control: private');
	header('Content-Length: ' . filesize($file));
	header('Content-Type: ' . getMimeType($file)); // wyslanie odpowiedniego typu MIME pliku zwracanego przez funkcje getMimeType (do napisania ;])
   	header('Content-Disposition: attachment; filename=' . basename(strtolower(end(explode(&quot;/&quot;, $file)))));

	// odczytanie pliku
    readfile($file);
}
else {
	print 'Nieprawidłowa nazwa lub ścieżka do pliku!'; // nie znaleziono pliku o podanym ID, blad!
}
?&gt;</pre>
<p>Zasada powyższego, przykładowego skryptu jest prosta. Filtrujemy w trzech poziomach. Zmieniając encje na znaki usuwamy ciąg <strong>../</strong> z adresu. Sprawdzamy opcjonalnie adres do pliku wg. zadanego przez nas wzorca (o ile takowy podamy) i ostatecznie porównujemy rozszerzenie pliku z dozwolonymi przez nas osobiście rozszerzeniami. Przykładem wzorca dla wyrażenia regularnego będzie np.:</p>
<pre class="brush: php; title: ;">$pattern				= '/^([a-zA-Z0-9\_\-]+)\\.([a-z0-9]{2,4})$/';</pre>
<p>Pozwala ono na ściąganie dowolnego pliku zawierającego w sobie duże i małe litery, cyfry oraz znaki _ i -, a także rozszerzenie będące małymi literami i/lub cyframi o długości od dwóch do czterech znaków.</p>
<p>Na koniec przekazuje kilku minutowy filmik obrazujący znalezienie i użycie błędu. Zapraszam <a target="_blank" href="http://6thavenue.org/stro/-movies/afd_attack_movie/afd_attack_movie.avi"  title="Zobrazowanie błędu"><strong>tutaj</strong></a>.</p>
<p>Uprzejmie proszę też o komentarze, jak wygląda ten tutorial, czy jest przydatny i przede wszystkim czy taka forma i taka ilość informacji będzie odpowiednia dla przyszłych artykułów z tego zakresu.</p>
<p>PS: <strong>wszelkie znalezione i ukazane błędy zostały przedstawione tylko i wyłącznie w formie edukacyjnej. Nie odpowiadam za formę wykorzystania ich przez użytkowników. Błędy zgłosiłem twórcom stron, więc prędzej czy później mogą one zostać załatane. Proszę o rozsądne ich wykorzystanie przy kształceniu własnych możliwości i NIE NISZCZENIE pracy autorów tych stron.</strong>.</p>
]]></content:encoded>
			<wfw:commentRss>http://m1chu.eu/2008/09/11/jak-zabezpieczyc-skrypt-phpmysql-czesc-1-luka-arbitrary-file-download-afd/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
<enclosure url="http://6thavenue.org/stro/-movies/afd_attack_movie/afd_attack_movie.avi" length="1811968" type="video/x-msvideo" />
<enclosure url="http://6thavenue.org/stro/-movies/afd_attack_movie/afd_attack_movie.mpg" length="23524352" type="video/mpeg" />
		</item>
		<item>
		<title>Jak prosto przechowywać adres IP w bazie danych MySQL?</title>
		<link>http://m1chu.eu/2008/04/24/jak-prosto-przechowywac-adres-ip-w-bazie-danych-mysql/</link>
		<comments>http://m1chu.eu/2008/04/24/jak-prosto-przechowywac-adres-ip-w-bazie-danych-mysql/#comments</comments>
		<pubDate>Thu, 24 Apr 2008 16:54:27 +0000</pubDate>
		<dc:creator>m1chu</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Webhosting]]></category>
		<category><![CDATA[inet]]></category>
		<category><![CDATA[int]]></category>
		<category><![CDATA[ip]]></category>
		<category><![CDATA[varchar]]></category>

		<guid isPermaLink="false">http://m1chu.eu/?p=42</guid>
		<description><![CDATA[Ostatnio w poszukiwaniu możliwości zapisywania adresu IP do bazy danych natknąłem się na jakże prosty, ale poniekąd jeszcze wtedy niezbyt znany mi sposób na ten proceder. Wreszcie od jakiegoś czasu udało mi się zapoznać z czymś dla mnie jeszcze nieznanym w zakresie tworzenia rozwiązań internetowych. Ale do rzeczy. Dla najbardziej aktualnie rozpowszechnionego protokołu komunikacyjnego IPv4 [...]]]></description>
			<content:encoded><![CDATA[<p>Ostatnio w poszukiwaniu możliwości zapisywania adresu IP do bazy danych natknąłem się na jakże prosty, ale poniekąd jeszcze wtedy niezbyt znany mi sposób na ten proceder. <em>Wreszcie od jakiegoś czasu udało mi się zapoznać z czymś dla mnie jeszcze nieznanym w zakresie tworzenia rozwiązań internetowych.</em> Ale do rzeczy.</p>
<p><span id="more-42"></span></p>
<p>Dla najbardziej aktualnie rozpowszechnionego protokołu komunikacyjnego <a target="_blank" href="http://pl.wikipedia.org/wiki/IPv4"  title="IPv4">IPv4</a> adres IP składa się z czterech oktetów liczbowych postaci dziesiętnej oddzielonych kropkami.</p>
<blockquote><p>Przykład: 127.0.0.1 (A.B.C.D)</p></blockquote>
<p>I właśnie przez te rozdzielające kropki adresu nie można takiego adresu zapisać jako bardziej wydajny <strong>INT</strong>.</p>
<p>Najprostszą linią oporu można by dla powyższej potrzeby utworzyć tabelę z kolumną typu <strong>VARCHAR</strong> lub <strong>CHAR</strong> i do niej zapisywać w przedstawionej powyżej postaci adresy IP. Najmniej wydajny jest ostatni typ &#8211; który w wypadku zapisania danych o mniejszej długości niż ta ustawiona wraz z typem uzupełnia pozostały zakres wartościami zerowymi.</p>
<pre class="brush: sql; title: ;">CREATE TABLE przyklad ( ip VARCHAR(15) NOT NULL ); # w najprostszym przykładzie</pre>
<p>Wszelkie próby ominięcia używania typów znakowych w bazie, czy to poprzez szyfrowanie, czy inne wytwory ludzkie wyobraźni zawsze będą wymagać innego typu danych niż liczbowy (chyba, że jakiś sposób uniwersalny znacie?). Nie licząc tych wbudowanych w <strong>MySQL</strong>&#8230;</p>
<p>Mówię tu o funkcjach typu <a target="_blank" href="http://utnij.eu/inet_aton/"  title="INET_ATON">INET_ATON</a> i <a target="_blank" href="http://utnij.eu/inet_ntoa/"  title="INET_NTOA">INET_NTOA</a>. Jak one działają?</p>
<ul>
<li><strong>INET_ATON</strong> &#8211; pozwala na konwersję znakowego <strong>4</strong> lub <strong>8</strong> bajtowego adresu IP do jej reprezentacji liczbowej (32bitowej). Konwersja odbywa się wg. prostej reguły, czyli iloczynu wartości danego oktetu i <strong>256</strong> podniesione do potęgi równej numerowi oktetu liczonego od prawej (pierwszy równa się <strong>0</strong>).</p>
<blockquote><p>INET_ATON(&#8217;127.0.0.1&#8242;) = 127 * 256^3 + 0 * 256^2 + 0 * 256^1 + 1*256^0 = 2130706432 + 0 + 0 + 1 = 2130706433</p></blockquote>
<p>Należy także wspomnieć o dwóch rzeczach. <strong>Nie trzeba</strong> podawać w argumencie powyższej funkcji pełnego adresu &#8211; można ograniczyć się np. do danego zakresu, czy dwóch najwyższych klas &#8211; <strong>A</strong> i <strong>B</strong>.</p>
<pre class="brush: sql; title: ;">... INET_ATON('127.0') ...</pre>
<p>Druga sprawa to fakt, że typ kolumny i pól do których będziemy zapisywać dane powinien być typu <strong>INT UNSIGNED</strong> (tylko wartości dodatnie). W przypadku kiedy pierwszy oktet (klasa A) będzie przy zapisie większa niż <strong>127</strong> mogą wystąpić błędy w bazie jeśli kolumna tychże adresów będzie ustawiona na czyste <strong>INT</strong> spowodowane wyjściem poza zakres tego typu (o czym więcej można przeczytać w powyższych linkach).</p>
<pre class="brush: sql; title: ;">CREATE TABLE przyklad ( ip INT(11) UNSIGNED NOT NULL ); # najprostsza wersja przygotowana pod użycie powyższej funkcji</pre>
</li>
<li><strong>INET_NTOA</strong> &#8211; konwersja zwrotna do powyższej. Pozwala na zmianę z wartości liczbowej do wartości łańcucha znaków, czyli mówiąc potoczniej z powrotem do znanego wszem i wobec adresu IP.</li>
</ul>
<p>Jak można się domyśleć pierwsza wymieniona wyżej funkcja będzie służyć do zapisu adresów IP, druga do odczytu. Kolejno można to zrobić w taki sposób:</p>
<pre class="brush: sql; title: ;">INSERT INTO przyklad ( ip ) VALUES ( INET_ATON('127.0.0.1') );</pre>
<pre class="brush: sql; title: ;">SELECT INET_NTOA( ip ) FROM przyklad;</pre>
<p>Należy pamiętać, że w przypadku odczytu argumentu z <strong>INET_NTOA</strong> <em>nie powinno</em> być żadnego typu apostrofów &#8211; niekiedy jako, że odczytujemy liczbę całkowitą może nastąpić błąd przy wykonaniu zapytania.</p>
]]></content:encoded>
			<wfw:commentRss>http://m1chu.eu/2008/04/24/jak-prosto-przechowywac-adres-ip-w-bazie-danych-mysql/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>10 punktów podstaw teoretycznego tworzenia stron&#8230;</title>
		<link>http://m1chu.eu/2008/02/24/10-punktow-podstaw-teoretycznego-tworzenia-stron/</link>
		<comments>http://m1chu.eu/2008/02/24/10-punktow-podstaw-teoretycznego-tworzenia-stron/#comments</comments>
		<pubDate>Sun, 24 Feb 2008 10:37:04 +0000</pubDate>
		<dc:creator>m1chu</dc:creator>
				<category><![CDATA[Edukacja]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Webhosting]]></category>
		<category><![CDATA[architektura informacji]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[projektowanie]]></category>
		<category><![CDATA[teoria]]></category>
		<category><![CDATA[tworzenie stron]]></category>
		<category><![CDATA[xhtml]]></category>

		<guid isPermaLink="false">http://m1chu.eu/edukacja/10-punktow-podstaw-teoretycznego-tworzenia-stron</guid>
		<description><![CDATA[Dawno nic nie pisałem, bo przymierzam się nad większymi zmianami tutaj, ale wczoraj coś mnie natchnęło i przygotowałem mały teoretyczno-praktyczny poradnik inspirowany przez moją własną prezentację. 1. Prolog Jakiś czas temu tworzyłem anglojęzyczną prezentację o tytule &#8222;How to create a good enough website?&#8221;. Rzecz jasna starałem się tam przedstawić przede wszystkim kwestie teoretyczne i przygotowawcze [...]]]></description>
			<content:encoded><![CDATA[<p>Dawno nic nie pisałem, bo przymierzam się nad większymi zmianami tutaj, ale wczoraj coś mnie natchnęło i przygotowałem mały teoretyczno-praktyczny poradnik inspirowany przez moją własną prezentację.</p>
<p><span id="more-33"></span></p>
<h2>1. Prolog</h2>
<p>Jakiś czas temu tworzyłem anglojęzyczną prezentację o tytule <strong>&#8222;How to create a good enough website?&#8221;</strong>. Rzecz jasna starałem się tam przedstawić przede wszystkim kwestie teoretyczne i przygotowawcze do kreowania stron internetowych, bo jakakolwiek próba praktycznego przedstawienia tak szerokiego zagadnienia w formie ustnej zakończyłaby się za pewnie wielogodzinną przemową bez konkretnych wniosków. Nie mniej jednak zmotywowało mnie to do napisania &#8222;kilku&#8221; słów szerszego poradnika na temat <strong>&#8222;jak zacząć i o czym należy pamiętać tworząc strony internetowe&#8230;&#8221;</strong> &#8211; w formie głównie teoretycznej z aspektami praktycznymi&#8230;</p>
<h2>2. Czy na pewno tego chcesz?</h2>
<p>Jest to chyba podstawowe pytanie które powinien zadać sobie każdy przyszły, bądź początkujący programista. <strong>&#8222;Czy naprawdę masz na tyle czasu i motywacji, aby utworzyć swój pierwszy serwis?&#8221;</strong>. Pytanie jest o tyle ważne, że tworzenie jakichkolwiek mniej lub bardziej zawiłych, czy to programów, czy w naszym przypadku aplikacji internetowych jest procesem najczęściej skomplikowanym i długim. Przede wszystkim musisz myśleć abstrakcyjnie, co powoduje, że w wielu przypadkach niektóre aspekty codziennej logiki odchodzą w zapomnienie. Nie licz na to, że każdy z Twoich problemów rozwiążą inni. Z tym wiąże się fakt, że musisz posiadać choć małą nutkę umiejętności szukania i analizowania gotowych rozwiązań bądź porad. Jeśli spełniasz powyższe zasady, masz szansę że twoja zabawa z kodem nie zakończy się klęską.</p>
<h2>3. Rozpocznij projektowanie tradycjonalnie&#8230;</h2>
<p>No właśnie. Pomimo różnych zawiłości które już po części wymieniłem początek jakiejkolwiek większej pracy należy rozpoczynać tradycjonalnie, najlepiej podług uogólnionych zasad. Musisz wiedzieć, że:</p>
<ul>
<li>tradycjonalnie <em>nie znaczy</em> zawsze tak samo,</li>
<li>pamiętaj, że rola projektanta wiążę się z rolą programisty z podstawowych powodów:</li>
<li>
<ul>
<li>kreowanie strony której nie da się zaimplementować lub implementacja nie będzie działać poprawnie jest co najmniej niemądre,</li>
<li>w drugą stronę &#8211; zmieniane struktury projektu za każdym razem kiedy chcemy zmienić sposób działania strony będzie na pewno czasochłonne, a niekiedy nawet kosztowne.</li>
</ul>
</li>
<li>jeśli stronę miałoby tworzyć kilka osób należy zabrać pod uwagę przy projektowaniu możliwości każdego z programistów w celu jak najbardziej optymalnej implementacji przyszłego kodu.</li>
</ul>
<h2>4. Co z tym projektowaniem samym w sobie?</h2>
<p>I doszliśmy do pierwszego punktu nie stricte teoretycznego. Przed praktycznym pisaniem musisz wiedzieć czego chcesz i czego oczekujesz po swojej stronie. Można to nazwać <a target="_blank" href="http://pl.wikipedia.org/wiki/Architektura_informacji" title="Architektura informacji" ><strong>&#8222;architekturą informacji&#8221;</strong></a> twojej strony. Po fazie informacyjnej przedstawionej wyżej musisz stworzyć, przetestować i zweryfikować strukturę i możliwości swojej przyszłej strony. Mówiąc dokładniej, czy to na kartce, czy we własnej głowie przeanalizuj:</p>
<ul>
<li>jaki serwis chcesz tworzyć?</li>
<li>jaki ma być cel tego serwisu?</li>
<li>co chcesz wykorzystać przy jego tworzeniu i w jaki sposób chcesz to zaimplementować?</li>
<li>i co najważniejsze &#8211; do jakiej grupy osób ma być on kierowany i czy chcesz rozwijać jego możliwości poprzez np. wielojęzykowość?</li>
</ul>
<p>Jest to o tyle ważny punkt całego kreowania, że wybór którego dokonasz przy planowaniu będzie obowiązywał twoją stronę praktycznie do końca jej istnienia. Pamiętaj także, aby dokładnie testować wszystkie możliwe skutki swoich ewentualnych wyborów. W wypadku większych projektów pomocne będą tu znane od wielu lat&#8230; kartka i długopis :]</p>
<p>Kiedy już dojdziesz dokładnie do tego czego oczekujesz po swoim przyszłym tworze pora poznać kilka informacji o projektowaniu samym w sobie. Wiele osób nie mających zbytnio nic wspólnego z technologiami internetowymi może uważać, że tworzenie rozwiązań tego typu to rodzaj  tajemniczej sztuki. Nic mylnego moim zdaniem. To po prostu pewne predyspozycje do bycia kreatywnym. Dokładniej <strong>zbiór umownych reguł do rozwiązywania problemów w celu zrozumienia zapotrzebowań użytkowników i tworzenia efektywnych rozwiązań internetowych</strong>. Oczywiście definicja ta ma tylko zobrazować pojęcie i naginanie jej wg. indywidualnych potrzeb jest jak najbardziej zrozumiałe. Niekiedy z angielskiego <strong>&#8222;design&#8221;</strong> jest mylnie kojarzony tylko i wyłącznie z tworzeniem wizualnej części stron, a związane jest to z dominacją grafików internetowych (z angielskiego <strong>&#8222;graphic designers&#8221;</strong>).</p>
<p>Reasumując paragraf:</p>
<ul>
<li>nie należy zapominać o kooperacji z użytkownikami, którzy często są bardzo ważnym źródłem informacji i zapotrzebowań wśród ludzi,</li>
<li>należy zapoznać się z podstawowymi zasadami użyteczności stron i projektowania stron internetowych &#8211; wymienionych po części powyżej,</li>
<li>należy projektować jak już zostało wyżej podane abstrakcyjnie, ale nie <em>&#8222;na ślepo&#8221;, w jednym kierunku</em>. Tłumacząc bardziej: <em>kreuj swój serwis tak, jak sam chciałbyś go widzieć będąc swoim własnym klientem</em>.</li>
</ul>
<h2>5. Mam ułożony w głowie dokładny plan, ale za co mam się &#8222;chwycić&#8221; żeby go zaimplementować?</h2>
<p>Po pierwsze języki programowania, bądź związane z nimi technologie. Bez tego &#8211; to lepiej odpalić nowe <strong>CoD</strong> i czekać, aż osoby które się znają na tego typu sprawach zrobią coś za nas i dla nas. Prawda jest taka, że informatyka nie jest łatwą dziedziną, bo pomimo, że jest młoda daje ogromne pole do popisu i manewru, a to za sprawą ogromu na jaki się rozwinęła. I tak jest i przy tworzeniu aplikacji internetowych. <a target="_blank" href="http://pl.wikipedia.org/wiki/Html" title="Html" ><strong>HTML</strong></a> lub bardziej rygorystyczny <a target="_blank" href="http://pl.wikipedia.org/wiki/XHTML" title="xHTML" ><strong>xHTML</strong></a> i do tego <a target="_blank" href="http://pl.wikipedia.org/wiki/Kaskadowe_arkusze_styl%C3%B3w" title="CSS" ><strong>CSS (Kaskadowe arkusze stylów)</strong></a> w celu implementacji strony wizualnej i jej modyfikacji, a także <a target="_blank" href="http://pl.wikipedia.org/wiki/Php" title="PHP" ><strong>PHP</strong></a> i najbardziej znany tego typu system <a target="_blank" href="http://pl.wikipedia.org/wiki/Mysql" title="MySQL" ><strong>MySQL</strong></a> w celu utworzenia zdatnych do działania algorytmów, rozwiązań i przechowania danych to niestety w dzisiejszych czasach niezbędne minimum.</p>
<p>Nie będę omawiał tu każdego z tych języków. Bo w zasobach internetu można znaleźć wiele dobrych kursów, opracowań i przykładów. Wystarczy spojrzeć <a target="_blank" href="http://google.pl" title="Google.pl" ><strong>tutaj</strong></a>. Dodam tylko, że postaram się zawrzeć kilka informacji o pewnych aspektach tych języków. Jeśli ktoś już na początku chce mieć swobodną możliwość wyboru to przedstawiam poniżej kilka języków wraz z ich alternatywami (oczywiście są to tylko przykłady, względem własnych potrzeb można konfiguracje ułożyć inaczej):</p>
<p style="text-align: center;"><img class="iborder" src="http://images33.fotosik.pl/154/5191cd4ca892c1d4.png" alt="Screen" /></p>
<p>Osobiście będę starał się opierać na podanej wyżej konfiguracji podstawowej.</p>
<p>Do programowania w czymkolwiek przydałby się też jakiś edytor/program przeznaczony do tego typu zadań. A mianowicie:</p>
<ul>
<li>PHP, HTML, xHTML, MySQL w <em>Notepad++, ZendStudio (dla bardziej zaawansowanych &#8211; posiada kolorowanie składni, podpowiedzi, klienta ftp, cvs, debuger kodu, zarządzanie projektami, przeglądanie funkcji, klas, metod i wiele innych)</em>. Dodam tylko, że nie polecam np. Notatnika z racji dla przykładu problemów z kodowaniem, o czym później,</li>
<li>CSS w <em>Top Style Pro, Visual Studio .NET</em>, itp.,</li>
<li>JS/AJAX w <em>ZendStudio, Visual Studio .NET</em>.</li>
</ul>
<p>Oczywiście to nieliczne przykłady, najlepiej zacząć od testowania ich, albo ewentualnie innych tego typu programów w celu odnalezienia najbardziej przyjaznego samemu sobie.</p>
<h2>6. Czym jest te wspomniane kodowanie?</h2>
<p>Nieszczęsna rzecz. I w gotowych serwisach i w czasie pisania swoich skryptów. Sprawa jest jasna &#8211; <em>najlepiej jeśli kodowanie plików, <a target="_blank" href="http://www.w3.org/International/O-HTTP-charset.pl.php" >charset</a> dokumentu i kodowanie wraz z porównywanie znaków bazy danych są tego samego typu</em>. Dzięki temu powinniście uniknąć problemów z &#8222;krzakami&#8221; przy imporcie starej bazy, czy pisania dodatkowego kodu odpowiadającego za dynamiczne zmiany kodowania.</p>
<p>Jako polscy użytkownicy dla których domyślnie grupami odbiorców są właśnie także Polacy macie do wyboru dwa kodowania:</p>
<p style="text-align: center;"><img class="iborder" src="http://images33.fotosik.pl/155/e793dea04612399c.png" alt="Screen" /></p>
<p>Jest jeszcze trzecie &#8211; Microsoftowskie <em>windows-1250</em>, ale stanowczo odradzam używanie go. Do podanych powyżej pierwsze odnosić się będzie stricte tylko do polskich użytkowników. Drugie jest uniwersalne i doradzam używanie go, jeśli masz świadomość, że w pewnym momencie będą twoją stronę przeglądać użytkownicy z zagranicy. W nawiasie podałem odpowiedni w MySQL-u.</p>
<p>Teraz ciut praktyki. Czyli jak to wygląda kolejno w <strong>xHTML-u / HTML-u</strong>, kodowaniu plików (na podstawie <strong>ZendStudio</strong>) i MySQL-u (via <a target="_blank" href="http://pl.wikipedia.org/wiki/PhpMyAdmin" title="phpMyAdmin" ><strong>phpMyAdmin</strong></a>).</p>
<p>W pierwszym i drugim języku każda strona i podstrona musi posiadać nadany odpowiedni charset. My będziemy chcieli uzyskać dla przykładu <strong>utf-8</strong>. xHTML:</p>
<pre class="brush: xml; title: ;">&lt;meta http-equiv=&quot;content-type&quot; content=&quot;application/xhtml+xml; charset=utf-8&quot; /&gt;</pre>
<p>A także w postaci deklaracji języka dokumentu, dla przykładu w tagu <strong>html</strong>:</p>
<pre class="brush: xml; title: ;">&lt;html lang=&quot;pl&quot; xml:lang=&quot;pl&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;  // xhtml 1.0</pre>
<pre class="brush: xml; title: ;">&lt;html xml:lang=&quot;pl&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt; // xhtml 1.1</pre>
<p>Jeśli wewnątrz strony zmienia się język to w tagu nadrzędnym regionu ze zmienionym językiem możemy zadeklarować inny język:</p>
<pre class="brush: xml; title: ;">Witaj użytkowniku!
&lt;p lang=&quot;en&quot; xml:lang=&quot;en&quot;&gt;Welcome user!&lt;/p&gt;
// xhtml 1.0</pre>
<pre class="brush: xml; title: ;">Witaj użytkowniku!
&lt;p xml:lang=&quot;en&quot;&gt;Welcome user!&lt;/p&gt;
// xhtml 1.1</pre>
<p>Dla czystego <strong>HTML-a</strong> powyższe elementy będą wyglądać następująco:</p>
<pre class="brush: xml; title: ;">&lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html; charset=utf-8&quot;&gt;</pre>
<pre class="brush: xml; title: ;">&lt;html lang=&quot;pl&quot;&gt;</pre>
<pre class="brush: xml; title: ;">Witaj użytkowniku!
&lt;p lang=&quot;en&quot;&gt;Welcome user!&lt;/p&gt;
</pre>
<p>To nie wszystko. Taki plik należy odpowiednio otwierać i odpowiednio zapisywać. Rzecz jasna piszę tu o kodowaniu. Dla przykładu &#8211; zapis w <strong>ZendStudio</strong> w kodowaniu proporcjonalnym do <strong>charset-u</strong> powinien wyglądać tak:</p>
<p style="text-align: center;"><img class="iborder" src="http://images25.fotosik.pl/163/1bb52424c102ee9cmed.png" alt="Screen" /></p>
<p>No i ostatnia sprawa. Aspekt bazy danych. Jeśli już z niej korzystasz w celu składowania danych należy ustawić porównywanie znaków i kodowanie pól w <em>phpMyAdmin</em> na w naszym przypadku <strong>utf8_unicode_ci</strong>.</p>
<p style="text-align: center;"><img class="iborder" src="http://images24.fotosik.pl/165/b509b13e566c72bc.png" alt="Screen" /></p>
<p style="text-align: center;"><img class="iborder" src="http://images34.fotosik.pl/154/bee4f720882aeada.png" alt="Screen" /></p>
<p>Należy pamiętać aby zmiany w phpMyAdmin wprowadzić <em>przed</em> wgraniem czy to pustej czy starej bazy danych.</p>
<h2>7. Ponad zakresowy aspekt numer jeden: <em>bezpieczeństwo!</em></h2>
<p>Kolejny praktyczny aspekt, który za pewnie można znaleźć w sieci, ale czymże byłby ten tekst bez niego. Dla laików &#8211; kilka porad co może czychać na niezabezpieczony skrypt i jak można się przed tym uchronić.</p>
<ul>
<li><em>Cross-Site scripting (XSS)</em></li>
</ul>
<p><strong>Opis:</strong> &#8211; sposób ataku możliwy do przeprowadzenia przy braku odpowiedniego filtrowania danych pozwalający na wstrzyknięcie niepowołanego kodu najczęściej w postaci JavaScript.<br />
<strong>Przykład kodu niezabezpieczonego:</strong></p>
<pre class="brush: php; title: ;">$nick = $_GET['nick'];
print 'Zalogowany jako: ' . $nick;</pre>
<p><strong>Przykład użycia luki:</strong></p>
<pre class="brush: xml; title: ;">http://serwer.pl/index.php?nick=&quot;&gt;&lt;script&gt;alert(document.cookie);&lt;/script&gt;</pre>
<p><strong>Sposób zabezpieczenia:</strong> należy przefiltrować dane poprzez funkcje htmlspecialchars(), strip_tags() lub/i htmlenties(), a także dodatkowo można via np. <strong>str_replace()</strong> usuwać ze zmiennych takie ciągi jak <strong><script type="text/javascript"></script></strong>.</p>
<pre class="brush: php; title: ;">$nick = strip_tags(htmlspecialchars($_GET['nick']));</pre>
<ul>
<li><em>SQL Injection</em></li>
</ul>
<p><strong>Opis:</strong> &#8211; atak spowodowany nieodpowiednim typowaniem lub brakiem filtrowania pozwalający na modyfikację zapytania do SQL-a.<br />
<strong>Przykład kodu niezabezpieczonego:</strong></p>
<pre class="brush: php; title: ;">$nick = $_GET['nick'];
mysql_query('SELECT id FROM users WHERE nick = ' . $nick);</pre>
<p><strong>Przykład użycia luki:</strong></p>
<pre class="brush: xml; title: ;">http://serwer.pl/index.php?nick=-1%20union%20select%201,2,3,4,5,6,7/*</pre>
<p><strong>Sposób zabezpieczenia:</strong> należy przefiltrować dane poprzez <strong>mysql_real_escape_string()</strong> wraz z sprawdzeniem, czy <em>magic_quotes</em> na serwerze jest włączony.</p>
<pre class="brush: php; title: ;">$nick = $_GET['nick'];
if ( ini_get('magic_quotes_gpc') )
{
	$nick = stripslashes($nick);
}
$nick = mysql_real_escape_string($nick);</pre>
<ul>
<li><em>Cross-site request forgery (XSRF)</em></li>
</ul>
<p><strong>Opis:</strong> &#8211; polega na wykorzystaniu uprawnień osoby administrującej w celu wykonania zadanego kodu.<br />
<strong>Przykład użycia luki:</strong></p>
<pre class="brush: xml; title: ;">[url]http://serwer.pl/index.php?mode=delete&amp;u=2[/url]</pre>
<p><strong>Sposób zabezpieczenia:</strong> należy przede wszystkim dokładnie sprawdzać co się otwiera. Nie zawsze, czy to grafiki, czy to odnośniki są tym czym mogą się wydawać. Po za tym należy przesyłać ważne dane np. w PA poprzez $_POST, a nie $_GET. Można ograniczyć przesyłanie linków w BBCode związanych z najważniejszymi funkcjami danego serwisu. Ostatecznie można też uniemożliwić używanie BBCode w serwisie.</p>
<ul>
<li><em>Session fixation</em></li>
</ul>
<p><strong>Opis:</strong> &#8211; polega na przejęciu konta użytkownika poprzez poznanie jego id sesji.<br />
<strong>Przykład użycia luki:</strong> &#8211; najczęściej poprzez przekazywanie id sesji poprzez adres i nieodpowiednie sprawdzanie go w czasie ładowania strony.</p>
<pre class="brush: xml; title: ;">http://www.mojadomena.pl/index.php?PHPSESSID=987654321</pre>
<p><strong>Sposób zabezpieczenia:</strong> należy regenerować id sesji w taki sposób, aby użytkownik nie mógł nadać sobie samemu jej id. Dodatkowo powinno się sprawdzać nadane id sesji, wraz z danymi użytkownika z tymi które istnieją w bazie danych.</p>
<pre class="brush: php; title: ;">session_start();
if (!isset($_SESSION['nick']))
{
	session_destroy();
}
session_regenerate_id();
$_SESSION['nick'] = true;</pre>
<ul>
<li><em>Local File Include / Remote File Include</em></li>
</ul>
<p><strong>Opis:</strong> &#8211; pozwala na załączenie (via np. include, require, readfile, fopen) niepożądanego pliku na serwerze lub zdalnie z poza serwera skryptu.<br />
<strong>Przykład niezabezpieczonego kodu:</strong></p>
<pre class="brush: php; title: ;">require 'include/' . $_GET['id'];</pre>
<p><strong>Przykład użycia luki:</strong> &#8211; <em>%00</em> na końcu to tzw. atak z użyciem zerowego bajtu (<em>Poison Null Byte</em>) przez co po zaincludeowaniu ścieżki wszystko co po za zmienną zostanie usunięte (pozbywamy się np. rozszerzenia html z include wpisanego na stałe).</p>
<pre class="brush: xml; title: ;">http://serwer.pl/index.php?id=./../../../../../../../../../etc/passwd%00</pre>
<p><strong>Sposób zabezpieczenia:</strong> można filtrować dane podług danych znaków i te wejściowe, ale także niektóre z $_SERVER. Po za tym należy wyłączyć możliwość zdalnego odwoływania się do plików z poza serwera przez readfile, include czy fopen.</p>
<p>Pamiętaj także, że w każdym z wyżej wymienionych przypadków jeśli masz pewność, że przekazywane dane będą wartością np. całkowitą najlepiej jest wykonać rzutowanie lub przefiltrować odpowiednią funkcją je. Dla przykładu:</p>
<pre class="brush: php; title: ;">$id = intval($_GET['id']); // funkcja</pre>
<pre class="brush: php; title: ;">$id = (int)$_GET['id']; // rzutowanie</pre>
<h2>8. Ponad zakresowy aspekt numer dwa: <em>podstawy optymalizacji!</em></h2>
<p>Punkt ten możesz brać z malutkim przymrużeniem oka ;] Optymalizacja jest ważna, ale bardziej w formie zaprojektowania serwisu i bazy danych (szczególnie korelacji pół i tabel, a także optymalizacji zapytań) niż aspektów stricte semantycznych które podam. O to kilka testów wraz z wynikami w komentarzach (w postaci prędkości):</p>
<pre class="brush: php; title: ;">// długość zmiennej
if ( strlen($try) &lt; 5000000 ) { echo 0; }
// kontra
if ( !isset($try{5000000}) ) { echo 0; }

// wynik:
// 1: 0.000046
// 2: 0.000012
// powodem jest fakt, że isset() jest konstrukcją językową, a strlen() funkcją</pre>
<pre class="brush: php; title: ;">// pre / post-inkrementacja
for ( $i = 0; $i &lt; 150000; ++$i )
{
print null;
}
// kontra
for ( $j = 0; $j &lt; 150000; $j++ )
{
print null;
}
// wynik:
// 1: 0.0441
// 2: 0.0489
// wszędzie po za parametrami warunków należy używać pre-inkrementacji, gdyż nie jest wtedy tworzona dodatkowa przejściowa zmienna przez co czas użycia jest krótszy.</pre>
<pre class="brush: php; title: ;">// przeliczanie funkcji w pętlach
for ( $i = 0; $i &lt; sizeof($tablica); ++$i )
{
print null;
}
// kontra
$size = sizeof($tablica);
for ( $j = 0; $j &lt; $size; ++$j )
{
print null;
}
// wynik:
// 1: 0.00066
// 2: 0.00031
// w pierwszym przypadku przy każdej iteracji pętli zostaje obliczany wynik funkcji sizeof co wydłuża działanie skryptu</pre>
<pre class="brush: php; title: ;">// wyrażenia regularne i funkcje wbudowane
if (preg_match(&quot;!^foo_!i&quot;, &quot;FoO_&quot;)) { }
// kontra
if (!strncasecmp(&quot;foo_&quot;, &quot;FoO_&quot;, 4)) { }
// wynik:
// 1: 0.0000791
// 2: 0.0000119
// wyrażenia regularne w wypadku prostych zastosowań są o wiele wolniejsze niż przeznaczone do tego funkcje wbudowane</pre>
<p>To tylko niektóre proste testy, ukazujące z pozoru nieznaczne, ale przy nagromadzeniu i większych projektach uciążliwe różnice w wydajności. Więcej informacji już niedługo będzie można uzyskać na moim blogu.</p>
<h2>9. A może kilka słów jak projektować bazy?</h2>
<p>Nie, nie uzyskasz krok po kroku porad jak utworzyć uniwersalną bazę. Nie ma chyba takich poradników. To zależy od tego czego potrzebujesz. Mogę jednak wypunktować główne założenia:</p>
<ul>
<li>jeśli masz zamiar wykonywać bardzo dużo operacji w jednoczesnym czasie to proponuje wybrać typ <strong>InnoDB</strong>. Jest on ciut wolniejszy, ale za to posiada takie dodatkowe aspekty jak transakcje co lepiej organizuje i zabezpiecza działanie na bazie (w przeciwieństwie do MyISAM),</li>
<li>każda tabela powinna posiadać indeks, a w wypadku kiedy istnieje możliwość, że tabela rozrośnie się do dużych rozmiarów korzystaj z unikalnych indeksów na danych kolumnach co zwiększy wydajność przeszukiwania tabeli. Musisz pamiętać jednak, że przed umieszczeniem wpisu w tabeli musisz sprawdzić, czy nie istnieje już czasami wartość tego typu w kolumnie o nadanym unikalnym indeksie. W przeciwnym razie wystąpi błąd przy wykonaniu zapytania.
<pre class="brush: sql; title: ;">ALTER TABLE nazwa_tabeli ADD UNIQUE INDEX (nazwa_kolumny);</pre>
</li>
<li>spróbuj przewidywać długość typów pól. Jeśli zapisujesz w czystej postaci IP to nie twórz typu <strong>VARCHAR</strong> większego niż <strong>15ście</strong> znaków. Jeśli zapisujesz zahashowane w md5 hasło to ustaw pole na <strong>32 znaki</strong>. Nadmiary wielkości nie są potrzebne w bazie i odbijają się na wydajności.</li>
<li>konstruuj tabelę tak, abyś mógł bez problemu przeszukiwać ją po ID. Kardynalnym błędem w zautomatyzowanych serwisach jest działanie np. na nickach użytkowników. Wyjątkiem są specyficzne serwisy, gdzie jedno pole danej tabeli nie oddziałuje na drugie. W przeciwnym wypadku działanie na ID jest bardzo dobrą metodą na łączenie danych z kilku tabel.
<pre class="brush: sql; title: ;">SELECT u.username, g.groups FROM groups g LEFT JOIN users u ON g.user_id = u.user_id WHERE g.group_id = 1;</pre>
</li>
<li>jeśli masz możliwość używaj modułu <strong>mysqli</strong> &#8211; jest wydajniejszy już z samej konstrukcji.</li>
</ul>
<h2>10. Co, gdy mój serwis już stoi?</h2>
<p>Teraz możesz się przygotować na kilka tygodni, miesięcy bardziej równomiernej pracy nad promocją serwisu. O ile naprawdę chcesz go promować. Twój kod za pewnie standardowo nie jest przystosowany dla wyszukiwarek. Więc pierwsze co możesz zrobić to pomajsterkować z <strong>mod_rewrite</strong> i przepisywaniem linków na bardziej przyjazne wyszukiwarką.</p>
<pre class="brush: xml; title: ;">RewriteEngine on
RewriteRule ^([a-z0-9]+)$ /index.php?action=$1 [L,QSA,NE,NC]</pre>
<p>Po za tym zainteresuj się systemami linkującymi i pozycyjnymi &#8211; szczególnie tymi o wysokim <a target="_blank" href="http://pl.wikipedia.org/wiki/PageRank" title="PageRank" ><strong>PageRank-u</strong></a>. Po jakimś czasie pomoże się wybić to twojej stronie na wyższe pozycje pod danymi frazami. Możesz wspomóc się automatycznymi skryptami do dodawania, ale one najczęściej dodają przede wszystkim do stron o niskim pożytku dla nas samych.</p>
<p>Ważnym i przyciągającym najwięcej klientów, czy użytkowników (a co zarazem idzie rozwijającym serwis i zwiększającym PR) są systemy typu AdWords. Płatnie wyświetlają one reklamy w google po wpisaniu zadanych fraz przez użytkownika w wyszukiwarce. Przewidywalnie za około 100 złotych może twoją stronę odwiedzić kilkadziesiąt tysięcy użytkowników (praktycznie jest to tyle kliknięć).</p>
<p>No i ostatecznie najważniejszym aspektem jest twoja opieka nad stroną. Jej rozwój, zmiana treści, , zachowanie poprawności (sprawdzanej np. <a target="_blank" href="http://validator.w3.org/" title="Validator W3" >tutaj</a>), udoskonalanie pod oczekiwania użytkowników czemu pomóc może np. system analityczny Google Analytics &#8211; to wszystko pozwoli Ci maksymalnie rozwinąć twój serwis internetowy.</p>
]]></content:encoded>
			<wfw:commentRss>http://m1chu.eu/2008/02/24/10-punktow-podstaw-teoretycznego-tworzenia-stron/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

