Nespoléhejte se na to, že, tak jako Java samotná, budou i základní knihovny a nástroje respektovat důležitost zpětné kompatibility. Například v případě Tomcatu se nám už několikrát stalo, že při upgradu na verzi, kde se mění pouze číslo patche, se kompletně rozpadla funkčnost aplikace. Poprvé to bylo myslím, když v patchi vyupgradovali na novější specku JSP a teď nám zase přihodili bombičku v podobě změny obsahu httpServletRequest.getPathInfo(), která nově vrací i tzv. path parametry.
Že nevíte o co se jedná? Jedná se o parametry připojené k url pomocí znaku středník. Zatím jsem neviděl mnoho případů, kdy by se k něčemu využívaly s vyjímkou jediného a tím je jsessionid parametr, který je používán k přenosu vygenerovaného SESSION_ID do doby než je známo, jestli klientská strana podporuje Cookies (pak už se toto id předává většinou pomocí cookie). Podrobnosti o zmatcích panujících v této části specky moc hezky rozebírá tento příspěvek (navíc má hezkou statistiku toho, jaký bordel v tom mají různé Servlet kontejnery): Java Servlet URI Parameters.
Vývojáři Tomcatu se rozhodli, že si tento nepořádek uklidí a udělali tak ve verzi 6.0.33. Jediné, podle čeho mohlo okolí zjistit (kromě své nefunkční aplikace) byl tento záznam v changelogu:
Improve handling of URLs with path parameters and prevent incorrect 404 responses that could occur when path parameters were present. (kkolinko)
Důvody které je k tomu vedly jsou rozepsány v těchto materiálech:
- Diskuse v Tomcat fóru vývojáře podobně překvapeného jako jsem já
- Odkazovaná chyba Spring Servlet Security, která vzniká právě zneužitím URI path parametrů
Záměr je to jistě bohulibý, ale provedení typicky Tomcatovské – nic neříkající záznam v changelogu a zvednutí čísla patche. Teď můžeme čekat jak se postupně v mnoha aplikacích začnou objevovat hlášení podobná tomuto: Incorrect handling of path parameters in PathDispatcher
Takže jestli se vám po upgradu na Apache Tomcat verze 6.0.33 rozpadne vaše webová aplikace, protože webový framework nepočítal s dodatečnými parametry v pathInfo (a to třeba i z toho důvodu, že si mohli vyložit Servlet specku stejně špatně jako dřív sami autoři Tomcatu), už víte o co jde. Při získávání pathInfo je nutné jej takto oříznout:
/**
* Removes path parameters from each path segment in the supplied path and truncates sequences of multiple '/'
* characters to a single '/'.
*
* @param path either the {@code servletPath} and {@code pathInfo} from the original request
*
* @return the supplied value, with path parameters removed and sequences of multiple '/' characters truncated,
* or null if the supplied path was null.
*/
private String strip(String path) {
if (path == null) {
return null;
}
int scIndex = path.indexOf(';');
if (scIndex < 0) {
int doubleSlashIndex = path.indexOf("//");
if (doubleSlashIndex < 0) {
// Most likely case, no parameters in any segment and no '//', so no stripping required
return path;
}
}
StringTokenizer st = new StringTokenizer(path, "/");
StringBuilder stripped = new StringBuilder(path.length());
if (path.charAt(0) == '/') {
stripped.append('/');
}
while(st.hasMoreTokens()) {
String segment = st.nextToken();
scIndex = segment.indexOf(';');
if (scIndex >= 0) {
segment = segment.substring(0, scIndex);
}
stripped.append(segment).append('/');
}
// Remove the trailing slash if the original path didn't have one
if (path.charAt(path.length() - 1) != '/') {
stripped.deleteCharAt(stripped.length() - 1);
}
return stripped.toString();
}
Poznámka ke kódu: Díky za upozornění Ariel na chybu v mém původním kódu pro ořez. Svůj původní kód jsem nahradil lepším, který jsem vybral přímo ze Springu, takže by již měl být v pořádku.
Všechno bych pochopil, co ale nechápu je, proč při nekompatibilních změnách tohoto typu autoři Tomcatu nepoužijí druhé verzovací číslo, aby upozornili na to, že tam může čekat vývojáře nějaký problém. V patchi podobné změny podle mého názoru nikdo nečeká. Určitě by se vyhnuli podobným „hate postům“, který jsem právě teď stvořil já.
Fuj!



Vzdycky me prekvapi, jak je kod ve Spring Frameworku robustni. Oni uz na to proste mysleli.
Na SpringSource je spolehnuti. Proc by nekdo pouzival neco jineho?
Ariel
Jo, mluvíš mi z duše. Ze čtení Springového kódu jsem se naučil plno věcí. Nejčastější proti argument slýchám, že Spring je moc „heavy“, což je sice pravda, ale ty poklady v něm za sto stojí.
Ahoj
Vyborny clanek, uplne souhlasim.
Jen bych upozornila na to, ze Path parameter je soucasti Path segmentu, cili jednoho „adresare“ v URI. To znamena, ze zdaleka nemusi byt na konci Stringu getPathInfo(). Proto tvuj kod na odstraneni Path parametru je spatne napsany. Pozor, prosim, na to.
Jinymi slovy, toto je platna adresa:
http://www.ariel-is-sexy.com/photos;showHidden=true/photosFromBath;includeNudePhotos=true/index.html
Mejte se
Ariel
Díky – tohle je asi nejvíc sexy komentář, co na blogu mám
Je v tom pěknej bordel – já jsem zase našel toto: http://www.w3.org/Addressing/rfc1808.txt
Kde se říká, cituji:
„Section 5 of RFC 1738 specifies that the question-mark character („?“) is allowed in an ftp or file path segment. However, this is not true in practice and is believed to be an error in the RFC. Similarly, RFC 1738 allows the reserved character semicolon („;“) within an http path segment, but does not define its semantics; the correct semantics are as defined by this document for.“
A dále potom:
„2.4.5. Parsing the Parameters
If the parse string contains a semicolon „;“ character, then the is
substring after the first (left-most) semicolon „;“ and up to the end
of the parse string is the parameters (<params>). If the semicolon
is the last character, or no semicolon is present, then
empty. The matched substring, including the semicolon character, is
removed from the parse string before continuing.“
Což by zase dávalo zapravdu tomu mému prvnímu řešení. Každopádně pro path matching je umístění path parametrů doprostřed pathInfo naprosto nepoužitelná záležitost. Tj. ta jednodušší varianta je více méně stejně dostatečně použitelná IMHO.
Každopádně jsem kód v zájmu formální správnosti převzal ze Spring frameworku, kde to mají předpokládám správně tak, jak jsi mě upozorňovala.
Patche se vzdy backportuji, chyby tedy mizi, ale verze zustavaji stejne. Zpetne se takto udrzuje momentalne cca 20 verzi tomcatu, pokud se budeme bavit o rhelu 4, 5 a 6. Zakaznik ma ale na vyber, verze nemusi mit zmrazene. Viz y stream vs z stream. Presne tohle je to, proc se za linux plati. Stabilita a zpetna kompatibilita.
Dík za info, zkonzultuju to s našima Operations. Vůbec netuším, co kde platíme …
Tak určitě je to past. Každopádně jsessionid v url, a to i pokud je to pouze na první request, je možná bezpečnostní díra, proto se doporučuje urlRewriting vypnout.
Počkej, tím chceš říct, že na těchto distribucích se neinstalují bezpečnostní patche? Myslím, že je dobrým zvykem patchovat web / app server i z důvodu bezpečnostních záplat.
Tj. dřív nebo později na to narazí taky. Leda, že by se postarali o nějaký dočasný fix pomocí wrappingu request objektu. Hmm, to se mi ale nějak nezdá …
A prave z techto duvodu je vhodne pouzivat distribuce s dlouhou podporou napriklad rhel (tomcat je soucasti) nebo jboss eap. Tam k tomuto nedochazi.