Acegi Captcha způsob integrace a možnosti použití

V tomto příspěvku se nechci věnovat popisu zprovoznění jCaptchy v bezpečnostní frameworku Acegi Security, jelikož toto je velmi dobře popsáno již v existujícím článku na MoroSystems weblogu. Spíš se chci zaobírat způsobem, jakým se k integraci do Acegi frameworku autoři postavili. Tento způsob mi přijde totiž přinejmenším neobvyklý. Zachovává sice zavedené principy Acegi, ale ten neodpovídá mým (ale řekl bych vcelku přirozeným) představám o tom, jak by měla captcha ve web strákách fungovat.

Princip práce s captchou v Acegi je podobný principu standardního přihlašování. Acegi při přístupu na “chráněné url” kontroluje ověření uživatele v SecurityContextu a pokud uživatel není ověřen, přesměruje tok aplikace na přihlašovací formulář nebo v případě captchy na formulář obsahující obrázek a textové pole pro vepsání rozpoznané captchy. Pokud řešíme přihlašování, je tento způsob přirozený – v případě captchy však očekávám, že captcha bude rovnou součástí formuláře, který je “hlídán”. Tak ale integrace jCaptchy v Acegi ve svém základu nefunguje.

Pokud Vám tento způsob připadne taky trochu podivný a zajímá Vás, jak si s tím poradit, čtěte dál.

Současná integrace Captchy do Acegi umožňuje vývojářům nezabývat se v jednotlivých formulářích captchou, ale ve chvíli kdy je již aplikace hotová určit URL, které mohou být zneužitelná boty a nastavit pravidla pro rozpoznávání chování robotů. Acegi v tomto směru poskytuje několik strategií:

Po krátké úvaze mě však přišel použitelný jediný a to je TestOnceAfterMaxRequestsCaptchaChannelProcessor s nastavením Threshold na hodnotu 0. To znamená, že při prvním přístupu na formulář chráněný captchou se má provést ověření “humanity” uživatele a po správném ověření už považovat uživatele s danou session za ověřeného a neobtěžovat ho dalšími captcha obrázky.

Captcha servlet filter

Vzhledem k nemožnosti cokoliv rozumného provést s existujícím filtrem org.acegisecurity.captcha.CaptchaValidationProcessingFilter, byl jsem nucen napsat si na základě části jeho funkcionality filtr vlastní (bohužel řada tříd z Acegi má jednu nepříjemnou vlastnost a to tu, že je velmi obtížné je extendovat či jinak modifikovat, jelikož mají řadu private metod / fieldů nebo občas i mnoho funkcionality v jedné metodě, kde by člověk potřeboval změnit jen jednu její část).

Nuže v následujícím kódu je implementace filtru, který provádí validaci captchy a zároveň poskytuje vygenerovaný captcha obrázek. Jelikož je filtr součástí Acegi delegating filtru je obvykle posazen na url-pattern “/*”, což nám umožňuje jednoduše se navěsit na libovolné volání serveru v daném kontextu. Toho filtr využívá k tomu, aby mohl při dotazu na konkrétní url (např.: http://server/context/generatedCaptchaImage.jpg) online vygenerovat captcha obrázek a okamžitě jej vložit do response.

Ten stejný filtr provádí validaci jím vygenerovaných captcha obrázků v případě, že v parametrech requestu objeví parametr s názvem “captcha” (nebo kterýkoliv jiný, který uvedeme v konfiguraci), pokusí se zvalidovat jeho hodnotu vůči naposledy poskytnuté captche. V případě, že validace neprojde je do requestu uložen atribut signalizující neplatný pokus o validaci captchy – v opačném případě je do CaptchaSecurityContext nastaven příznak human na true.

Tento přístup se liší od standardní implementace tím, že vůbec nepracuje s patterny chráněných url. Pouze jednoduše pro určité url (/generatedCaptchaImage.jpg) vrací vygenerovanou captchu a při konkrétním parametru v requestu se naopak pokouší validovat captchu. Ve každém případě však propouští zpracování requestu dál, tzn. vlastní url formuláře není tímto filtrem nijak chráněno.

Zaznamenání chyby při validaci formuláře

Filtr není závislý na žádném web frameworku, jediný jeho výstup je nastavení příznaku v SecurityContextu nebo specifického atributu v requestu. Vlastní ochranu formuláře musíme tedy řešit až o krok dále – na úrovni validačního rámce konkrétního použitého frameworku. V některých bude tato ochrana velmi triviální, někde se můžeme při implementaci ochrany docela nadřít. Nejlepším způsobem je se vetřít do standardní validace konkrétního validačního rámce a využít jej i k propagaci chybového hlášení, zpracování flow při chybě, obarvení chybových polí atp. – tím si ušetříme spoustu práce.

V mém případě se jednalo o integraci do frameworku Stripes a tam je tato záležitost zcela přímočará. Spočívá v implementaci Interceptoru, který se spouští ve fázi BindingAndValidation a který pouze prověří přítomnost atributu signalizujícího špatně rozeznanou captchu. V takovém případě automaticky přidá do seznamu chyb nový záznam, což způsobí, že zpracování requestu skončí ve fázi validace dat a vrátí se zpět na původní formulář.

Obdobně by bylo asi velmi jednoduché implementovat tento způsob validace i v JSF frameworcích. Obtížně si jeho realizaci naopak umím představit ve Strutsech (minimálně v 1.2.X verzi).

Webová vrstva – formulář a zobrazení captchy

V JSP stránce přidáme do formuláře IMG tag, který se odkazuje na “virtuální url”, na které bude reagovat CaptchaGenerationValidationProcessingFilter vrácením nového Captcha obrázku. Ve formuláři bude taktéž k dispozici textové políčko pro zapsání odpovědi opět s názvem, který daný filtr očekává. Celá tato část je uzavřená do IF klauzule, která prověřuje, zda již test “humanity” pro aktuálního uživatele (session) náhodou nebyl v minulosti proveden. Pokud ano, zmizí captcha z formuláře. Pokud tedy uživatel bude ve vaší aplikaci vyplňovat více formulářů chráněných captchou, bude muset tuto captchu vyplnit pouze v prvním formuláři, a když jej aplikace úspěšně prověří, v dalších formulářích již nebude uživatel obtěžován.

Závěrem

Jak jsem již v článku uvedl, integrace captchy v Acegi frameworku se mi zdá prapodivná. Na stranu druhou jsem si vědom, že přímá integrace do formulářů s sebou nese dodatečné problémy – není možné přímo v Acegi implementovat dostatečnou podporu, jelikož zobrazování chyb se musí provádět na úrovni konkrétního použitého web frameworku. Taktéž toto řešení vyžaduje, aby se v každém formuláři vložila část obsahující captchu a kontrolní pole. Toto si museli autoři zcela jistě uvědomovat – bohužel už o tom ale nikde nenajdete zmínku a musíte nad tím přemýšlet sami.

Na úplný závěr si ještě neodpustím jeden povzdech. Z několika přednášek o tvorbě API jsem si odnesl pravidla:

  • nedělejte API širší než je nezbytně nutné,
  • příliš neotvírejte třídy k dědičnosti a nebojte se použít final,
  • omezte viditelnost metod na maximální možnou míru

Z pohledu vývojáře API dávají pravidla smysl. Má daleko větší kontrolu nad svým API a především má daleko větší možnosti refaktoringu a změn v něm.

Z pohledu uživatele API to však vede místy k naprostému zoufalství. V Acegi je většina tříd poměrně slušně připravená k rozšiřování, ovšem najdete tam i takové kousky, kdy je veškerá logika třídy v jedné metodě, nebo naopak jsou důležité metody jako friendly nebo private (aniž by to dávalo zjevný smysl). Na obdobné potíže jsme narazili i v knihovně jCaptcha a častokrát na místech, kde jsme to naprosto nečekali a kde nám to ani nedává smysl.

Jediným výsledkem je bohužel to, že buď to přímo zkopírujete kusy zdrojáku z originálu, nebo použijete reflection ke znásilnění původního kódu. Prostě není jiná možnost – výsledkem je, že tvůrci API svoji kompatibilitu neochránili, pouze si udělali alibi, že při vydání nové verze neručí za to, když “vám to přestane fungovat”.

Podělte se s ostatními:
  • Digg
  • del.icio.us
  • Technorati
  • Diigo
  • DZone
  • FriendFeed
  • Google Bookmarks
  • LinkedIn
  • Reddit
  • RSS
  • StumbleUpon
  • Twitter
Ohodnoťte článek:
Takovéhle články už radši ne!Nic nového pod sluncem.Průměr - obsahuje zajímavé střípky informací.Hodnotný článek - lecos nového jsem se dozvěděl.Skvělý článek - informace se mi dost hodí. (doposud bez hlasů)
Loading ... Loading ...

Nechte zde svůj komentář

Opište prosím text z obrázku: