<?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>SAGES:: Blog</title>
	<atom:link href="http://blog.sages.com.pl/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.sages.com.pl</link>
	<description>Blog konsultantów firmy Sages - artykuły o technologiach Java EE i pokrewnych</description>
	<lastBuildDate>Sun, 25 Jul 2010 18:54:44 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Zapisywanie hasła użytkownika w przeglądarce w aplikacjach GWT</title>
		<link>http://blog.sages.com.pl/2010/07/zapisywanie-hasla-uzytkownika-w-przegladarce-w-aplikacjach-gwt/</link>
		<comments>http://blog.sages.com.pl/2010/07/zapisywanie-hasla-uzytkownika-w-przegladarce-w-aplikacjach-gwt/#comments</comments>
		<pubDate>Sat, 24 Jul 2010 14:08:55 +0000</pubDate>
		<dc:creator>Łukasz Kobyliński</dc:creator>
				<category><![CDATA[Problemy i rozwiązania]]></category>
		<category><![CDATA[gwt]]></category>
		<category><![CDATA[uwierzytelnianie]]></category>

		<guid isPermaLink="false">http://blog.sages.com.pl/?p=82</guid>
		<description><![CDATA[Identyfikacja użytkownika jest niezbędnym elementem prawie każdej aplikacji dostępnej zdalnie. Najczęściej identyfikacji dokonujemy poprzez weryfikację dostarczonej przez użytkownika kombinacji login / hasło. Po uwierzytelnieniu umożliwiamy użytkownikowi korzystanie z całości lub części funkcjonalności aplikacji. Jak w takim razie najlepiej zrealizować proces identyfikacji i uwierzytelnienia w GWT?

Uwierzytelnianie a aplikacja GWT
Przypomnijmy sobie najpierw jak przebiega typowy proces uwierzytelnienia [...]]]></description>
			<content:encoded><![CDATA[<p>Identyfikacja użytkownika jest niezbędnym elementem prawie każdej aplikacji dostępnej zdalnie. Najczęściej identyfikacji dokonujemy poprzez weryfikację dostarczonej przez użytkownika kombinacji login / hasło. Po uwierzytelnieniu umożliwiamy użytkownikowi korzystanie z całości lub części funkcjonalności aplikacji. Jak w takim razie najlepiej zrealizować proces identyfikacji i uwierzytelnienia w GWT?<br />
<span id="more-82"></span></p>
<h2>Uwierzytelnianie a aplikacja GWT</h2>
<p>Przypomnijmy sobie najpierw jak przebiega typowy proces uwierzytelnienia i jakie role w nim występują. Po pierwsze, mamy użytkownika, który wypełnia pola interfejsu aplikacji, przeznaczone na wprowadzenie loginu i hasła. Po drugie, mamy procedurę weryfikacji tych danych, najprawdopodobniej odpytującą warstwę trwałości &#8211; a więc pewną bazę &#8211; w celu skonfrontowania wartości dostarczonych przez użytkownika z tymi, przyjmowanymi za prawidłowe. Hasła użytkowników powinny być przechowywane w warstwie trwałości w postaci zaszyfrowanej, aby nie ujawnić prawdziwego hasła nawet w przypadku wycieku danych z bazy. W tym celu możemy skorzystać z jednej z bibliotek Java do szyfrowania (np. <a href="http://www.mindrot.org/projects/jBCrypt/" target="_blank">jBCrypt</a>) lub szyfrowania realizowanego przez bazę danych.</p>
<p>Specyfiką aplikacji napisanej w GWT jest działanie interfejsu całkowicie po stronie klienta, pod kontrolą przeglądarki internetowej. Informacje podane przez użytkownika muszą zatem być przekazane przez sieć, do zdalnego serwera, gdzie nastąpi weryfikacja. Czy proces uwierzytelnienia mógłby być przeprowadzony w aplikacji klienckiej? Oczywiście nie, bo kod działający lokalnie w przeglądarce użytkownika można łatwo zmodyfikować i obejść jakiekolwiek zabezpieczenia, pomijając sprawdzenie poprawności danych.</p>
<p>Pierwsze pytanie jakie rodzi nam się w głowie, to w jaki sposób zabezpieczyć dane przesyłane przez sieć. Wysyłamy kombinację login i hasło, czy nie powinny być one w jakiś sposób zaszyfrowane? Nie jest to niestety proste, bo jak pamiętamy, nasza aplikacja po kompilacji staje się kodem JavaScriptowym, działającym w przeglądarce internetowej. Działamy więc w dużo bardziej ograniczonym środowisku &#8211; ze względu na język i platformę uruchomieniową &#8211; niż, chociażby aplikacje standalone w Javie. W szczególności, mamy zawężone możliwości wykonywania bardziej skomplikowanych operacji matematycznych, a co za tym idzie większość bibliotek kryptograficznych jest bezużyteczna w aplikacjach GWT. Poza tym należy pamiętać, że zaszyfrowanie hasła po stronie klienta i przesłanie go otwartym kanałem niewiele nam daje. Po pierwsze, kod kliencki jest całkowicie modyfikowalny, więc algorytm szyfrowania można zmienić, usunąć lub zasymulować we własnej aplikacji atakującego. Po drugie, podsłuchanie zaszyfrowanego hasła jest tak samo wartościowe dla atakującego, co podsłuchanie hasła niezaszyfrowanego &#8211; w pierwszym przypadku serwer weryfikuje przecież poprawność na podstawie wartości zaszyfrowanej.</p>
<p>Cóż nam pozostaje? Możemy pozostać przy przesyłaniu hasła otwartym tekstem do serwera (pamiętając jednak o szyfrowaniu w bazie), możemy wykorzystać komunikację SSL, ażeby uniemożliwić podsłuch lub &#8211; w końcu &#8211; możemy całkowicie zrezygnować z uwierzytelniania w aplikacji GWT i przeprowadzić ją bezpośrednio na serwerze. Aplikacja GWT zostanie załadowana dopiero po uwierzytelnieniu i przyjmować będzie, że zawsze działa w kontekście pewnego uwierzytelnionego użytkownika.</p>
<h2>Formularz logowania a automatyczne zapisywanie hasła</h2>
<p>W tym artykule nie będę się zajmował szczegółowo kwestiami bezpieczeństwa, chciałbym jednak zwrócić uwagę na mechanizm, który od pewnego czasu bardzo ułatwia nam życie jako użytkownikom systemów. Mianowicie każda chyba z istniejących obecnie przeglądarek umożliwia zapisywanie hasła po pierwszym wpisaniu ich w odpowiednie pola aplikacji internetowej i automatyczne wypełnianie przy każdym kolejnym włączeniu owej aplikacji. Skąd przeglądarka wie, że pola tekstowe, które widnieją na stronie służą do &#8220;zalogowania się&#8221; użytkownika? Reguły nieco różnią się pomiędzy przeglądarkami, ale generalnie na stronie musi znajdować się pole tekstowe i pole typu password w formularzu z przyciskiem &#8220;submit&#8221;, a formularz ten nie może być generowany dynamicznie przez JavaScript (musi istnieć w oryginalnym HTMLu). Pamiętając, że aplikacja GWT tworzy swój interfejs po stronie klienta dynamicznie, poprzez modyfikację dokumentu za pomocą JavaScriptu, widzimy od razu, że w przypadku formularza zrealizowanego w GWT automatyczne zapisywanie hasła nie zadziała.</p>
<p>Co możemy zatem zrobić? Chciałbym przedstawić trzy rozwiązania, które różnią się rezultatem, a wybór jednego z nich powinien nastąpić po zastanowieniu się, czy najbardziej zależy nam na kompatybilności ze wszystkimi przeglądarkami, czy na łatwości oprogramowania procesu uwierzytelnienia, czy też na możliwości utworzenia formularza z kodu GWT.</p>
<h2>Podejście pierwsze</h2>
<p>W skrócie pomysł można opisać następująco: na stronie, będącej kontenerem naszej aplikacji tworzymy ukryty formularz do wpisywania loginu i hasła przez użytkowników (w statycznym HTMLu), natomiast w kodzie GWT pokazujemy ów formularz wtedy, kiedy jest potrzebny i niskopoziomowymi instrukcjami, umożliwiającymi dostęp do elementów HTML pobieramy dane z pól tekstowych i przesyłamy na serwer. Po odebraniu i pozytywnym zinterpretowaniu odpowiedzi ukrywamy formularz i umożliwiamy dalsze działanie aplikacji. Podejście takie zaproponowane zostało w oficjalnym <a href="http://code.google.com/p/google-web-toolkit-incubator/wiki/LoginSecurityFAQ" target="_blank">FAQu GWT</a> i można zaimplementować je w następujący sposób (MyApp.html):</p>
<pre>&lt;div id="hiddenDiv" style="visibility:hidden"&gt;
 &lt;form id="loginForm" action="/myApp/login"&gt;
  &lt;h1&gt;Proszę się zalogować&lt;/h1&gt;
  &lt;center&gt;
  &lt;table&gt;
   &lt;tr&gt;&lt;td&gt;Login:&lt;/td&gt;
    &lt;td&gt;&lt;input type="text" id="login" /&gt;&lt;/td&gt;&lt;/tr&gt;
   &lt;tr&gt;&lt;td&gt;Hasło:&lt;/td&gt;
    &lt;td&gt;&lt;input type="password" id="password" /&gt;&lt;/td&gt;&lt;/tr&gt;
   &lt;tr&gt;&lt;td&gt;&lt;input type="submit" id="submit" /&gt;&lt;/td&gt;
    &lt;td&gt;&lt;input type="reset" /&gt;&lt;/td&gt;&lt;/tr&gt;
  &lt;/table&gt;&lt;/center&gt;
 &lt;/form&gt;
&lt;/div&gt;
</pre>
<p>W kodzie aplikacji, z formularza skorzystalibyśmy następująco (opakowujemy formularz HTML w komponent GWT i sygnalizujemy, że chcemy, żeby akcja formularza była wykonana w iframie zagnieżdżonym na stronie &#8211; drugi argument metody FormPanel.wrap jest ustawiony na true):</p>
<pre>Element formElement = Document.get().getElementById("loginForm");
if (formElement != null) {
 Element formDiv = Document.get().getElementById("hiddenDiv");
 if (formDiv != null) {
  formDiv.setAttribute("style", "visibility:visible");
 }
 FormPanel form = FormPanel.wrap(formElement, true);
 form.addSubmitCompleteHandler(new SubmitCompleteHandler() {
  @Override
  public void onSubmitComplete(SubmitCompleteEvent event) {
   zinterpretuj(event.getResults());
  }
 });
}</pre>
<p>W tym podejściu na serwerze musimy wygenerować odpowiedź w postaci tekstowej (Content-Type: text/html) &#8211; np. JSON i zinterpretować po stronie klienta, w celu podjęcia decyzji, czy dopuścić użytkownika do dalszej części aplikacji. Można do formularza również dodać słuchacza SubmitHandler, w którym zaimplementujemy wstępną walidację wprowadzonych wartości przed wysłaniem na serwer.</p>
<h2>Drugie podejście</h2>
<p>W tym podejściu chcemy wykorzystać standardową dla GWT komunikację z serwerem na zasadzie zdalnego wywoływania metod, a nie samodzielnego generowania odpowiedzi na serwerze w postaci tekstowej. Rozwiązanie, zaproponowane<a href="http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/2b2ce0b6aaa82461" target="_blank"> w tym wątku</a>, opiera się pomyśle, aby akcją wykonywaną przy wysłaniu formularza nie było fizyczne przesłanie danych na serwer, a wywołanie pośredniej metody JavaScriptowej, która dopiero zawoła zdalną metodę, tak jak zwykle robi się to w GWT. Nasz plik .html wygląda analogicznie jak poprzednio, z różnicą dotyczącą domyślnej akcji po wysłaniu formularza:</p>
<pre>&lt;div id="hiddenDiv" style="visibility:hidden"&gt;
 &lt;form id="loginForm" action="javascript:__gwt_login()"&gt;
 ...
</pre>
<p>Natomiast w kodzie wykonujemy następujące operacje (tym razem formularz nie musi znajdować się w zagnieżdżonym iframie):</p>
<pre>injectLoginFunction(this);
Element formElement = Document.get().getElementById("loginForm");
if (formElement != null) {
 Element formDiv = Document.get().getElementById("hiddenDiv");
 if (formDiv != null) {
  formDiv.setAttribute("style", "visibility:visible");
 }
 FormPanel form = FormPanel.wrap(formElement, false);
}
</pre>
<p>Metoda injectLoginFunction dodaje do strony funkcję JavaScriptową, która pośredniczy w wywołaniu RPC:</p>
<pre>private native void injectLoginFunction(LoginViewImpl inst) /*-{
 $wnd.__gwt_login = function() {
  inst.@pl.com.sages.client.view.LoginViewImpl::doLogin()();
 }
}-*/;

public void doLogin() {
 loginValue = InputElement.as(Document.get()
  .getElementById("login")).getValue();
 passwordValue = InputElement.as(Document.get()
  .getElementById("password")).getValue();
 rpcService.verifyUser(login, password, new AsyncCallback&lt; ...
 // zdalne wywołanie metody na serwerze i rejestracja słuchacza,
 // interpretującego odpowiedź
}
</pre>
<p>To podejście nie działa niestety w przeglądarkach opartych na WebKicie, np. w Chrome.</p>
<h2>Trzecie podejście</h2>
<p>Ten sposób rozwiązania formularza do identyfikacji użytkownika został zaproponowany <a href="http://raibledesigns.com/rd/entry/browser_based_username_password_autocomplete" target="_blank">na blogu Matta Raible</a>. Założenie było takie, żeby wykorzystać komponenty GWT jakie pola do wpisywania loginu i hasła (również z zewnętrznych bibliotek &#8211; np. GXT), a nie poprzestawać na standardowych kontrolkach HTML. W tym wypadku akcją przypisaną do ukrytego formularza HTML musi być prawidłowy URL, np. /myApp/login, inaczej (przynajmniej w moich testach) zapamiętywania hasła nie działa. W naszym kodzie przepisujemy wartości podane przez użytkownika (wprowadzone do naszych komponentów GWT) do owego ukrytego formularza:</p>
<pre>Document.get().getElementById("login").setAttribute("value",
 loginField.getValue());
Document.get().getElementById("password").setAttribute("value",
 passwordField.getValue());

Element formElement = Document.get().getElementById("loginForm");
FormPanel form = FormPanel.wrap(formElement, true);
form.submit();
</pre>
<p>Wysłanie formularza powoduje pojawienie się pytania o zgodę na zapisania hasła w przeglądarce. Pozostaje jeszcze kwestia przepisania wartości pól z automatycznie wypełnionego przez przeglądarkę formularza HTML na nasze komponenty GWT. Matt Raible proponuje wykorzystanie klasy Timer, żeby opóźnić przepisanie wartości pól, aż będą one dostępne dla skryptu, ale w moich testach nie było to potrzebne:</p>
<pre>
loginField.setValue(((InputElement)Document.get()
 .getElementById("login")).getValue());
passwordField.setValue(((InputElement)Document.get()
 .getElementById("password")).getValue());
</pre>
<p>Również i to podejście nie działa niestety w Chrome.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sages.com.pl/2010/07/zapisywanie-hasla-uzytkownika-w-przegladarce-w-aplikacjach-gwt/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Błąd Buffer Overflow&#8230; w szczegółach</title>
		<link>http://blog.sages.com.pl/2010/07/blad-buffer-overflow-w-szczegolach/</link>
		<comments>http://blog.sages.com.pl/2010/07/blad-buffer-overflow-w-szczegolach/#comments</comments>
		<pubDate>Sun, 11 Jul 2010 12:52:58 +0000</pubDate>
		<dc:creator>Krzysztof Cabaj</dc:creator>
				<category><![CDATA[Problemy i rozwiązania]]></category>
		<category><![CDATA[bezpieczeństwo]]></category>
		<category><![CDATA[buffer overflow]]></category>

		<guid isPermaLink="false">http://blog.sages.com.pl/?p=68</guid>
		<description><![CDATA[Chyba każda osoba interesująca się sieciami komputerowymi i ich bezpieczeństwem zetknęła się z atakiem typu „Buffer overflow”. Idea działania większości też jest znana, jednak kojarzy się z jakimiś niesamowitymi sztuczkami. W tym artykule chcę przybliżyć jak w szczegółach wygląda podatny kod oraz jak trzeba spreparować specjalny ciąg wejściowy aby doprowadzić to ataku.
Rozpatrzmy prostą funkcję zaprezentowaną [...]]]></description>
			<content:encoded><![CDATA[<p>Chyba każda osoba interesująca się sieciami komputerowymi i ich bezpieczeństwem zetknęła się z atakiem typu „Buffer overflow”. Idea działania większości też jest znana, jednak kojarzy się z jakimiś niesamowitymi sztuczkami. W tym artykule chcę przybliżyć jak w szczegółach wygląda podatny kod oraz jak trzeba spreparować specjalny ciąg wejściowy aby doprowadzić to ataku.<span id="more-68"></span></p>
<p>Rozpatrzmy prostą funkcję zaprezentowaną poniżej:</p>
<pre>void VulnFunc(char *ptr)
{
  char buffer[32];
  int i;

  for(i=0;(*ptr)!='&amp;';i++)
    buffer[i]=*(ptr++);

  //dalsze instrukcje działające na buforze, nie zmieniające jego
  //zawartości, przykładowo liczące wartość skrótu MD5 z zawartości
  //bufora, nieistotne jeśli chodzi o atak buffer overflow
}
</pre>
<p>Możemy łatwo wyobrazić sobie i podać zadanie tej funkcji, która pobiera pewne dane, kopiuje je do bufora oraz wykonuje z jego pomocą pewne operacje. Łatwo możemy wskazać widoczny od razu błąd: dane kopiujemy do bufora bez sprawdzania czy go nie przepełnimy. Warunkiem zakończenia kopiowania jest natrafienie na znak &amp;. Właśnie taki błąd, o ile możemy podać dowolne dane wejściowe może zostać wykorzystany.</p>
<p>Nim przejdziemy do preparowania odpowiedniego ciągu znaków będących exploitem obejrzyjmy stos podczas normalnego wywołania funkcji, zaprezentowany na rysunku nr 1.</p>
<div id="attachment_77" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.sages.com.pl/wp-content/uploads/2010/07/rys1.png"><img class="size-medium wp-image-77" title="Stos podczas normalnego wywołania funkcji" src="http://blog.sages.com.pl/wp-content/uploads/2010/07/rys1-300x107.png" alt="Stos podczas normalnego wywołania funkcji" width="300" height="107" /></a><p class="wp-caption-text">Stos podczas normalnego wywołania funkcji</p></div>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } -->W lewej części rysunku przedstawiona jest zawartość pamięci wykorzystywanej na stos. Wyróżnione na czerwono obszary to zmienna i oraz bufor, z przepisaną zawartością, w tym przypadku tekstem „Ala”. Wyróżniony na niebiesko fragment to wskaźnik ptr przekazywany jako parametr. Wszystkie te informacje można skonfrontować z zawartością zmiennych podczas wykonania programu zaprezentowanych po prawej stronie rysunku.</p>
<p>Wróćmy na chwilę do wyróżnionego na niebiesko adresu, będącego parametrem. W takim przypadku nasze zainteresowanie powinny dotyczyć 32 bitowy adres znajdujący się bezpośrednio przed nim. Adres ten, jest adresem powrotu z funkcji. Jeśli uda nam się go zmienić, w momencie powrotu z funkcji … wykonamy inny kod, nieprzewidziany przez programistę.</p>
<p>Aby prześledzić taki przypadek rozpatrzmy sytuację, kiedy do funkcji zostanie podany ciąg znaków postaci (liczby hexadecymalne):</p>
<p><code><br />
0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x83,0xec,0x80,0xbe,<br />
0xe6,0x10,0x41,0x00,0x68,0x41,0x6c,0x61,0x00,0x8b,0xdc,0x6a,0x40,0x53,<br />
0x53,0x33,0xc0,0x50,0xff,0xd6,0xff,0x15,0xe6,0x6c,0x41,0x00,0x68,<br />
0xfd,0x12,0x00,0x26</code></p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } -->… i podobnie jak w poprzednim wywołaniu funkcji prześledźmy zawartość stosu.</p>
<div id="attachment_78" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.sages.com.pl/wp-content/uploads/2010/07/rys2.png"><img class="size-medium wp-image-78" title="Stos przy próbie ataku" src="http://blog.sages.com.pl/wp-content/uploads/2010/07/rys2-300x102.png" alt="Stos przy próbie ataku" width="300" height="102" /></a><p class="wp-caption-text">Stos przy próbie ataku</p></div>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } -->Na zaprezentowanym rysunku możemy zaobserwować, że do bufora zostało przepisane dużo więcej danych niż poprzednio. Dokładnie 44 bajty (wartość hexadecymalna 2c w obszarze pamięci zmiennej i). Wartość ta jest większa niż zadeklarowany na 32 bajty bufor. Jednak co najważniejsze, czy &#8211; powinniśmy powiedzieć &#8211; najgroźniejsze, został nadpisany adres powrotu z funkcji (fragment wyróżniony na niebiesko), gdzie teraz znajdują się instrukcje które zostaną wykonane przez program. Pamiętając iż zmienne na maszynach z procesorem Intel są przechowywane „od końca” możemy odkodować adres powrotu. Skok nastąpi do instrukcji znajdującej się pod adresem 0&#215;0012fd68. Jeśli przyjrzymy się dokładnie obszarowi pamięci stosu, możemy zauważyć, że skoczymy w zaprezentowany na rysunku obszar pamięci. Dokładniej wykonanie nowego kodu zaczniemy od instrukcji o kodzie 0&#215;90. Dalej możemy zaobserwować jeszcze kilka instrukcji o tym samym kodzie. Taka sekwencja jest często spotykana w exploitach. Kod 0&#215;90 ma instrukcja NOP – no operation. Instrukcja która nic nie robi, ale ma długość jednego bajta. Taka sekwencją zwana poduszeczką NOP ( jest przydatna, jeśli okazało by się, iż nie trafimy dokładnie w początek kodu exploita). Dalszą analizę tego kodu zostawmy na później … i zobaczmy co się stanie jak wykonamy ten kod. Wyniki przedstawia rysunek.</p>
<div id="attachment_79" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.sages.com.pl/wp-content/uploads/2010/07/rys3.png"><img class="size-medium wp-image-79" title="Wykonanie kodu" src="http://blog.sages.com.pl/wp-content/uploads/2010/07/rys3-300x154.png" alt="Wykonanie kodu" width="300" height="154" /></a><p class="wp-caption-text">Wykonanie kodu</p></div>
<h3>Co robi kod na stosie … trochę assemblera</h3>
<p>Znaczenie instrukcji o kodzie 0&#215;90 zostało już wyjaśnione wcześniej, teraz przyjrzyjmy się reszcie kodu.<br />
. . .</p>
<table>
<tbody>
<tr>
<td>0&#215;90</td>
<td>nop</td>
</tr>
<tr>
<td>0&#215;83,0xec, 0&#215;80,</td>
<td>sub esp,0&#215;80//zrobienie miejsca na stosie</td>
</tr>
<tr>
<td>0xbe,0xe6, 0&#215;10, 0&#215;41, 0&#215;00</td>
<td>mov esi, &lt;adres&gt;</td>
</tr>
<tr>
<td>0&#215;68,0&#215;41, 0&#215;6c, 0&#215;61, 0&#215;00</td>
<td>push &lt;dane&gt;</td>
</tr>
<tr>
<td>0&#215;8b,0xdc</td>
<td>mov ebx,esp</td>
</tr>
<tr>
<td>0&#215;6a,0&#215;40</td>
<td>push 0&#215;40</td>
</tr>
<tr>
<td>0&#215;53</td>
<td>push ebx</td>
</tr>
<tr>
<td>0&#215;53</td>
<td>push ebx</td>
</tr>
<tr>
<td>0&#215;33,0xc0</td>
<td>xor eax, eax</td>
</tr>
<tr>
<td>0&#215;50</td>
<td>push eax</td>
</tr>
<tr>
<td>0xff,0xd6</td>
<td>call esi</td>
</tr>
<tr>
<td>0xff,0&#215;15, 0xe6, 0&#215;6c, 0&#215;41, 0&#215;00</td>
<td>call &lt;adres&gt;</td>
</tr>
<tr>
<td>0&#215;68,0xfd, 0&#215;12,0&#215;00</td>
<td>adres nadpisujacy adres powrotu</td>
</tr>
<tr>
<td>0&#215;26</td>
<td>znak &amp;</td>
</tr>
</tbody>
</table>
<p>Co robi ten kod? Pierwsza instrukcja odejmuje 128 od rejestru esp – stack pointer, robiąc miejsce na stosie dla dalszego kodu. W dalszej części przypisujemy do rejestru esi adres funkcji MessageBox. Później odkładamy 5 wartości na stos, najpierw pewne dane, które już wcześniej widzieliśmy na stosie (co dokładnie … zostawiam czytelnikowi do zgadnięcia). Nim odłożymy kolejne 4 wartości zapamiętujemy zawartość rejestru esp w rejestrze ebx. Adres ten odpowiada początkowi stosu, czyli aktualnie danym załadowanym na stos przed chwila. W dalszej części odkładamy na stos liczbę 0&#215;40, dwa razy wskaźnik wskazujący na pewne dane znajdujące się na stosie oraz wartość 0. Potem dokonujemy wywołania funkcji MessageBoxA. Co robiliśmy wcześniej na stosie? – odkładaliśmy parametry – handle do okna (0 – NULL, czyli okno bez powiązania z innym oknem), dalej dwa razy wskaźnik na zawartość okna i jego tytuł (czy już wiadomo jakie dane były wkładane na stos? <img src='http://blog.sages.com.pl/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  ) i na końcu typ okna, w naszym przypadku okno informacyjne. Na uwagę zasługuje jeszcze ostatnia wartość – 0&#215;26 czyli w reprezentacji ASCII znak &amp; &#8211; warunek stopu pętli kopiującej dane do bufora.</p>
<p>Oczywiście zaprezentowany w tym przykładzie kod nie robi nic groźnego, jedynie wyświetla okienko. Jednak mając więcej miejsca na stosie można spreparować naprawdę groźną funkcję. Ale to już temat na kolejny wpis … <img src='http://blog.sages.com.pl/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h3>Kilka wyjaśnień</h3>
<p>Wykorzystywane w przykładowym kodzie exploita adresy zostały wcześniej odkryte poprzez analizę kodu działającego programu za pomocą debuggera. Architektura systemu Windows (no może z wyjątkiem Visty i dalszych) powoduje, że w tym samym środowisku  (te same zainstalowane programy) wykonujący się program zawsze będzie posiadał te same adresy funkcji oraz stosu. Dzięki temu w środowisku testowym można je poznać … i wykorzystywać do ataku na inne identyczne lub podobne (patrz poduszeczkę NOP) systemy. Analogicznie ma się sprawa z adresem wykorzystywanej funkcji MessageBoxA.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sages.com.pl/2010/07/blad-buffer-overflow-w-szczegolach/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lekkie aplikacje internetowe z wykorzystaniem Java Enterprise Edition w wersji 6</title>
		<link>http://blog.sages.com.pl/2010/06/lekkie-aplikacje-internetowe-z-wykorzystaniem-java-enterprise-edition-w-wersji-6/</link>
		<comments>http://blog.sages.com.pl/2010/06/lekkie-aplikacje-internetowe-z-wykorzystaniem-java-enterprise-edition-w-wersji-6/#comments</comments>
		<pubDate>Wed, 02 Jun 2010 20:29:14 +0000</pubDate>
		<dc:creator>Jakub Koperwas</dc:creator>
				<category><![CDATA[Problemy i rozwiązania]]></category>
		<category><![CDATA[EJB3.1]]></category>
		<category><![CDATA[JEE6]]></category>

		<guid isPermaLink="false">http://blog.sages.com.pl/?p=51</guid>
		<description><![CDATA[JEE 6 czas zacząć
Niniejszym wątkiem o prowokującym tytule otwieram cykl postów dotyczących Java Enterprise Edition w wersji 6 (JSR 316).
Po dłuższym namyśle postanowiłem nie zaczynać od przeglądu całej JEE6, ani też podejmować się jej subiektywnej oceny, a tym samym nie wpisywać się w polemikę w stylu: „czy lepszy czasem kulejący standard czy super przemyślany nie-standard”. [...]]]></description>
			<content:encoded><![CDATA[<h1>JEE 6 czas zacząć</h1>
<div id="_mcePaste">Niniejszym wątkiem o prowokującym tytule otwieram cykl postów dotyczących Java Enterprise Edition w wersji 6 (JSR 316).</div>
<div id="_mcePaste">Po dłuższym namyśle postanowiłem nie zaczynać od przeglądu całej JEE6, ani też podejmować się jej subiektywnej oceny, a tym samym nie wpisywać się w polemikę w stylu: „czy lepszy czasem kulejący standard czy super przemyślany nie-standard”. Skupię się na faktach, nowościach, zmianach i co najwyżej subiektywnych odczuciach, dotyczących „trendów”.</div>
<div><span id="more-51"></span></div>
<div id="_mcePaste">To, co można powiedzieć bezsprzecznie, nie narażając się na bycie kontrowersyjnym, to to że w JEE6 trochę się zmieniło. Ci, którzy do tej pory tworzyli aplikacje w JEE5 i obserwowali pewne kłopotliwe braki w specyfikacjach – czasem duże, jak np. brak criteria API w JPA1, czy drobne: niemożność automatycznego startowania usług czasowych w EJB 3.0 &#8211; odczują prawdopodobnie poprawę komfortu pracy. Ci, którzy korzystali do tej pory ze szkieletów typu Seam z przyjemnością odkryją, że spora część Seam’a z jego kontekstami, wstrzykiwaniem zależności, zasięgiem konwersacji, czy rozgłaszaniem i konsumowaniem zdarzeń przez komponenty zostały wciągnięte do standardu mocą specyfikacji Context and Dependency Injection for Java EE (JSR 299) i Dependency Injection for Java (JSR 330). Optymizmem napawa również fakt, iż niektóre składowe specyfikacji trzymane dla zgodności z poprzednimi wersjami, zostały oznaczone jako możliwe do usunięcia w kolejnych wersjach – sama główna specyfikacja EJB 3.1 to ponad 600 stron opisująca nawet takie starocie jak EJB 1.1 Entity Bean – najwyższy czas już posprzątać.</div>
<h1>Lekkie aplikacje w JEE i to bez dodatkowych szkieletów?</h1>
<div id="_mcePaste">Na omawianie poszczególnych ”ficzerów” JEE6 przyjdzie pora przy omawianiu konkretnych specyfikacji, tu chciałbym rozważyć pewien kierunek zapoczątkowany przez JEE5 a mocno rozwinięty w JEE6, który powoli zrywa z konotacją JEE = „ciężka aplikacja”, w opozycji do np. „lekkich szkieletów”.</div>
<div id="_mcePaste">Historycznie aplikacja JEE wykorzystująca EJB kojarzyła się raczej z aplikacją posiadającą wydzieloną warstwę logiki biznesowej, w szczególności dostępnej zdalnie, w opozycji do podejścia opartego na wyłącznie komponentach webowych, z logiką umieszczoną w POJO (Plain Old Java Objects), czyli w zwyczajnych obiektach javowych. Zwykle też nie obyłoby się bez wsparcia wszelakich szkieletów umożliwiających Inversion of Controll/Dependency Injection jak np. Spring, czy SEAM, zwłaszcza gdy nie używamy EJB i nie mamy możliwości użycia transakcji zarządzanej przez kontener (CMT), deklaratywnego zabezpieczania zasobów etc.</div>
<div id="_mcePaste">Aktualna formuła Java EE 6 stara się objąć standardem tworzenie zarówno „ciężkich”, jak i „lekkich aplikacji”, w taki sposób, aby dało się je pisać bez konieczności korzystania z dodatkowych szkieletów spoza standardu.</div>
<div id="_mcePaste">Powyższe stwierdzenie opieram na następujących zmianach, które zaobserwowałem:</div>
<h3>Wprowadzenie koncepcji Komponentu Zarządzanego (Managed Bean &#8211; dalej MB)</h3>
<div id="_mcePaste">Wiele szkieletów reklamuje się jako opartych na POJO. Nie jest jednak to pojęcie zdefiniowane formalnie. Jeśli potraktujemy POJO dosłownie (Zwyczajny Obiekt Java), czyli interpretując jako obiekt, który nie jest zarządzany przez żaden kontener i nie posiada konfiguracji, wówczas ciężko jest przyznać rację wielu szkieletom które chwalą się, że oparte są na POJO, ale np. wymagają uprzedniego skonfigurowania w XMLu, czy anotacjach po to, ażeby działo wstrzeliwanie zależności, czy transakcje (patrz http://en.wikipedia.org/wiki/Plain_Old_Java_Object).</div>
<div id="_mcePaste">W takim kontekście POJO rozumiane było raczej jako nie-EJB, co prawdopodobnie zgodne jest z pierwotną intencją twórcy pojęcia, Martina Fowlera (http://www.martinfowler.com/bliki/POJO.html).</div>
<div id="_mcePaste">JEE 6 „definiuje” lub „zawłaszcza” (w zależności od poglądów) to pojęcie w specyfikacji Managed Beans 1.0, która jest wydzieloną częścią głównej specyfikacji JEE6. Cytując za specyfikacją:</div>
<div id="_mcePaste">„Komponent Zarządzany (Managed Bean) jest zarządzanym przez kontener obiektem, znanym jako skrót POJO, który spełnia minimalny zestaw wymogów.”</div>
<div id="_mcePaste">W telegraficznym skrócie: MB jest oznaczony specjalną anotacją, może posiadać nazwę widoczną w JNDI i określony cykl życia, który jest zarządzany przez kontener. Nie ma tu słowa o bardziej zaawansowanych elementach, które np. posiadają EJB.</div>
<div id="_mcePaste">Managed Bean jest pojęciem wspólnym, wykorzystywanym dalej przez inne specyfikacje (EJB, JSF) które mogą na te wymagania wpływać. Szczegóły wymagań dla MB poznamy w załączniku.</div>
<h3 id="_mcePaste">Wyodrębnienie specyfikacji Interceptors</h3>
<div id="_mcePaste">Specyfikacja Interceptorów (Przechwytywaczy), została wyodrębniona w obrębie specyfikacji EJB i może być wykorzystywana ze wspomnianymi wcześniej Komponentami Zarządzanymi, dodając elementy aspektowości do programowania w JEE. Dla przypomnienia: Interceptor potrafi przechwycić wołanie metody komponentu, powodując wykonanie własnego kodu, który może np. zmodyfikować parametry wołania etc.</div>
<h3 id="_mcePaste">Wprowadzenie możliwości wstrzykiwania zależności (Dependency Injction)</h3>
<div id="_mcePaste">W JEE5 pojawiły się pierwsze próby podejścia do problemu – w postaci anotacji @EJB, @Resource, @WebserviceRef, potrafiące wstrzyknąć zależność będącą komponentem EJB, zasobem JCA czy odwołaniem do usługi Webservice. Wstrzykiwanie było możliwe do ściśle określonych miejsc: komponentu EJB, servletu, komponentu zarządzanego JSF, etc.</div>
<div id="_mcePaste">Mocą specyfikacji Context and Dependency Injection for Java EE (JSR 299) większość klas Java, a w szczególności wszystkie Java Beans są komponentami zarządzanymi (MB), a do wszystkich komponentów zarządzanych stosują się mechanizmy wstrzykiwania zależności. Szczegóły poznamy w innych postach, warto tylko zauważyć że wstrzykiwanie zależności dostępne jest teraz niemal dla każdego możliwego obiektu.</div>
<h3 id="_mcePaste">Wprowadzenie kontekstów</h3>
<div id="_mcePaste">Wymieniona wyżej specyfikacja JSR 299 umożliwia również umieszczanie komponentów w kontekstach, które determinują ich cykl życia. Do tej pory możliwość sterowania długością życia komponentów zarządzanych (JEE, Spring) był ograniczony: komponenty mogły być albo tworzone ad hoc dla każdego żądania, albo trwać w całej rozciągłości sesji lub też aplikacji. Seam  wprowadził nowy zasięg konwersacji, którego długość życia określana jest przez programistę. Specyfikacja CDI wciela tę możliwość do JEE6, dając bardzo potężne narzędzie do żonglowania komponentami w obrębie aplikacji.</div>
<div id="_mcePaste">Gdyby na tym poprzestać to rysuje nam się widok aplikacji opartej na prawie zwyczajnych klasach, ze wsparciem dla wstrzykiwania zależności, kontekstowości i przechwytywania wołań. Ale pójdziemy jeszcze dalej, włączymy w to „lekkie EJB” (brzmi jak margaryna o smaku masła, ale mimo wszystko o dziwo smakuje)</div>
<div id="_mcePaste">
<h3>Umożliwienie użycia EJB w „lżejszych” scenariuszach z zachowaniem podstawowych możliwości, poprzez</h3>
<ul>
<li> wprowadzenie lekkiej wersji EJB, pozwalającej jedynie na dostęp lokalny, pozbawionej MDB, czy usług czasowych (więcej w dodatku i innych postach)</li>
<li> wprowadzenie bezinterfejsowych EJB, które już coraz bardziej przypominają POJO</li>
<li> wprowadzenie kontenera „embeddable”, czyli lekkiego kontenera zagnieżdżanego w aplikacjach Java SE, pozwalającego na użycie tamże komponentów EJB</li>
<li> możliwość wdrażania EJB jako moduł war (czyli jako część aplikacji webowej</li>
</ul>
</div>
<div>Tym sposobem podstawowe funkcjonalności EJB, takie jak deklaratywne zabezpieczanie zasobów, czy transakcje zarządzane przez kontener zasilają możliwości naszych Komponentów Zarządzanych, nie pociągając za sobą konieczności używania „całej armaty”, np. z dostępem zdalnym i innymi funkcjonalnościami, nie zawsze potrzebnymi w prostej aplikacji, a całą aplikację możemy zapakować w wara.</div>
<div id="_mcePaste">Całość przypieczętowuje pojęcie profilu</div>
<h3 id="_mcePaste">Wprowadzenie koncepcji profilu</h3>
<div id="_mcePaste">Profil to, cytując za specyfikacją: „konfiguracja Platformy JEE, nakierowana na konkretną klasę aplikacji” (klasę w rozumieniu rodzaju).  Do tej pory mówiliśmy o Serwerach aplikacji które musiały wspierać całą JEE oraz o Serwerach Webowych, które adresowały lekkie aplikacje webowe  takich jak np. Tomcat. Serwery webowe wspierały pewien wybrany zbiór specyfikacji np. Servlet/JSP/JSF.. Pojęcie serwera webowego nie było jednak sprecyzowane w  szczególności nie istniała lista specyfikacji którą powinien implementować serwer webowy. W JEE6 wprowadzony zostaje tzw. „Web Profile” czyli zestaw specyfikacji które powinien zawierać serwer webowy. W skład Web Profile w  szczególności wchodzą specyfikacje Servlet, JSP, JSF, ale także  EJB Lite i JTA. A więc komponenty EJB będą od teraz mogły działać na serwerach typu Web, a nie tylko pełnych serwerach aplikacji. Cały Web Profile opisany jest w dodatku.</div>
<h1>Co dalej?</h1>
<div id="_mcePaste">W kolejnych postach zechcę przybliżyć jedynie to, co w JEE6 nowe lub zmienione, nie zaś omawiać ją krok po kroku. Postaram się jednak żeby posty zrozumiałe były również dla początkujących w JEE6. Zacznę od omówienia zmian w specyfikacji EJB 3.1 i JPA 2.0. Postaram się także z bliska pokazać jak w praktyce udaje się tworzyć „lekkie” aplikacje oparte w całości na JEE6.</div>
<h2>DODATEK 1 – Wymagania Specyfikacji Managed Beans.</h2>
<div>Na podstawie dokumentu <em>Managed Beans 1.0 Specification</em> będącego częścią  <em>Java TM Platform, Enterprise Edition 6 (Java EE 6) Specification (JSR 316 )</em></div>
<h3>Wymagania podstawowe:</h3>
<div id="_mcePaste">o	Definiowanie Komponentu Zarządzanego: jest to klasa konkretna, rozszerzalna (nie abstract, nie final), nie będąca wewnętrzną klasą statyczną; Komponent Zarządzany oznaczony jest anotacją javax.annotation.ManagedBean. W wersji podstawowej ziarno powinno posiadać konstruktor bezargumentowy</div>
<div id="_mcePaste">o	Nazewnictwo: Komponent zarządzany może mieć nazwę podaną w anotacji @ManagedBean(“myName“), nawzwa musi być unikalna w obrębie modułu. Nazwanie Komponentu pociąga możliwość wyszukania go w przestrzeni nazw JNDI pod nazwami:</div>
<div id="_mcePaste">java:app/&lt;module-name&gt;/&lt;bean-name&gt; – dla przestzeni nazw aplikacji oraz</div>
<div id="_mcePaste">java:module/&lt;bean-name&gt; &#8211; dla przestrzeni nazw modułu</div>
<div id="_mcePaste">o	Cykl życia i zasoby</div>
<div id="_mcePaste">Ziarna mogą korzystać z metod oznaczonych javax.annotation.PostConstruct i javax.annotation.PreDestroy do wskazania metod wołanych przez kontener po utworzeniu i przed zniszczeniem Ziarna.</div>
<div id="_mcePaste">Komponenty zarządzane w ogólności nie mają własnej przestrzeni nazw( tak jak np. EJB), zasoby z których korzystają powinny się znaleźć w przestrzeni modułu</div>
<div id="_mcePaste">o	Wątki</div>
<div id="_mcePaste">Metoda ziarna wykonywana jest w tym samym wątku co wołający</div>
<div id="_mcePaste">o	Interceptory</div>
<div id="_mcePaste">MB mogą używać interceptorów zdefiniowanych w specyfikacji Interceptors 1.1</div>
<div>Przykład Komponentu Zarządzanego:</div>
<p><code><br />
@ManagedBean(“myName“),<br />
public class MyClass{</code></p>
<p>@PostConstruct<br />
public void init(){}</p>
<p>@PreDestroy<br />
public void destroy(){};</p>
<p>//inne metody</p>
<p>}</p>
<div id="_mcePaste">Jest komponentem zarządzanym i widoczny w przestrzeni nazw  JNDI  dal modułu jako</div>
<div id="_mcePaste">java:module/myName</div>
<div id="_mcePaste">Komponent definiuje również metodę wołanych przy tworzeniu i niszczeniu Komponentu.</div>
<h3>Rozszerzenie wymagań:</h3>
<div id="_mcePaste">Wymagania podane w specyfikacji mogą być zmieniane w specyfikacjach bazujących na powyższej w następujący sposób</div>
<div id="_mcePaste">•	Definiowanie Komponentu Zarządzanego: specyfikacja rozszerzająca może podać inne sposoby definiowania komponentu , w szczególności dołożyć wsparcie dla konstruktorów z argumentami, niwelując konieczność deklarowania konstruktora bezargumentowego</div>
<div id="_mcePaste">•	nazewnictwo: specyfikacja rozszerzająca może podać inny sposób nazywania MB – np. poprzez dodatkowe anotacje, jednakże jeśli istnieje anotacja @ManagedBean(”&#8230;”) to ma ona zawsze pierwszeństwo. Spec. rozszerzająca może również wprowadzać dodatkowe przestrzenie nazw lub też nazwy alternatywne dla ziarna</div>
<div id="_mcePaste">•	cykl życia i zasoby,</div>
<div id="_mcePaste">Spec Roz może definiować bardziej skomplikowany cykl życia a także wprowadzić przestrzeń nazw dla komponentu “java:comp”</div>
<div id="_mcePaste">•	wątki : specyfikacja może anulować wymaganie na konieczność wołania w wątku wołającego</div>
<div id="_mcePaste">•	Interceptory specyfikacja może udostępniać dodatkowy mechanizmy interceptorów niż podane w sepcyfikacji</div>
<h2>DODATEK 2 – Lista specyfikacja wymagana dla serwera umożliwiającego udostępnianie aplikacji zgodnej z Web Profile</h2>
<div>Na postawie <em>Java™ Platform, Enterprise Edition 6 (Java EE 6) Web Profile Specification CHAPTER WP.2 Web Profile Definition</em> będącego częścią <em> JavaTM Platform, Enterprise Edition 6 (Java EE 6) Specification (JSR 316 )</em></div>
<div><em><br />
</em></div>
<div id="_mcePaste">o	Servlet 3.0</div>
<div id="_mcePaste">o	JavaServer Pages (JSP) 2.2</div>
<div id="_mcePaste">o	Expression Language (EL) 2.2</div>
<div id="_mcePaste">o	Debugging Support for Other Languages (JSR-45) 1.0</div>
<div id="_mcePaste">o	Standard Tag Library for JavaServer Pages (JSTL) 1.2</div>
<div id="_mcePaste">o	JavaServer Faces (JSF) 2.0</div>
<div id="_mcePaste">o	Common Annotations for theJava Platform (JSR-250) 1.1</div>
<div id="_mcePaste">o	Enterprise JavaBeans (EJB) 3.1 Lite</div>
<div id="_mcePaste">o	Java Transaction API (JTA) 1.1</div>
<div id="_mcePaste">o	Java Persistence API (JPA) 2.0</div>
<div id="_mcePaste">o	Bean Validation 1.0</div>
<div id="_mcePaste">o	Managed Beans 1.0</div>
<div id="_mcePaste">o	Interceptors 1.1</div>
<div id="_mcePaste">o	Contexts and Dependency Injection for the Java EE Platform 1.0</div>
<div id="_mcePaste">o	Dependency Injection for Java 1.0</div>
<h2>DODATEK 3 – EJB vs. EJB Light</h2>
<div>Na podstawie dokumentu  <em>JSR 318: Enterprise JavaBeansTM,Version 3.1EJB Core Contracts and Requirements chapter 21.1  EJB 3.1 Lite</em></div>
<div id="_mcePaste">
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="205" valign="top"></td>
<td width="205" valign="top">EJB Light</td>
<td width="205" valign="top">Pełne EJB</td>
</tr>
<tr>
<td width="205" valign="top">Komponenty sesyjne</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Komponenty   sterowane wiadomością</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Komponenty   Encyjne zgodne z EJB 2.x/1.x</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK, oznaczone   do usunięcia w przyszłych specyfikacjach</td>
</tr>
<tr>
<td width="205" valign="top">JPA 2.0</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Dostęp lokalny   i bez-interffejsowy</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Dostęp zdalny   zgodny z EJB 3.0</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Dostęp zdalny   zgodny z EJB 2.x</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Dostep przez   JAX-WS</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Dostęp przez   JAX-RPC</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK, oznaczone   do usunięcia w przyszłych specyfikacjach</td>
</tr>
<tr>
<td width="205" valign="top">Usługa czasowa   (Timer Service)</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Wołanie   asynchroniczne komponentów sesyjnych</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Interceptory</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Zgodność z RMI-IIOP</td>
<td width="205" valign="top">NIE</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Transakcje   zarządzane przez kontener/komponent (CMT i BMT)</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">Deklaratywne i   programistyczne zabezpieczanie komponentów</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK</td>
</tr>
<tr>
<td width="205" valign="top">API kontenera zagnieżdżonego</td>
<td width="205" valign="top">TAK</td>
<td width="205" valign="top">TAK, ale jeśli   serwer dostarcza pełnego EJB to API dla kontenera zagnieżdżonego może   ograniczać się jedynie do zestawu Light</td>
</tr>
</tbody>
</table>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.sages.com.pl/2010/06/lekkie-aplikacje-internetowe-z-wykorzystaniem-java-enterprise-edition-w-wersji-6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Atak wstrzyknięcia na stronach bez pól tekstowych</title>
		<link>http://blog.sages.com.pl/2010/03/atak-wstrzykniecia-na-stronach-bez-pol-tekstowych/</link>
		<comments>http://blog.sages.com.pl/2010/03/atak-wstrzykniecia-na-stronach-bez-pol-tekstowych/#comments</comments>
		<pubDate>Wed, 10 Mar 2010 13:36:13 +0000</pubDate>
		<dc:creator>Krzysztof Cabaj</dc:creator>
				<category><![CDATA[Problemy i rozwiązania]]></category>
		<category><![CDATA[bezpieczeństwo]]></category>
		<category><![CDATA[injection flaw]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[WebScarab]]></category>
		<category><![CDATA[XSS]]></category>

		<guid isPermaLink="false">http://blog.sages.com.pl/?p=30</guid>
		<description><![CDATA[Artykuł ten jest pierwszym z serii wpisów poświęconych zagadnieniom bezpieczeństwa aplikacji Webowych. W niniejszym artykule zostanie omówiona możliwość wykorzystania podatności typu injection na stronach bez pól tekstowych oraz wykorzystanie narzędzia WebScarab do przeprowadzenia takiego ataku.
Wykorzystanie podatności wstrzyknięcia (ang. Injection Flaw) jest częstą przyczyną włamań do aplikacji internetowych. Problemy związane z bezpieczeństwem aplikacji dotyczące wstrzyknięcia niepoprawnych [...]]]></description>
			<content:encoded><![CDATA[<p>Artykuł ten jest pierwszym z serii wpisów poświęconych zagadnieniom bezpieczeństwa aplikacji Webowych. W niniejszym artykule zostanie omówiona możliwość wykorzystania podatności typu injection na stronach bez pól tekstowych oraz wykorzystanie narzędzia WebScarab do przeprowadzenia takiego ataku.</p>
<p>Wykorzystanie <a href="http://www.owasp.org/index.php/Injection_Flaws">podatności wstrzyknięcia (ang. Injection Flaw)</a> jest częstą przyczyną włamań do aplikacji internetowych. Problemy związane z bezpieczeństwem aplikacji dotyczące wstrzyknięcia niepoprawnych danych na stronach z polami testowymi są powszechne (w szczególności ataki typu <a href="http://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29">Cross Site Scripting, XSS</a>). Jednak możliwość wykorzystania podatności wstrzyknięcia na stronach bez pól tekstowych nie jest już tak oczywista.</p>
<p><span id="more-30"></span></p>
<h3>Injection Flaw</h3>
<p>Rozpatrzmy prostą funkcjonalność aplikacji obsługującej sklep internetowy, służącą do przedstawienia statusu złożonych zamówień. Przykładowy wygląd takiej aplikacji przedstawiony jest na rysunku 1.<br />
<a href="http://blog.sages.com.pl/wp-content/uploads/2010/03/aplikacja.jpg"><img src="http://blog.sages.com.pl/wp-content/uploads/2010/03/aplikacja-300x213.jpg" alt="" title="Aplikacja przykładowa" width="300" height="213" class="aligncenter size-medium wp-image-31" /></a></p>
<p>Jak można przypuszczać, informacja uzyskana od użytkownika, związana z wyborem interesującego statusu przesyłki służy do sparametryzowania zapytania SQL do bazy danych. Z dużym prawdopodobieństwem można założyć, że wykorzystane zapytanie SQL będzie zbliżone do zapytania w następującej postaci:</p>
<p><code>SELECT id, towar, adres FROM zamowienia WHERE klient_id = ...<br />
          and status = ...</code></p>
<p>Jeśli założy się, że programista aplikacji kleił ręcznie zapytanie oraz nie zapewnił walidacji dostarczanych danych, łatwo można się domyśleć jaki tekst trzeba umieścić w zapytaniu aby uzyskać informację o wszystkich zamówieniach.</p>
<p>Rozważyć można sytuację, kiedy zamiast spodziewanego identyfikatora statusu podstawiony zostanie fragment zapytania SQL postaci:</p>
<p><code>4 or 1=1</code></p>
<p>W takim przypadku warunek w zapytaniu będzie zawsze prawdziwy, co w efekcie spowoduje wyszukanie wszystkich rekordów z tabeli.</p>
<p>Ponieważ aplikacja wysyła dane użytkownika za pomocą metody GET, w wysyłanym URLu możemy zaobserwować wszystkie parametry. Wystarczy, że w odpowiednim miejscu zostanie wklejony spreparowany fragment danych a cały URL ponownie wysłany. Na rysunku 2 można zaobserwować wynik wykonania za pomocą zwykłej przeglądarki takiego złośliwego zapytania.<br />
<a href="http://blog.sages.com.pl/wp-content/uploads/2010/03/aplikacja-atak.jpg"><img src="http://blog.sages.com.pl/wp-content/uploads/2010/03/aplikacja-atak-300x208.jpg" alt="" title="Atak na aplikację przykładową" width="300" height="208" class="aligncenter size-medium wp-image-33" /></a></p>
<p>Jak można było się spodziewać, w wyniku tego zapytania została ujawniona cała zawartość tabeli. W tym przypadku poznano wszystkich adresatów do których sklep wysłał paczki. Jednak jeśli byłby to system bankowy i &#8220;dziurawa&#8221; funkcjonalność dotyczyłaby kodów jednorazowych &#8211; ujawnione zostałyby adresy, pod które zostały one wysłane. Jeśli w tablicy znajdowały by się adresy mailowe, to ich poznanie mogło by posłużyć do wysyłania SPAMu lub wiadomości związanych z <a href="http://pl.wikipedia.org/wiki/Phishing">phishingiem</a>. Taki atak byłby o tyle groźny, że dotyczył rzeczywistych klientów danej organizacji.</p>
<p>W omawianym powyżej przykładzie aplikacja wykorzystywała do komunikacje metodę GET, więc atak można przeprowadzić nawet za pomocą przeglądarki. Jednak wykorzystanie metody POST, chociaż ukrywa część informacji przed zwykłym użytkownikiem oraz umożliwia szyfrowanie przesyłanych danych w przypadku protokołu HTTPS, nie jest zabezpieczeniem (patrz paradygmat <a href="http://pl.wikipedia.org/wiki/Security_through_obscurity">security by obscurity</a>). W takim przypadku pomocne może być narzędzie <a href="http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project">WebScarab</a>. WebScarab jest specjalnym rodzajem serwera proxy. Każde żądanie przechodzące przez niego jest przechwytywane i może zostać dowolnie zmodyfikowane. Program ten jest często wykorzystywany przy testach bezpieczeństwa aplikacji webowych. </p>
<h3>Jak przeprowadzić atak wykorzystując program WebScarab</h3>
<p>Pierwszym krokiem jest ustawienie proxy w używanej przeglądarce na adres wykorzystywany przez program WebScarab (standardowo nasłuchuje na porcie 8008). Następnym krokiem jest wejście na stronę, której ewentualne podatności chcemy sprawdzić. Podczas wywołania zapytania zostanie ono przechwycone i zaprezentowane użytkownikowi. W tym miejscu można dokonać dowolnych modyfikacji nagłówków a także danych przesyłanych za pomocą metody POST. Przykład takiej modyfikacji dobrze znanym fragmentem zapytania SQL zaprezentowany jest na rysunku 3.<br />
<a href="http://blog.sages.com.pl/wp-content/uploads/2010/03/webscarab-atak.jpg"><img src="http://blog.sages.com.pl/wp-content/uploads/2010/03/webscarab-atak-300x242.jpg" alt="" title="Atak za pomocą WebScarab" width="300" height="242" class="aligncenter size-medium wp-image-34" /></a></p>
<p>Efekt przeprowadzonych działań widoczny jest na rysunku 4.<br />
<a href="http://blog.sages.com.pl/wp-content/uploads/2010/03/aplikacja-atak-post.jpg"><img src="http://blog.sages.com.pl/wp-content/uploads/2010/03/aplikacja-atak-post-300x217.jpg" alt="" title="WebScarab - rezultat" width="300" height="217" class="aligncenter size-medium wp-image-35" /></a></p>
<p>Zgodnie z oczekiwaniami po zatwierdzeniu zmiany dokonanej w programie WebScarab uzyskany zostaje dostęp do wszystkich danych znajdujących się w tabeli.</p>
<h3>Podsumowanie</h3>
<p>Jak można zabezpieczyć się przed tego typu atakiem</p>
<ul>
<li>Nie kleić ręcznie zapytań, zamiast tego skorzystać ze specjalnych funkcji lub obiektów służących do tego (ale nie zawsze się da, np. zapytanie XPath). Dla przykładu, w JDBC mamy do dospozycji PreparedStatement:
<pre>
. . .
PreparedStatement ps;
c = ds.getConnection();
ps = c.prepareStatement("SELECT id, towar, adres FROM zamowienia
   WHERE klient_id = ? and status = ?");
ps.setString(1, klient_id);
ps.setString(2, status);
ps.execute();
. . .
</pre>
</li>
<li>Nie ufać w żadne dane wysłane od klienta, zawsze dokonywać walidacji albo choćby procesu escapowania danych
<pre>
. . .

try {
   Integer val = null;
   val = val.parseInt(request.getParameter("status"));
   if (val <= 0 || val > 4)
      throw new NumberValidationException(); //własna klasa wyjątku
   . . .
} catch(NumberFormatException ex) {
   out.println("Parameter problem, try once again");
} catch(NumberValidationException ex) {
   out.println("Parameter problem, try once again");
} catch(...) ...
</pre>
</li>
]]></content:encoded>
			<wfw:commentRss>http://blog.sages.com.pl/2010/03/atak-wstrzykniecia-na-stronach-bez-pol-tekstowych/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Optymalizacja zapytań z podzapytaniami</title>
		<link>http://blog.sages.com.pl/2010/03/optymalizacja-zapytan-z-podzapytaniami/</link>
		<comments>http://blog.sages.com.pl/2010/03/optymalizacja-zapytan-z-podzapytaniami/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 12:38:32 +0000</pubDate>
		<dc:creator>Piotr Kołaczkowski</dc:creator>
				<category><![CDATA[Problemy i rozwiązania]]></category>
		<category><![CDATA[bazy danych]]></category>
		<category><![CDATA[optymalizacja]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[zapytania]]></category>

		<guid isPermaLink="false">http://blog.sages.com.pl/?p=17</guid>
		<description><![CDATA[
Współczesne systemy relacyjnych baz danych, również te z otwartym kodem źródłowym, umożliwiają tworzenie bardzo złożonych zapytań SQL. Poprzez umieszczanie podzapytań w seksji SELECT, FROM oraz WHERE, jak również przez łączenie zapytań za pomocą operatorów takich jak UNION czy INTERSECT, nie trudno napisać zapytanie, które nie zmieści się na monitorze. Ta elastyczność ma niestety swoją cenę: [...]]]></description>
			<content:encoded><![CDATA[<p>
Współczesne systemy relacyjnych baz danych, również te z otwartym kodem źródłowym, umożliwiają tworzenie bardzo złożonych zapytań SQL. Poprzez umieszczanie podzapytań w seksji SELECT, FROM oraz WHERE, jak również przez łączenie zapytań za pomocą operatorów takich jak UNION czy INTERSECT, nie trudno napisać zapytanie, które nie zmieści się na monitorze. Ta elastyczność ma niestety swoją cenę: analiza takiego złożonego zapytania jest nie lada wyzwaniem dla silnika bazy danych i, jak pokazuje praktyka, niektóre systemy baz danych nie najlepiej sobie z tym radzą. Najprościej wytłumaczyć to na przykładzie, wzorowanym na prawdziwym przypadku.
</p>
<p> <span id="more-17"></span></p>
<p>Wyobraźmy sobie, że tworzymy system, którego jednym z zadań jest zapisywanie informacji o aktywności użytkowników umieszczających pliki na serwerze. Dla każdego zarejestrowanego użytkownika, chcemy wiedzieć, kiedy i jaki plik umieścił. Schemat bazy danych dla tej funkcjonalności wygląda następująco:
</p>
<pre>
             Table "public.test_user"
     Column      |         Type          | Modifiers
-----------------+-----------------------+-----------
 user_id         | integer               | not null
 name            | character varying(64) | not null
 login           | character varying(16) | not null
 hashed_password | character varying(32) | not null

Indexes:
    "test_user_pkey" PRIMARY KEY, btree (user_id)
    "test_user_login_key" UNIQUE, btree ("login")
</pre>
<pre>
            Table "public.test_upload"
   Column    |          Type          | Modifiers
-------------+------------------------+-----------
 upload_id   | integer                | not null
 user_id     | integer                | not null
 path        | character varying(255) | not null
 upload_time | timestamp              | not null

Indexes:
    "test_upload_pkey" PRIMARY KEY, btree (upload_id)
Foreign-key constraints:
    "test_upload_user_id_fkey" FOREIGN KEY (user_id)
        REFERENCES test_user(user_id)
</pre>
<p>
Tabela <i>test_user</i> zawiera ok. 10 tys. użytkowników. Jeden użytkownik średnio umieścił na serwerze 5 plików, choć są oczywiście tacy, którzy nie umieścili ani jednego, jak i tacy, którzy umieścili ich kilkaset. Każdemu umieszczonemu plikowi odpowiada jeden rekord w tabeli <i>test_upload</i>.
</p>
<p>
Powiedzmy, że nasz przykładowy serwis potrzebuje wyświetlić 10 różnych użytkowników, którzy dodawali ostatnio jakieś pliki. Użytkownik, który dodał plik najdawniej, ma znaleźć się na końcu listy, użytkownik, którego plik jest &#8220;najświeższy&#8221; &#8211; na samym początku. Jednak żaden użytkownik nie powinien znaleźć się na liście &#8220;top 10&#8243; więcej niż raz.
</p>
<p>
Pierwsze podejście do tego zapytania mogłoby wyglądać następująco:
</p>
<pre>
SELECT name, path, upload_time
FROM test_user u JOIN test_upload l ON (u.user_id = l.user_id)
ORDER BY upload_time DESC
LIMIT 10;
</pre>
<p>
Niestety, zapytanie, choć faktycznie wyświetla ostatnio dodane pliki, niezupełnie robi to, czego oczekujemy &#8211; jeden użytkownik może wystąpić więcej niż raz na liście. Z pomocą przychodzi dodatkowy warunek, który wyeliminuje starsze wpisy dla tego samego użytkownika:
</p>
<pre>
SELECT name, path, upload_time
FROM test_user u JOIN test_upload l ON (u.user_id = l.user_id)
WHERE upload_time =
   (SELECT max(upload_time) FROM test_upload WHERE user_id = u.user_id)
ORDER BY upload_time DESC
LIMIT 10;
</pre>
<p>
Teraz zapytanie już działa poprawnie, ale w zależności od systemu baz danych, na którym pracujemy, mogliśmy wprowadzić inny poważny problem: zapytanie będzie się wykonywać dużo wolniej. Na naszym systemie testowym opartym o PostgreSQL 8.2 pierwsza (niepoprawna) wersja wykonywała się 200 ms. Tymczasem wersja &#8220;poprawiona&#8221;, wykonywała się nieco ponad 10 minut, czyli ok. 3 tys. razy wolniej. Przyczynę tej powolności pokazuje wynik EXPLAIN:
</p>
<div style="width: 580px; overflow: auto; background: #FFFFB3">
<pre class="noborder">
                                                                QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=50967080.34..50967080.36 rows=10 width=40) (actual time=617733.543..617733.554 rows=10 loops=1)
   ->  Sort  (cost=50967080.34..50967080.52 rows=75 width=40) (actual time=617733.542..617733.548 rows=10 loops=1)
         Sort Key: l.upload_date
         ->  Nested Loop  (cost=0.00..50967078.00 rows=75 width=40) (actual time=16.545..617691.016 rows=9967 loops=1)
               Join Filter: (l.upload_date = (subplan))
               ->  Seq Scan on test_upload l  (cost=0.00..894.00 rows=50000 width=28) (actual time=0.012..41.359 rows=50000 loops=1)
               ->  Index Scan using test_user_pkey on test_user u  (cost=0.00..0.28 rows=1 width=20) (actual time=0.009..0.011 rows=1 loops=50000)
                     Index Cond: (u.user_id = l.user_id)
               SubPlan
                 ->  Aggregate  (cost=1019.02..1019.03 rows=1 width=4) (actual time=12.333..12.333 rows=1 loops=50000)
                       ->  Seq Scan on test_upload  (cost=0.00..1019.00 rows=6 width=4) (actual time=1.967..12.323 rows=6 loops=50000)
                             Filter: (user_id = $0)
 Total runtime: 617733.733 ms
</pre>
</div>
<p>
Po pierwsze, podzapytanie jest wykonywane dosyć nieoptymalnie &#8211; skanowana jest cała tabela, aby znaleźć pliki jednego użytkownika. Po drugie, podzapytanie jest wykonywane 50000 razy, raz na każdy rekord analizowany w głównym zapytaniu. Z pierwszym problemem można sobie poradzić przez dodanie odpowiedniego indeksu:
</p>
<pre>
CREATE INDEX upload_user_id_idx ON test_upload(user_id);
</pre>
<p>
Ta prosta zmiana spowodowała, że czas zapytania skrócił się do ok 0,6 sekundy:
</p>
<div style="width: 580px; overflow: auto; background: #FFFFB3"">
<pre class="noborder">
                                                                          QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=422099.29..422099.31 rows=10 width=40) (actual time=614.207..614.214 rows=10 loops=1)
   ->  Sort  (cost=422099.29..422099.47 rows=74 width=40) (actual time=614.205..614.208 rows=10 loops=1)
         Sort Key: l.upload_time
         ->  Merge Join  (cost=0.00..422096.99 rows=74 width=40) (actual time=0.071..602.762 rows=9967 loops=1)
               Merge Cond: (u.user_id = l.user_id)
               Join Filter: (l.upload_time = (subplan))
               ->  Index Scan using test_user_pkey on test_user u  (cost=0.00..378.25 rows=10000 width=20) (actual time=0.021..5.243 rows=10000 loops=1)
               ->  Index Scan using upload_user_id_idx on test_upload l  (cost=0.00..1595.25 rows=50000 width=28) (actual time=0.014..24.648 rows=50000 loops=1)
               SubPlan
                 ->  Aggregate  (cost=8.38..8.39 rows=1 width=4) (actual time=0.009..0.009 rows=1 loops=50000)
                       ->  Index Scan using upload_user_id_idx on test_upload  (cost=0.00..8.36 rows=6 width=4) (actual time=0.003..0.006 rows=6 loops=50000)
                             Index Cond: (user_id = $0)
 Total runtime: 614.338 ms
</pre>
</div>
<p>
W tym artykule jednak chcieliśmy przedstawić inną technikę optymalizacji podzapytań: poprzez ich eliminację. Wiadomo, że podzapytanie, którego nie ma, nie potrzebuje czasu. Zależy nam jednak na tym, aby eliminując podzapytanie, całość była nadal poprawna, tj. zwracała właściwe wyniki, czyli musimy je czymś zastąpić. Kluczem do zastosowania tej techniki jest zamiana podzapytania na złączenie. Złączenia są łatwiejsze dla systemu do obliczenia chociażby z tego względu, że istnieją różne algorytmy realizacji złączeń i optymalizator ma tutaj większe &#8220;pole do popisu&#8221;. Poza tym hurtowy dostęp do dużej tabeli jest zwykle tańszy niż tysiące małych, prostych dostępów wybierających po kilka rekordów.</p>
<p>Wiele silników baz danych potrafi wykonać takie przekształcenie automatycznie dla podzapytań nieskorelowanych, jednak w tym przypadku mamy do czynienia z podzapytaniem skorelowanym, ponieważ odwołuje się ono do zapytania otaczającego. Komercyjne systemy baz danych poradziłyby sobie i z tym przypadkiem, ale jeśli nie mamy tego szczęścia ich używać, musimy poradzić sobie sami.
</p>
<p>
W pierwszej kolejności usuńmy niepotrzebny już indeks <i>upload_user_id_idx</i>. Następnie wykonajmy zapytanie:
</p>
<pre>
SELECT user_id, max(upload_time)
FROM test_upload
GROUP BY user_id;
</pre>
<p>
Wykonuje się jedynie 63 ms i zawiera wszystkie potrzebne dane do sprawdzenia, czy dany plik użytkownika jest tym &#8220;ostatnim&#8221; i powinien być uwzględniony w wyniku. Teraz tylko trzeba to zapytanie połączyć z pełną zawartością tabeli z użytkownikami i plikami, i na końcu odpowiednio posortować:
</p>
<pre>
SELECT name, path, upload_time
FROM test_user u
  JOIN test_upload l ON (u.user_id = l.user_id)
  JOIN (
          SELECT user_id, max(upload_time) AS ud
          FROM test_upload
          GROUP BY user_id
       ) x
  ON (x.user_id = l.user_id AND l.upload_time = ud)
ORDER BY upload_time DESC
LIMIT 10;
</pre>
<p>
Czas wykonania tego zapytania wyniósł 167 ms, czyli wyeliminowanie podzapytania zapewniło prawie 4-krotne przyspieszenie:
</p>
<div style="width: 580px; overflow: auto; background: #FFFFB3"">
<pre  class="noborder">
                                                                    QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=4324.97..4324.99 rows=10 width=40) (actual time=166.005..166.011 rows=10 loops=1)
   ->  Sort  (cost=4324.97..4325.13 rows=66 width=40) (actual time=166.003..166.004 rows=10 loops=1)
         Sort Key: l.upload_date
         ->  Hash Join  (cost=2053.31..4322.97 rows=66 width=40) (actual time=93.562..155.832 rows=9967 loops=1)
               Hash Cond: ((l.user_id = u.user_id) AND (l.upload_date = x.ud))
               ->  Seq Scan on test_upload l  (cost=0.00..894.00 rows=50000 width=28) (actual time=0.010..18.370 rows=50000 loops=1)
               ->  Hash  (cost=1920.04..1920.04 rows=8885 width=28) (actual time=93.524..93.524 rows=9922 loops=1)
                     ->  Hash Join  (cost=1498.00..1920.04 rows=8885 width=28) (actual time=68.164..86.451 rows=9922 loops=1)
                           Hash Cond: (x.user_id = u.user_id)
                           ->  HashAggregate  (cost=1144.00..1255.06 rows=8885 width=8) (actual time=52.218..57.378 rows=9922 loops=1)
                                 ->  Seq Scan on test_upload  (cost=0.00..894.00 rows=50000 width=8) (actual time=0.008..18.568 rows=50000 loops=1)
                           ->  Hash  (cost=229.00..229.00 rows=10000 width=20) (actual time=15.925..15.925 rows=10000 loops=1)
                                 ->  Seq Scan on test_user u  (cost=0.00..229.00 rows=10000 width=20) (actual time=0.007..5.835 rows=10000 loops=1)
 Total runtime: 166.287 ms
</pre>
</div>
<p>
Testy przeprowadziliśmy na małym zbiorze danych, który całkowicie mieścił się w pamięci. Co się stanie jednak, jeśli zwiększymy ilość danych? Aby to sprawdzić, wygenerowaliśmy drugi, duży zbiór danych tak, by tabela użytkowników zawierała 500 tys. rekordów, a tabela z dodanymi plikami &#8211; 10 mln. Tym razem wersja z podzapytaniem wykorzystującym indeks na test_upload(user_id) wykonywała się ponad 30 minut i musieliśmy przerwać test. Natomiast wersja bez podzapytania skorelowanego zajęła 44 sekundy. Z kolei stosunek kosztów obu zapytań oszacowany przez optymalizator PostgreSQL wyniósł ok. 1600:1. Różnice są dlatego tak znaczne, że tym razem dane nie mieszczą się w całości w pamięci i każdorazowe wykonanie podzapytania wymagało fizycznego dostępu do przypadkowego miejsca dysku. Tymczasem w drugim przypadku rekordy są pobierane sekwencyjnie i nie traci się czasu na pozycjonowanie głowic dysku. Wersji z podzapytaniem bez indeksu nie sprawdzaliśmy na tym zbiorze danych. Gdybyśmy to zrobili, prawdopodobnie datę publikacji tego artykułu należałoby przesunąć o rok.
</p>
<p>Jak widać, użycie złączenia i podzapytania nieskorelowanego we FROM zamiast podzapytania skorelowanego w WHERE może zapewnić duże zyski wydajności. Należy jednak pamiętać też o zagrożeniach jakie niesie ta technika. Przede wszystkim zmieniając postać zapytania, ryzykujemy, że nowe zapytanie nie będzie równoważne oryginałowi. W wielu przypadkach zamiana może wydawać się mechaniczna, ale należy bardzo uważać, żeby dodając kolejne złączenie nie wprowadzić duplikatów rekordów. Złączenia, w przeciwieństwie do zastosowania operatorów EXISTS, IN czy &#8216;=&#8217; w sekcji WHERE, mogą nie tylko eliminować rekordy, ale również je powielać. Problem ten rozwiązuje się zwykle przez upewnienie się, że nigdy nie zostanie dołączony więcej niż jeden rekord (w naszym przypadku poprzez proste spostrzeżenie, że user_id jest unikalne), albo przez dodanie słowa DISTINCT, tak aby ewentualne duplikaty usunąć na końcu. Drugim zagrożeniem jest próba stosowania tej techniki zawsze i wszędzie, gdzie się tylko da. A niestety nie zawsze daje ona zyski w wydajności. Prezentowane zapytanie udało się nam przyspieszyć do 0,4 ms na małym zbiorze danych i 1 s na zbiorze dużym. Jak? To już temat na osobny artykuł.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sages.com.pl/2010/03/optymalizacja-zapytan-z-podzapytaniami/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Witamy na naszym blogu</title>
		<link>http://blog.sages.com.pl/2010/03/witamy-na-naszym-blogu/</link>
		<comments>http://blog.sages.com.pl/2010/03/witamy-na-naszym-blogu/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 11:38:35 +0000</pubDate>
		<dc:creator>Team Sages</dc:creator>
				<category><![CDATA[Sages]]></category>

		<guid isPermaLink="false">http://blog.sages.com.pl/?p=14</guid>
		<description><![CDATA[Serdecznie witamy na blogu konsultantów firmy Sages. Od dzisiaj rozpoczynamy publikację artykułów, przygotowanych przez naszych specjalistów, w których postaramy się przedstawić rozwiązania problemów, na jakie często natrafiamy w czasie naszej pracy, wskazać ciekawe zastosowania i integracje istniejących technologii, a także zaprezentować pomysły, który dopiero w przyszłości mogą potencjalnie stać się codziennie wykorzystywanymi narzędziami.
Liczymy na odzew [...]]]></description>
			<content:encoded><![CDATA[<p>Serdecznie witamy na blogu konsultantów firmy Sages. Od dzisiaj rozpoczynamy publikację artykułów, przygotowanych przez naszych specjalistów, w których postaramy się przedstawić rozwiązania problemów, na jakie często natrafiamy w czasie naszej pracy, wskazać ciekawe zastosowania i integracje istniejących technologii, a także zaprezentować pomysły, który dopiero w przyszłości mogą potencjalnie stać się codziennie wykorzystywanymi narzędziami.</p>
<p>Liczymy na odzew i zapraszamy do regularnej lektury!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sages.com.pl/2010/03/witamy-na-naszym-blogu/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
