FK~

Moje odkazy

Ostatní odkazy

Close Windows
Nenajdete mě na Facebooku ani Twitteru
Rozpad EU
Jsem členem FSF
There Is No Cloud …just other people's computers.
Sane software manifesto / Manifest příčetného softwaru

Java na serveru: bezpečnost a EJB

vydáno: 4. 3. 2010 17:13, aktualizováno: 22. 9. 2014 16:54

Dnes budeme pokračovat v tématu zabezpečení aplikace. Od ukládání uživatelů do souboru přejdeme k praktičtější autentizaci vůči databázi. A do naší aplikace doplníme registrační formulář pro nové uživatele.

Logo OpenJDK (průhledné 1)

Uživatelé v databázi

Minule jsme pro jednoduchost uložili databázi uživatelů do souboru – použili jsme k tomu FileRealm. Jelikož je autentizační framework používaný v Javě modulární, snadno přenastavíme naši aplikaci, aby ověřovala uživatele vůči záznamům v databázi.

Jen pro připomenutí: jako obvykle si z Mercurialu stáhneme aktuální verzi zdrojových kódů k dnešnímu dílu seriálu:

$ hg pull
$ hg up "6. díl"

Potřebné databázové tabulky

Nejprve si v databázi vytvoříme tři nové tabulky:

CREATE TABLE uzivatel (
	id
		INTEGER
		NOT NULL
		DEFAULT nextval('uzivatel_seq'::regclass)
		PRIMARY KEY,
	prezdivka
		CHARACTER VARYING(64)
		NOT NULL -- Uživatelské jméno
		UNIQUE,
	heslo
		CHARACTER VARYING(512)
		NOT NULL,
	jmeno
		CHARACTER VARYING(64),
	prijmeni
		CHARACTER VARYING(64),
	email
		CHARACTER VARYING(255),
	datum
		TIMESTAMP WITH TIME ZONE
		NOT NULL DEFAULT now()
)

CREATE TABLE "role" (
	kod character varying(16) NOT NULL,
	popis character varying(255),
	CONSTRAINT skupina_pk PRIMARY KEY (kod)
)

CREATE TABLE uzivatel_role (
	prezdivka
		CHARACTER VARYING(64)
		NOT NULL,
	"role"
		CHARACTER VARYING(16)
		NOT NULL,
	CONSTRAINT uzivatel_role_pk
		PRIMARY KEY (role, prezdivka),
	CONSTRAINT uzivatel_role_role_fk
		FOREIGN KEY ("role")
		REFERENCES "role" (kod) MATCH SIMPLE
		ON UPDATE CASCADE ON DELETE CASCADE,
	CONSTRAINT uzivatel_role_uzivatel_fk
		FOREIGN KEY (prezdivka)
		REFERENCES uzivatel (prezdivka) MATCH SIMPLE
		ON UPDATE CASCADE ON DELETE CASCADE
)

Kompletní definici tabulek naleznete ve zdrojových kódech aplikace. První tabulka obsahuje záznamy uživatelů, jejich hesla a profily. Druhá tabulka slouží k přiřazení rolí jednotlivým uživatelům. Třetí tabulka není nezbytně nutná, ale je vhodné si tento číselník rolí vytvořit. Jednak si k rolím můžeme doplnit vysvětlující poznámku a jednak nám pomůže referenční integrita v tom, aby se nám v aplikaci nevyskytovaly role, které tam být nemaí (třeba v důsledku překlepů).

V naší aplikaci přiřazujeme každému uživateli roli bezny a budeme ji vyžadovat pro operace, které může provádět běžný přihlášený uživatel (např. přidávat nové podniky nebo psát komentáře). Pokud bychom někomu chtěli pozastavit účet, stačí mu v databázi tuto roli odebrat a tyto funkce nebude mít dostupné – opět výhoda deklarativního přístupu – nemusíme do kódu aplikace přidávat kontroly, zda uživatel nemá např. ve sloupečku neaktivni_ucet hodnotu true (zaručeně bychom na přidání kontroly alespoň na jednom místě zapomněli).

Úprava nastavení serveru

Po vytvoření tabulek k databázi přistoupíme k nastavení aplikačního serveru – samotnou aplikaci nebude potřeba měnit.

definice JDBCRealmu

V Glassfishi jednoduše smažeme původní realm nekurakNET a vytvoříme nový se stejným jménem, tentokráte ale založený na třídě JDBCRealm a vyplníme tyto jeho parametry:

  • JAAS Context: jdbcRealm
  • JNDI: jdbc/nekurak
  • User table: uzivatel
  • User Name Column: prezdivka
  • Password Column: heslo
  • Group Table: uzivatel_role
  • Group Name Column: role
  • Digest Algorithm: SHA-512
  • Encoding: Hex

Většina parametrů asi nepotřebuje vysvětlení. Aplikační server se pokusí najít záznam pro danou přezdívku (uživatelské jméno) v tabulce uzivatel. Následně ověří heslo. A poté přiřadí úspěšně ověřenému uživateli role podle záznamů v tabulce uzivatel_role.

Hesla je samozřejmě možné uchovávat i v čistém tvaru (algoritmus: none), ale vhodnější bude je mít v databázi zahashovaná. Hashovací algoritmus si můžeme vybrat – MD5 (dosud je jako výchozí), nebo SHA-1, SHA-512 a další. Určíme kódování – jelikož výstupem hashovacího algoritmu je pole bajtů, převádějí se tyto bajty na text, který se snadněji ukládá do databáze. Kromě obvyklejšího hexadecimálního kódování můžeme použít i Base64.

Pokud zvolíme SHA-512 a Hex, budou hesla ve stejném tvaru, jaký můžeme získat v příkazové řádce pomocí:

echo -n "nějaké pěkné heslo" | sha512sum
7df16188f807fb…503e6e800fabeafc9bc1413d4dfe0a743e35ca -

Parametr -n je zde důležitý, protože příkaz echo jinak přidává nakonec řetězce znak konce řádku (hash by se tudíž neshodoval s tím, co očekává autentizační modul).

JNDI jméno odkazující na datový zdroj jsme použili stejné, jako používá aplikace. Ale není to pravidlem – např. můžeme chtít zabezpečit aplikaci tak, aby nemohla číst hesla uživatelů (ke své činnosti to totiž nepotřebuje), tudíž bychom si definovali dvě různá databázová spojení (pod různými DB uživateli), jedno pro autentizaci a druhé pro samotnou aplikaci.

Pokud nám standardní způsob ověřování nevyhovuje, můžeme si vytvořit vlastní autentizační modul a použít ho místo JDBCRealm. Např. bychom mohli uživatele ověřovat pomocí uložené databázové procedury (v tom případě by právo na SELECT hesel nemusel mít ani tento autentizační modul – pouze by zavolal proceduru s parametry jméno/heslo a ta by vrátila seznam rolí, kterými uživatel disponuje, nebo vyhodila výjimku při neúspěšné autentizaci). Výhodou je, že jakmile si jednou takový modul napíšeme, můžeme ho používat pro všechny své aplikace na daném serveru.

Registrační formulář

Se záznamy uživatelů budeme v aplikaci pracovat stejně jako s jinými daty – nad datovou a EJB vrstvou si vytvoříme prezentační vrstvu (JavaBeany a JSP), formulář bude velice podobný tomu na přidávání podniků. Pouze doplníme ještě jeden krok:

  1. Uživatel vyplní registrační formulář a odešle ho.
  2. Zobrazíme mu zadané údaje a on je potvrdí.
  3. Po potvrzení teprve založíme účet.

Uživatel potvrzuje údaje kliknutím na odkaz na stránce. Neodesílá se podruhé formulář a místo toho odkaz obsahuje token (hash náhodného čísla). Data jsou mezitím uložena v objektu na straně serveru (se scope="session"). Jednak si uživatel může zkontrolovat vyplněné údaje a jednak to naši aplikaci ochrání před CSRF. V případě registračního formuláře je ochrana před tímto typem útoku možná zbytečná, ale až budeme psát funkci pro hlasování v anketách, bude se ochrana pomocí tokenu taleko víc hodit – Zabráníme tak útočníkovi ve zmanipulování ankety umístěním odkazu (např. obrázku) na nějakou hodně navštěvovanou stránku.

Pokud bychom k registračnímu formuláři doplnili CAPTCHu Mohli bychom hash v odkazu vypustit (CAPTCHa aplikaci ochrání i před CSRF).

V souboru registrovatUzivatele.jsp si inicializujeme tři objekty:

<jsp:useBean
	id="uzivatel"
	class="cz.frantovo.nekurak.dto.Uzivatel"
	scope="request"/>
<jsp:useBean
	id="uzivatelPredRegistraci"
	class="cz.frantovo.nekurak.web.UzivatelPredRegistraci"
	scope="request"/>
<jsp:useBean
	id="registraceUzivatele"
	class="cz.frantovo.nekurak.web.RegistraceUzivatele"
	scope="session"/>

Objekt uzivatel je uživatelský účet, který bude nakonec uložen do databáze. uzivatelPredRegistraci je instance pomocné třídy, která ho obaluje a stará se o generování náhodného kontrolního tokenu. Tyto dva objekty mají nastavenou platnost jen pro daný HTTP požadavek (scope="request"). Třetí objekt RegistraceUzivatele je platný pro celou relaci uživatele (scope="session") a to proto, aby udržel stav (údaje vyplněné do formuláře a nastavené objektu uzivatel), mezi HTTP požadavky (kontrola údajů a následně potvrzení registrace). Uživatel by si totiž mohl otevřít několik registračních formulářů, odeslat je a pak jeden z nich potvrdit.

Za zmínku stojí, že pokud v JSP vyplňujete vlastnosti nějakého objektu (v tomto případě uživatelského účtu) a názvy proměnných v Javě se shodují s názvy POST/GET parametrů odeslaných přes HTTP, není potřeba vlastnosti nastavovat po jedné a lze to udělat hromadně:

<jsp:setProperty name="uzivatel" property="*"/>

Pokud se v budoucnu rozhodneme podmínit registraci ověřením e-mailové adresy (v současnosti není ani povinná, stačí vyplnit přezdívku a heslo), stačí, když nebudeme uživatelům přiřazovat roli bezny automaticky hned po registraci a uděláme to až když uživatel klikne na odkaz v e-mailu. Tento systém je poměrně flexibilní a umožňuje měnit procesy na základě aktuálních potřeb.

Závěr

V dnešním díle jsme si ukázali, jak lze prakticky bez zásahu do aplikace změnit způsob ověřování uživatelů – ověřování oproti SQL databázi místo souboru (který se hodí spíše pro prototypování). A přidali jsme registrační formulář pro nové uživatele. Příště je na řadě trochu teoretičtější díl – porovnání výhod Javy a PHP na webovém serveru.

Odkazy a zdroje:

Témata: [počítačová bezpečnost] [Java]

Komentáře čtenářů

Tento článek zatím nikdo nekomentoval

Přidat komentář

reagujete na jiný komentář (zrušit)
jméno nebo přezdívka
název příspěvku
webová stránka, blog
e-mailová adresa
nápověda: možnosti formátování
ochrana proti spamu a špatným trollům

Náhled komentáře