środa, 26 października 2011

Ruby on Rails 3.1.1 i Redis jako SessionStore

Ruby on Rails od wersji 2.0 do realizacji pamięci sesji (ang. session store) używa ciasteczek, poprzez mechanizm CookieStore. Jest to koncepcja, z którą mam pewne kłopoty i której nie rozumiem.
W teorii sesja ma być obiektem zapamiętywanym po stronie serwera, a tutaj jest zapamiętywana po stronie użytkownika w ciasteczkach...Przypomina mi to trochę sytuację używania metody GET protokołu HTTP do zmiany danych na serwerze, oczywiście można ją do tego wykorzystać, ale to nie znaczy, że trzeba, a nawet nie należy! Specyfikacja protokołu definiuje do tego celu inne żądania (ang. requests), jak chociaż by POST.
Ale wracając do mechanizmu CookieStore, posiada on jeszcze jedną wadę, dane zapisane w ciasteczkach nie są szyfrowane! Jest to zwykły plaintext z kodowaniem Base64! Wszyscy mogą odczytać informacje zapisane w sesji.
Oczywiście, można powiedzieć, że zapisywanie wrażliwych informacji w sesji jest praktyką dość ryzykowną, niemniej w pewnych sytuacjach może się okazać zdrowym kompromisem pomiędzy bezpieczeństwem użytkownika, a szybkością działania całej aplikacji.
Na szczęście Ruby on Rails umożliwia bardzo prosty sposób zmiany "session stora", wystarczy edytować plik konfiguracyjny config/initializers/session_store.rb, który standardowo wygląda mniej więcej tak:

config/initializers/session_store.rb:

# Be sure to restart your server when you modify this file.
{ApplicationName}::Application.config.session_store :cookie_store, 

key: '_{ApplicationName}_session'

# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails generate session_migration")
# {ApplicationName}::Application.config.session_store :active_record_store

Jak widać na listingu powyżej, bardzo prosto (jak zawszy w przypadku railsów ;P) można pamięć sesji realizować za pomocą bazy danych. Jest to dość bezpieczny sposób. Aplikację można uruchomić na kilku serwerach, które będą się łączyć z bazą danych i utrzymywać informację o połączeniu z użytkownikiem.
Sposób fajny tyle tylko, że wolny...każdy 'select' ma znaczenie...jak mawiają starzy górale...

Ciekawym kompromisem zdaje się być wykorzystanie jako SessionStore szybkiej bazy danych jak na przykład Redis. Jest to moim zdaniem jeden z ciekawszych projektów wpisujących się w nurt NoSQL databases. Po informacje o tej bazie odsyłam na stronę projektu. Tutaj tylko powiem, że jest to baza typu key-value, która rezyduje w pamięci RAM (stąd jej duża szybkość), dodatkowo od czasu do czasu zrzucająca informacje na dysk.

Pierwszym krokiem w celu wykorzystania bazy Redis jako SessionStore (poza oczywiście zainstalowaniem samej bazy) jest zainstalowanie gemu redis-store. 
W przypadku Ruby on Rails 3 wystarczy dodać gem 'redis-store' do pliku Gemfile i wywołać komendę bundle. 
Kolejnym krokiem jest edycja pliku config/initializers/session_store.rb
config/initializers/session_store.rb:
{ApplicationName}::Application.config.session_store :redis_session_store, :servers => "redis://my_secreat_password@127.0.0.1:6379/2"
gdzie poza wyborem SessionStora jako :redis_session_store przekazujemy hasha z informacjami jak należy łączyć się z bazą, tj: hasło, adres bazy, na jakim porcie nasłuchuje i którą bazę użyć. Można także użyć opcjonalne parametry: :expire_in - czas TTL w sekundach oraz :has_prefix - do ustawienia przestrzeni nazw.

To wszystko! Od teraz wszystkie informacje związane z sesją będą zapisywane w bazie Redis! A nasza aplikacja będzie szybka i bezpieczna:)