Regexp lecke

A történet ott kezdődik, hogy symfony 1.0 alatt szerettem volna a formjaimat csrf ellen védeni. A pluginek között van is egy sfCSFRFilterPlugin, ami pont jó a célra. Azonban hamarosan rá kellett döbbennem, hogy mégsem. Ugyanis pl admin generator alatt a törlés javascriptes post formos megoldását üti a fent említett filter. Ezért kicsit továbbfejlesztettem (pl token generálás, sessionben tárolt token, időkorlát megadása, …), hogy a fenti funkció is működjön. Na itt kezdődik a történet🙂

Az alapötlet, hogy egy paraméterhez kötötten lehet bekapcsolni az admin generator alatt használotos javascript form bővítést. Ime a kódrész:

...
if ($this->getParameter('admin_generator', 0)) {
  $pattern = "#(b(w+)bs*=s*document.createElement(("|')form3);)#i";
  $replace = sprintf('$1 h = document.createElement($3input$3); h.type = $3hidden$3; h.name = $3_csrf_token$3; h.value = $3%s$3; $2.appendChild(h); ', $sessionToken);
  $content = preg_replace($pattern, $replace, $content);
}
...

Működik is, úgy ahogy. A legtöbb esetben inkább sehogy. A hibajelenség ezzel a kóddal kapcsolatban az volt, hogy nemhogy a csrf ellen nem véd, még a confirm üzenetet is eltünteti a rekord törlés ikonról.

Firebug konzolon szépen látszik a hiba: a h.value értékével van gond. Kb így néz ki az egyes oldalfrissítések alkalmával:

... h.name = '_csrf_token'; h.value = 36cf71f146833bf43bbc43414e15d16';...
... h.name = '_csrf_token'; h.value = b1849e3db7b5ca56a0a68f8793027e3'; ...
... h.name = '_csrf_token'; h.value = 'f212d00e27d0cc5693e2569b507f0cfe'; ...
... h.name = '_csrf_token'; h.value = 913dfdcc22afc8d797c3dd031d3fa61'; ...
... h.name = '_csrf_token'; h.value = 'f17b82be999fd882d0512f63299321f0'; ...

hiszem ebből már látszik, hogy a h.value kezdő aposztróf időnként (a legtöbb esetben) nincs jelen. Ami sokkal rosszabb, hogy időnként viszont ott van🙂. Na de mi okozza?

A kódban lévő hiba egy kicsit alattomos és nem is biztos, hogy első ránézére megadja magát. Lássuk lépésenként.

1. az a kódrész amely a h.value-t kiíratja így néz ki:

h.value = $3%s$3;

Erre a formára azért van szükség, hogy a kód ” és ‘ esetén is működőképes legyen.

2. Az érték, amit behelyettesítünk a korábban generált token. Mi is ez a token? Ebben az esetben egy md5 hash, és pont ez okozza a problémát.

A generált hasht előbb behelyettesítjük a csere mintába. A hash előtt pedig pont a backreference található. És csak ezután kerül a string a regexp motorhoz. Ez azt eredményezi, hogy ha pl a $sessionToken = ‘1234’, akkor a backreference értéke nem a várt 3. egyezés, hanem a 31., ami persze nem létezik. A preg_replace() Parameters szekció 3. bekezdése persze ezt leírja, de ezek azok az esetek, amikor mindez igazán rögül😉

Végül a kód megfelelően működő változata:

...
if ($this->getParameter('admin_generator', 0)) {
  $pattern = "#(b(w+)bs*=s*document.createElement(("|')form3);)#i";
  $replace = sprintf('${1} h = document.createElement(${3}input${3}); h.type = ${3}hidden${3}; h.name = ${3}_csrf_token${3}; h.value = ${3}%s${3}; ${2}.appendChild(h); ', $sessionToken);
  $content = preg_replace($pattern, $replace, $content);
}
...
Kategória: regexp, symfony, trükkök, ubuntu
Címke: , , , ,
Közvetlen link a könyvjelzőhöz.

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s