czwartek, 27 listopada 2008

Się działo się... Warsjawa Eclipse DemoCamp 2008

Było minęło... ale, że było fajnie, opiszę pokrótce jak to miało miejsce.

Warsjawa Eclipse DemoCamp 2008, odbyła się w ubiegłą sobotę, 22 listopada na terenie wydziału MIMUW Uniwersytetu Warszawskiego. Swoją obecnością zaszczyciło nas ponad 150 słuchaczy, ale zacznijmy od początku (relacja z czuba i na żywo)...

a na początku był Waldemar Kot. Waldi po nieudanych inwestycjach w przemysł księgarski



powrócił w klimaty bliższe naszemu sercu. Jego prezentację dotyczącą mechanizmu Publish-Subscribe wysłuchała pełna sala.



Było jak zwykle u Waldiego. Ciekawie, fachowo i konkretnie. Po prostu rewelacja. Choć w sobotę rano nikt jeszcze w to nie wierzył, poziom widowiska nie opadł do sameog końca. Kolejna prelekcja "Taking SQL IDEs from the Stone Age to the 21st century" Wassima Mełhema tylko podsyciła apetyt.

Szczęście w nieszczęściu, że zaraz potem był czas na przerwę. Obiadową. Na każdego czekał kotlecik oraz zupka. Mniam...



To co zapowiadała pierwsza część spotkania w drugiej stało się faktem. Worek z bramkami się rozwiązał. Co prawda Eclipse RPC nie do końca zmieścił się w przeglądarce Jacek Pospychały, ale to był dopiero początek emocji. Telekomunikacja w Javie pełna była pełna ostrej walki bark w bark Tomasz Zieleniewskiego oraz Waldemara Kota. Na szczęście sędziowie byli stanowczy i konsekwentni. O żadnych zadymach nie mogło być zatem mowy. W dogrywce 2 krótsze prezentacje - Dojo Toolkit Łukasza Lenarta oraz Spring Dynamic Modules Agaty i Jacka Laskowskich i każdy mógł być zadowolony ze zwycięskiego remisu.

Tenże uczciliśmy w Lolku. Co prawda nikt się nie spodziewał tylu zwycięzców i miejsc brakło, ale cóż. Taka okazja nie trafia się na co dzień...



Było super. Pozostaje mi tylko mieć nadzieję, że to nie pierwszy i nie ostatni raz.

poniedziałek, 17 listopada 2008

Warsjawa Eclipse DemoCamp 2008

Już w tą sobotę, 22 listopada w Warszawie w budynku MIMUW przy ul. Banacha 2 w sali 5440 w godz. 9:00-17:00 odbędzie się konferencja Warsjawa Eclipse DemoCamp 2008 organizowana przez Warszawski JUG. Konferencja dotyczy tego, co w jawie piszczy, a dokładniej:

09:00 - 09:45 Rejestracja (w trakcie: kawa/herbata, stół szwedzki)
09:45 - 11:15 Comet, Bayeux i mechanizm Publish-Subscribe poprzez HTTP - Waldemar "waldi" Kot (Warszawa JUG)
11:30 - 13:00 Taking SQL IDEs from the Stone Age to the 21st century - Wassim Melhem (Eclipse Foundation)
13:00 - 14:00 Przerwa obiadowa (zarejestrowani bezpłatnie!)
14:00 - 14:45 Czy Eclipse RCP mieści się w przeglądarce? - Jacek Pospychała (IBM Eclipse Support Center)
14:50 - 15:35 Telekomunikacja w Javie - kilka słów o konwergencji i usługach w telekomunikacji - Tomasz Zieleniewski (TouK)
15:40 - 16:10 Dojo Toolkit - Łukasz Lenart (Warszawa JUG)
16:15 - 16:45 OSGi + Spring Framework = Spring Dynamic Modules - Jacek Laskowski (Warszawa JUG)
16:45 - 17:00 Losowanie nagrody głównej - Kapituła Warsjavy
17:00 Spotkania integracyjnego czas zacząć! - Kapituła Warsjavy

Zauważyliście pewnie kilka frykasów: stół szwedzki, obiadek, imprezka, tajemnicza nagroda główna... To dzięki naszym wspaniałym sponsorom:
7N, Javatech, e-point, Touk, Eclipse, JetBrains.

Konferencja jest całkowicie bezpłatna, pod warunkiem rejestracji. Osobiście będę sprawdzał listę obecności ;).

Więcej informacji na stronie warsjawa.pl

W imieniu organizatorów serdecznie zapraszam.

sobota, 15 listopada 2008

kolorowanie kodu źródłowego na blogu

Już od moich pierwszych spotkań z blogspotem brakowało mi możliwości ładnej prezentacji kodu źródłowego na jego stronach. Inne serwisy, takie jak wikidot.com pozostawiały (i pozostawiają) go daleko w tyle. W końcu jednak mi się udało coś na to poradzić...

Dokładnie nie mi, tylko twórcom projektu syntaxhighlighter. To narzędzie, to nic więcej jak zestaw skryptów JavaScript, które kolorują i ozdabiają kod źródłowy.

Aby skorzystać z tego wynalazku dołączyłem do swojego bloga dedykowany widget. Na stronie http://fazibear.googlepages.com/blogger.html kliknąłem w ikonkę Add to Blogger. Pasek tytułowy pozostawiłem pusty, dzięki temu widget pozostaje niewidoczny.

Jeśli chodzi o instalację skryptów syntaxhighlighter, to by było na tyle. Uźycie ich, jest równie proste. Kod źródłowy wystarczy otoczyć tagami pre lub textarea o parametrach name="code" class="kod-języka". Obsługiwane języki to:

język kod języka
C++ cpp, c, c++
C# c#, c-sharp, csharp
CSS css
Delphi delphi, pascal
Java java
Java Script js, jscript, javascript
PHP php
Python py, python
Ruby rb, ruby, rails, ror
Sql sql
VB vb, vb.net
XML/HTML xml, html, xhtml, xslt


efekt jest mniej więcej taki:



Niezłe... ale to jeszcze nie koniec. Takie bloki kodu można konfigurować. W tym celu kod języka należy uzupełnić opcjami rozdzielonymi dwukropkiem. Dostępnych jest 5 parametrów konfiguracyjnych:



nogutter Ukrywa boczny pasek z numerami linii.
nocontrols Ukrywa górne kontrolki.
collapse Automatycznie wcięcia.
firstline[value] Ustawia numer pierwszej linii value. Domyślna wartość to 1.
showcolumns Pokaże numery kolumn


i tak na przykład


<pre name="code" class="java:nocontrols:firstline[76]"">
public class Hello {
private static final String HELLO_MSG = "Dzień dobry, cześć i czołem!"
}
</pre>


wyświetli blok kodu bez kontrolek, w dodatku pierwsza linia kodu będzie miała numer 76.

No po prostu rewelacja. Niestety nie zupełnie. Syntaxhighlighter nie jest też pozbawiony bugów. Jeden z nich można podziwiać na przykładzie mojego poprzedniego posta "Uprowadzenie sesji". Prezentuję tam klasę:


public class UserManager {

private List users;

public UserManager() {
users = new ArrayList(3);
User user1 = new User();
user1.setLogin("user1");
user1.setPassword("pass1");
user1.setBalance(54321);
User user2 = new User();
user2.setLogin("user2");
user2.setPassword("pass2");
user2.setBalance(65354);
User user3 = new User();
user3.setLogin("user3");
user3.setPassword("pass3");
user3.setBalance(91254);
users.add(user1);
users.add(user2);
users.add(user3);
}

public boolean check(String login, String password) {
User user = loadUser(login);
return user != null && user.getPassword().equals(password);
}

public User loadUser(String login) {
for (User user : users) {
if (user.getLogin().equals(login)) {
return user;
}
}
return null;
}

public void setUsers(List users) {
this.users = users;
}
}


która po upiększeniu przez syntaxhighlighter wygląda tak:



public class UserManager {

private List users;

public UserManager() {
users = new ArrayList(3);
User user1 = new User();
user1.setLogin("user1");
user1.setPassword("pass1");
user1.setBalance(54321);
User user2 = new User();
user2.setLogin("user2");
user2.setPassword("pass2");
user2.setBalance(65354);
User user3 = new User();
user3.setLogin("user3");
user3.setPassword("pass3");
user3.setBalance(91254);
users.add(user1);
users.add(user2);
users.add(user3);
}

public boolean check(String login, String password) {
User user = loadUser(login);
return user != null && user.getPassword().equals(password);
}

public User loadUser(String login) {
for (User user : users) {
if (user.getLogin().equals(login)) {
return user;
}
}
return null;
}

public void setUsers(List users) {
this.users = users;
}
}


Skąd na końcu </user></user></user>?
Nie wiadomo...

wtorek, 4 listopada 2008

uprowadzenie sesji

Dawno nic nie napisałem ciekawego. Ostatnio same zawirowania. Do tego zainstalowałem StarCrafta na Ubuntu oraz znalazłem sposób na kolorowanie kodu w tym oto blogu. Wydaje mi się on teraz nieco bardziej czytelny. Pomału na szczęście wychodzę na prostą, przynajmniej jeśli chodzi o bloga...

Jako młode pachole, prawie każdy z nas chciał być strażakiem, policjantem, pilotem... Ja chciałem być pilotem, a najlepiej astronautą. Nic z tego niestety nie wyszło.

Jako starsze pachole, już nie każdy, ale przynajmniej niektórzy chcieli być hakerami. Łamać hasła, włamywać się do NASA, wykradać materiały o UFO. Wszystkich, którzy wciąż mają takie zamiary, uprzejmie informuję, że ich do tego broń boże nie zachęcam. Jednak jak coś już wykradniesz z NASA, pochwal się w komentarzu.

No ale do rzeczy. Dziś wpis z gatunku bezpieczeństwa. Pomysł nań nasunął mi się podczas rozważań co do mechanizmu logowania w prostej aplikacji WWW. Aplikacja prosta, więc możnaby autoryzację zaimplementować samemu. Można też użyć czegoś gotowego, np. SpringSecurity. Rozwiązanie to raczej pewne i sprawdzone, ale czy nie będzie to strzelanie armatą do muchy?

Rozważmy zatem pierwsze podejście.

Stworzyłem przykładową aplikację, która przechowuje poufne informacje o zarobkach pracowników. Na aplikację składają się 3 klasy:
User, która zawiera informacje o pracowniku

public class User {
private String login;
private String password;
private int balance;

public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}

}

UserManager to klasa, która umożliwia proste operacje na użytkownikach. W prawdziwej aplikacji klasa zapewne będzie ukryta za interfejsem, korzystając do tego z obiektów DAO i inne takie. Na potrzeby jednak naszej aplikacji poniższy menadżer wystarczy:

public class UserManager {

private List<User> users;

public UserManager() {
users = new ArrayList<User>(3);
User user1 = new User();
user1.setLogin("user1");
user1.setPassword("pass1");
user1.setBalance(54321);
User user2 = new User();
user2.setLogin("user2");
user2.setPassword("pass2");
user2.setBalance(65354);
User user3 = new User();
user3.setLogin("user3");
user3.setPassword("pass3");
user3.setBalance(91254);
users.add(user1);
users.add(user2);
users.add(user3);
}

public boolean check(String login, String password) {
User user = loadUser(login);
return user != null && user.getPassword().equals(password);
}

public User loadUser(String login) {
for (User user : users) {
if (user.getLogin().equals(login)) {
return user;
}
}
return null;
}

public void setUsers(List<User> users) {
this.users = users;
}
}

Pozostała jeszcze klasa UserLoginBean, która odpowiada za zalogowanie użytkownika oraz sprawdza czy i jaki użytkownik jest w danym momencie zalogowany.

public class UserLoginBean {
private String login;
private String password;
private User loggedUser;
private UserManager userManager;

public String login() {
if (userManager.check(login, password)) {
loggedUser = userManager.loadUser(login);
login = null;
password = null;
return "success";
}
return "faliture";

}

public String logout() {
userManager = null;
return null;
}

public boolean isUserLogged() {
return loggedUser != null;
}

public User getLoggedUser() {
return loggedUser;
}

public void setUserManager(UserManager userManager) {
this.userManager = userManager;
}

public String getLogin() {
return login;
}

public void setLogin(String login) {
this.login = login;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}

Informacje o zalogowanym użytkowniku przechowywane są w sesji, stąd też taki zasięg ziarna UserLoginBean. Widać to w poniższym pliku faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
<managed-bean>
<managed-bean-name>userManager</managed-bean-name>
<managed-bean-class>pl.matt.UserManager</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>userLoginBean</managed-bean-name>
<managed-bean-class>pl.matt.UserLoginBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>userManager</property-name>
<value>#{userManager}</value>
</managed-property>
</managed-bean>
<navigation-rule>
<from-view-id>/pages/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/pages/home.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>faliture</from-outcome>
<to-view-id>/pages/login.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
</faces-config>

do kompletu aplikacji brakuje jeszcze pliku web.xml

<?xml version="1.0"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>sessionHijacktion</display-name>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<context-param>
<param-name>facelets.REFRESH_PERIOD</param-name>
<param-value>2</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
</web-app>

oraz kilku stronek JSF. Dwie pierwsze, nie są specjalnie ciekawe. Strona login.xhtml zawiera prosty formularz, umożliwiający zalogowanie użytkownika do aplikacji

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core">
<f:loadBundle basename="resources" var="msg" />
<ui:composition template="/templates/common.xhtml">
<ui:define name="pageTitle">Login form</ui:define>
<ui:define name="pageHeader">Proszę się zalogować</ui:define>
<ui:define name="body">
<h:message showSummary="true" showDetail="false" style="color: red; font-weight: bold;" for="name" />
<h:form id="helloForm">
<h:panelGrid columns="2">
<h:outputText value="login" />
<h:inputText required="true" id="name" value="#{userLoginBean.login}" />
<h:outputText value="password" />
<h:inputSecret required="true" id="password" value="#{userLoginBean.password}" />
<h:outputText value="" />
<h:commandButton id="submit" action="#{userLoginBean.login}" value="zaloguj" />
</h:panelGrid>
</h:form>
</ui:define>
</ui:composition>
</html>

Natomiast strona home.xhtml przedstawia poufne informacje o każdym z użytkowników

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">

<f:loadBundle basename="resources" var="msg" />
<ui:composition template="/templates/common.xhtml">
<ui:param name="loggedOnly" value="true" />
<ui:define name="pageTitle">Greeting to User</ui:define>
<ui:define name="pageHeader">Greeting Page</ui:define>
<ui:define name="body">
#{msg.greeting} #{userLoginBean.loggedUser.login} masz na koncie #{userLoginBean.loggedUser.balance}
</ui:define>
</ui:composition>
</html>

Na uwagę zasługuje parametr

<ui:param name="loggedOnly" value="true" />

dzięki któremu treść strony home.xhtml jest widoczna tylko dla zalogowanych użytkowników.
Jak to działa? Wszystko staje się jasne, gdy obejrzymy plik /templates/common.xhtml zawierający szablon każdej strony aplikacji:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">

<f:loadBundle basename="resources" var="msg" />
<head>
<title><ui:insert name="pageTitle">Page Title</ui:insert></title>
</head>

<body bgcolor="#ffffff">
<table style="border:1px solid #CAD6E0" align="center" cellpadding="0" cellspacing="0" border="0" width="400">
<tbody>

<tr>
<td class="header" height="42" align="center" valign="middle" width="100%" bgcolor="#E4EBEB">
<ui:insert name="pageHeader">Page Header</ui:insert>
</td>
</tr>
<tr>
<td height="1" width="100%" bgcolor="#CAD6E0"></td>
</tr>

<tr>
<td width="100%" colspan="2">
<table width="100%" style="height:150px" align="left" cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td align="center" width="100%" valign="middle">
<ui:fragment rendered="#{!loggedOnly || userLoginBean.userLogged}">
<ui:insert name="body">Page Body</ui:insert>
</ui:fragment>
<ui:fragment rendered="#{loggedOnly and !userLoginBean.userLogged}">
Proszę się zalogować
</ui:fragment>
</td>
</tr>
</tbody>
</table>
</td>
</tr>

<tr>
<td colspan="2" valign="bottom" height="1" width="100%" bgcolor="#CAD6E0"></td>
</tr>
</tbody>
</table>
</body>

</html>

a dokładnie jego fragment:

<ui:fragment rendered="#{!loggedOnly || userLoginBean.userLogged}">
<ui:insert name="body">Page Body</ui:insert>
</ui:fragment>
<ui:fragment rendered="#{loggedOnly and !userLoginBean.userLogged}">
Proszę się zalogować
</ui:fragment>

Jeżeli parametr loggedOnly ustawiony jest na wartość true, zawartość strony zostanie wyświetlona, jedynie zalogowanemu użytkownikowi.

Prosta aplikacja, proste zabezpieczenie, ale działają.
Czy na pewno?

Odpalam aplikację na serwerze JBoss 4.2.1.GA.
Wyłączam w przeglądarce obsługę ciasteczek. Próbuję się zalogować, ale nie znam hasła.


mogę się więc jedynie obejść smakiem:


Niby nic. Dla mojego żądania serwer otworzył nową sesję, a jej identyfikator dokleił do adresu URL. Nie ma się co dziwić, w końcu moja przeglądarka nie obsługuję ciasteczek.

Teraz, jako, że nie znam hasła, a chcę się zalogować, kopiuję URL z identyfikatorem sesji i przesyłam mojej ofierze. Oczywiście proszę ją podstępnie o zalogowanie się.


Ofiara moja grzecznie klika w URL i się loguje jako użytkownik "user3".

A ja jeszcze grzeczniej, zmieniam w moim URLu adres z login.jsf na home.jsf (zostawiam identyfikator sesji) i...



Bingo. Też jestem zalogowany!

Jak to się stało? Ano przesyłając adres z dopiętym identyfikatorem sesji, ofiara ataku zalogowała się do aplikacji korzystając mojej sesji. Ja następnie otworzyłem zabezpieczoną stronę, korzystając już z naszej wspólnej sesji, w której właśnie się ktoś zalogował.

Jak się przed takim atakiem bronić? Najlepszym rozwiązaniem jest przydzielanie nowej sesji użytkownikowi przy logowaniu. Tak robi właśnie SpringSecurity. Przy okazji kopiuje on z automatu wszystkie obiekty ze starej sesji, do nowej. Tak więc fakt całej operacji jest dla użytkownika jest niezauważalny.
Więcej o tym ataku znajdziesz na Wikipedii.
Co ciekawe, przesiadka na JBossa 4.2.2.GA uniemożliwia już ten atak, przynajmniej w takiej prostej formie, jak ta opisana powyżej.

Pozostaje tylko się domyślać, przed iloma jeszcze nieznanymi mi typami ataków chroni mnie i moją aplikację SpringSecurity.