Uwierzytelnianie w oparciu o tabelę bazy danych
Wprowadzenie
Zend_Auth_Adapter_DbTable zapewnia możliwość
przeprowadzenia uwierzytelniania w oparciu o dane przechowywane w
tabeli bazy danych. Z tego względu, że klasa
Zend_Auth_Adapter_DbTable wymaga przekazania instancji
klasy Zend_Db_Adapter_Abstract do jej konstruktora,
każda ta instancja jest powiązana z konkretnym połączeniem do bazy
danych. Inne opcje konfiguracyjne mogą być ustawione za pomocą
konstruktora lub za pomocą metod instancji, po jednej dla każdej z
opcji.
Dostępne opcje konfiguracyjne to:
tableName: Jest to nazwa tabeli bazy
danych, która zawiera dane uwierzytelniania i do której
jest przeprowadzane zapytanie uwierzytelniające.
identityColumn: Jest to nazwa kolumny
tabeli bazy danych, która reprezentuje tożsamość.
Kolumna tożsamości musi zawierać unikalne wartości,
na przykład takie jak nazwa użytkownika czy adres
e-mail.
credentialColumn: Jest to nazwa kolumny
tabeli bazy danych, która reprezentuje wartość
uwierzytelniającą. W prostym schemacie uwierzytelniania
opartym o nazwę tożsamości i hasło, wartość
uwierzytelniająca odpowiada hasłu. Zobacz także opcję
credentialTreatment.
credentialTreatment: W wielu przypadkach,
hasło i inne wrażliwe dane są zszyfrowane, haszowane,
zakodowane lub w inny sposób przetworzone przez
jakąś funkcję lub algorytm. Określając metodę
przerobienia danych, taką jak na przykład
'MD5(?)' czy 'PASSWORD(?)',
programista może użyć konkretnej funkcji SQL na danych
uwierzytelniających. Z tego względu, że te funkcje są
specyficzne dla konkretnych systemów baz danych, zajrzyj
do odpowiednich dokumentacji aby sprawdzić dostępność
takich funkcji dla twojego systemu bazy danych.
Podstawowe użycie
Jak wyjaśniono we wprowadzeniu, konstruktor klasy
Zend_Auth_Adapter_DbTable wymaga przekazania mu
instancji klasy Zend_Db_Adapter_Abstract,
zapewniającej połączenie do bazy danych, z którym powiązana jest
instancja adaptera uwierzytelniania. Na początku powinno być
utworzone połączenie do bazy danych.
Poniższy kod tworzy adapter bazy danych przechowywanej w pamięci,
tworzy prostą strukturę tabeli, a następnie wstawia wiersz, w
oparciu o który przeprowadzimy później zapytanie
uwierzytelniające. Ten przykład wymaga dostępnego rozszerzenia
PDO SQLite:
':memory:'));
// Budujemy zapytanie tworzące prostą tabelę
$sqlCreate = 'CREATE TABLE [users] ('
. '[id] INTEGER NOT NULL PRIMARY KEY, '
. '[username] VARCHAR(50) UNIQUE NOT NULL, '
. '[password] VARCHAR(32) NULL, '
. '[real_name] VARCHAR(150) NULL)';
// Tworzymy tabelę z danymi uwierzytelniania
$dbAdapter->query($sqlCreate);
// Budujemy zapytanie wstawiające wiersz, dla którego możemy przeprowadzić
// próbę uwierzytelniania
$sqlInsert = "INSERT INTO users (username, password, real_name) "
. "VALUES ('my_username', 'my_password', 'My Real Name')";
// Wstawiamy dane
$dbAdapter->query($sqlInsert);
]]>
Gdy połączenie do bazy danych oraz dane w tabeli są już
dostępne, możesz utworzyć instancję klasy
Zend_Auth_Adapter_DbTable. Opcje konfiguracyjne
mogą być przekazane do konstruktora lub przekazane jako
parametry do metod dostępowych już po utworzeniu instancji:
setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password')
;
]]>
W tym momencie intancja adaptera uwierzytelniania jest gotowa
do przeprowadzenia zapytań uwierzytelniających. W celu
utworzenia zapytania, wejściowe dane uwierzytelniania
są przekazywane do adaptera przed wywołaniem metody
authenticate():
setIdentity('my_username')
->setCredential('my_password');
// Przeprowadzamy zapytanie uwierzytelniające, zapisując rezultat
$result = $authAdapter->authenticate();
]]>
Oprócz możliwości użycia metody getIdentity()
obiektu rezultatu uwierzytelniania, obiekt
Zend_Auth_Adapter_DbTable pozwala także na
odebranie wiersza tabeli po udanym uwierzytelnieniu.
getIdentity() . "\n\n";
// Wyświetlamy wiersz rezultatów
print_r($authAdapter->getResultRowObject());
/* Wyświetlone dane:
my_username
Array
(
[id] => 1
[username] => my_username
[password] => my_password
[real_name] => My Real Name
)
*/
]]>
Z tego względu, że wiersz tabeli zawiera dane potrzebne do
uwierzytelniania, ważne jest, aby dane były zabezpieczone przed
dostępem przez osoby nieuprawnione.
Zaawansowane użycie: Stałe przechowywanie obiektu DbTable Result
Domyślnie Zend_Auth_Adapter_DbTable po udanym
uwierzytelnieniu zwraca do obiektu uwierzytelniającego spowrotem
tę samą tożsamość. W innym przykładzie użycia programista może
chcieć przechować w stałym mechanizmie przechowywania
Zend_Auth obiekt tożsamości zawierający inne użyteczne
informacje. W takim przypadku może użyć metody
getResultRowObject() aby zwrócić obiekt klasy
stdClass. Poniższy kod ilustruje sposób jego użycia:
_auth->authenticate($adapter);
if ($result->isValid()) {
// przechowujemy tożsamość jako obiekt, w którym zwracane są jedynie
// pola username oraz real_name
$storage = $this->_auth->getStorage();
$storage->write($adapter->getResultRowObject(array(
'username',
'real_name'
)));
// przechowujemy tożsamość jako obiekt, w którym kolumna zawierająca
// hasło została pominięta
$storage->write($adapter->getResultRowObject(
null,
'password'
));
/* ... */
} else {
/* ... */
}
]]>
Przykład zaawansowanego użycia
O ile głównym przeznaczeniem komponentu Zend_Auth
(i odpowiednio Zend_Auth_Adapter_DbTable) jest
uwierzytelnianie a nie autoryzacja,
jest kilka problemów które możemy rozwiązać odrobinę przekraczając
pole zastosowań komponentu. Zależnie od tego jak zdecydujesz
wyjaśnić swój problem, czasem może być przydatne rozwiązanie
problemu autoryzacji podczas uwierzytelniania.
Komponent Zend_Auth_Adapter_DbTable posiada
pewien wbudowany mechanizm, który może być użyty do dodania dodatkowych
warunków podczas uwierzytelniania, dzięki czemu można rozwiązać niektóre
problemy.
Innym przykładem może być implementacja mechanizmu saltingu. Jest to
technika pozwalająca w znaczny sposób zwiększyć bezpieczeństwo
aplikacji. Polega ona na tym, że dołączając do każdego hasła losowy
łańcuch znaków spowodujemy, że niemożliwe będzie przeprowadzenie
ataku brute force na bazę danych w oparciu o przygotowany słownik.
Zaczniemy od zmodyfikowania schematu tabeli bazy danych, aby móc
przechowywać nasz łańcuch znaków salt:
query($sqlAlter);
]]>
W prosty sposób wygenerujmy salt dla każdego rejestrującego się
użytkownika:
I skonfigurujmy sterownik bazy danych:
Możesz jeszcze zwiększyć bezpieczeństwo używając dodatkowo
statycznego fragmentu łańcucha znaków umieszczonego na stałe
w kodzie aplikacji. W przypadku, gdy atakujący uzyska dostęp
do bazy danych (np. za pomocą ataku SQL
injection), a nie będzie miał dostępu do kodu źródłowego,
hasła wciąż będą dla niego nieprzydatne.
Innym sposobem jest użycie metody getDbSelect()
klasy Zend_Auth_Adapter_DbTable po utworzeniu
adaptera. Ta metoda zwróci obiekt klasy Zend_Db_Select,
który ma być użyty do przeprowadzenia uwierzytalniania. Ważne jest, że
ta metoda zawsze zwróci ten sam obiekt, niezależnie od tego czy metoda
authenticate() została wywołana czy nie.
Ten obiekt nie będzie posiadał żadnych informacji
dotyczących nazwy tożsamości i hasła, ponieważ te dane będą umieszczone
tam dopiero w czasie wywołania metody authenticate().
Przykładem sytuacji w której można by użyć metody getDbSelect()
może być potrzeba sprawdzenia statusu użytkownika, czyli sprawdzenia
czy konto użytkownika jest aktywne.
getDbSelect();
$select->where('active = "TRUE"');
// uwierytelniamy, z warunkiem users.active = TRUE
$adapter->authenticate();
]]>