One little-known feature of the Java Servlet Spec allows for encoding session identifiers in URLs. In theory, this allows browsers without support for cookies to maintain session state with your website. In practice, however, there are several problems with this approach:
Every link on your site needs manual intervention
Cookieless sessions are achieved in Java by appending a string of the format
;jsessionid=
SESSION_IDENTIFIER
to the end of a URL. To do this, all links emitted by your website need to be passed through either
HttpServletResponse.encodeURL()
, either directly or through mechanisms such as the JSTL
<c:out />
tag. Failure to do this for even a
single
link can result in your users losing their session forever.
Using URL-encoded sessions can damage your search engine placement
To prevent abuse, search engines such as Google associate web content with a single URL, and penalize sites which have identical content reachable from multiple, unique URLs. Because a URL-encoded session is unique per visit, multiple visits by the same search engine bot will return identical content with different URLs. This is not an uncommon problem; a test search for ;jsessionid in URLs returned around 79 million search results.
It's a security risk
Because the session identifier is included in the URL, an attacker could potentially impersonate a victim by getting the victim to follow a session-encoded URL to your site. If the victim logs in, the attacker is logged in as well - exposing any personal or confidential information the victim has access to. This can be mitigated somewhat by using short timeouts on sessions, but that tends to annoy legitimate users.
What you can do
For the vast majority of web sites, requiring cookies to store session state is not a major problem. It is probably safe to disable URL-based sessions entirely. At a bare minimum, session identifiers need to be hidden from search bots to avoid the repercussions detailed above. Unfortunately, the servlet spec does not provide a standard way to disable the use of URL-based sessions and many servlet containers do not provide a mechanism to disable them either.
The solution is to create a servlet filter which will intercept calls to
HttpServletRequest.encodeURL()
and skip the generation of session identifiers. This will require a servlet engine that implements the Servlet API version 2.3 or later (J2EE 1.3 for you enterprise folks). Let's start with a basic servlet filter:
package com.randomcoder.security; import java.io.IOException; import javax.servlet.*; import javax.servlet.http.*; public class DisableUrlSessionFilter implements Filter { public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO add filter logic here } public void init(FilterConfig config) throws ServletException {} public void destroy() {} }
We don't need to be concerned with the
init()
and
destroy()
methods; let's focus on
doFilter()
. First, let's exit quickly if for some reason the current request is non-HTTP, and cast the
request
and
response
objects to their HTTP-specific equivalents:
if (!(request instanceof HttpServletRequest)) { chain.doFilter(request, response); return; } HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response;
Next, let's invalidate any sessions that are backed by a URL-encoded session id. This prevents an attacker from generating a valid link. Just because we won't be generating session-encoded links doesn't mean someone else won't try:
if (httpRequest.isRequestedSessionIdFromURL()) { HttpSession session = httpRequest.getSession(); if (session != null) session.invalidate(); }
To disable the default URL-encoding functionality, we need to wrap the existing
HttpServletResponse
object. Fortunately, the Servlet API provides just such a class ready-made in
HttpServletResponseWrapper
. We could subclass it to provide our own handling, but this is a trivial enough change that an anonymous inner class will do nicely:
HttpServletResponseWrapper wrappedResponse = new HttpServletResponseWrapper(httpResponse) { public String encodeRedirectUrl(String url) { return url; } public String encodeRedirectURL(String url) { return url; } public String encodeUrl(String url) { return url; } public String encodeURL(String url) { return url; } };
You may notice that we have overridden four methods, not one.
encodeRedirectURL
is used to encode redirected URLs, which can sometimes require different logic to determine if session identifiers are required. The other two methods are deprecated, but are included here for completeness.
Finally, we need to pass the original request and our response wrapper to the next filter in the chain:
chain.doFilter(request, wrappedResponse);
Our servlet filter is now written, but we still need to tell our servlet container about it. For this, we need to add the following to web.xml :
<filter> <filter-name> DisableUrlSessionFilter </filter-name> <filter-class> com.randomcoder.security.DisableUrlSessionFilter </filter-class> </filter> ... <filter-mapping> <filter-name>DisableUrlSessionFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
This registers our filter with the servlet container, and maps it to all requests. For best results, the filter mapping should be placed above any other filter mappings to prevent any calls to
encodeURL
from slipping through.
Resources
Update: This site offers some additional advice using mod_rewrite. Thanks, Darren!
Update: Fixed reference to encodeUrl(). As pointed out below, this is found in HttpServletResponse, not HttpServletRequest.
The first (in a reasonable implementation) is no more difficult than breaking any cryptographic code. For example, a 16-character JSESSIONID employing just A-Z, a-z and 0-9 provides 47,672,401,706,823,533,450,263,330,816 combinations, meaning that with one million logged on users (i.e. one million legit JSESSIONIDs that could be found) and an attack rate of 100 unique JSESSIONIDs per second, it would take over seven trillion years on average to find a legitimate JSESSIONID.
The second (again, in a reasonable implementation) should be prohibited based on other characteristics of the client connection, such as the IP address. While IP spoofing is theoretically possible, the ability to both grab a JSESSIONID while it is valid _and_ spoof an IP address is a pretty big task.
The exception is when the attacker is behind the same NAT firewall as the victim, in which case the ability to grab a JSESSIONID is likely easy _and_ the IP address of the attacker is automatically the same IP address as the victim. There is no way to protect against this case except by explicitly requiring a persistent HTTPS connection in concert with the use of JSESSIONID.
As a general rule of thumb, it is good to default to cookies, and only switch to JSESSIONID URL encoding when cookies are not supported, and only if the above considerations (and those listed elsewhere in this thread) do not apply.
Peace,
Cameron Purdy
I believe URL-based sessions are an abuse of the URL specification and cause more problems than they solve.
In short the intent behind jsessionid is to provide a backup so that the web application can still function correctly even when a feature not under the developer's control is rendered unavailable. You may believe that they cause more problems than they solve - however they solve the largest problem that any user of your site might encounter which is your application not working _at all_ without them changing either their browser, or browser settings. Are they really willing to do that for your site? Or are they just going to wander off thinking, "gee it seemed like that site would be cool, but it didn't work."
Spiders (such as Google) typically do not support cookies and so by default, the server will append a session token to all session-enabled URLs sent to spiders. I didn't cover this approach in the article, but you could modify the code to only hide session identifiers from known bots (using user-agent sniffing). This would be a bit more fragile, but would address your concern that we need a fallback for "cookie-challenged" browsers.
By far the most common use for cookies is to enable logins to a site, and if necessary, this can be enabled with BASIC authentication for simple clients. As with any good web site, things should degrade whenever possible.
I also added something to strip incoming jsessionid links from Google etc using mod rewrite.
Details on my blog: http://boncey.org/2007_1_8_purging_jsessionid
i have tried u r above given filter .
but this is not working for me :-(.
i have also added session=false on jsp page.
but then too i have same jsessionId...
can u please let me know how do i configure this
solution.
and i feel
HttpSession session = httpRequest.getSession();
if (session != null) session.invalidate();
this will disable http session forever so i guess filter should be called for static pages only
If the filter is not working for you, it is possible that you have not configured it in web.xml. Try modifying the filter adding debugging statements. If you don't see any output, the filter is probably not being executed.
As for the session invalidation logic, that only fires if the current request has a URL-encoded identifier, so it won't affect any normal URLs.
Best of luck.
Glad to hear this was useful to you. Excellent follow-up article by the way -- I think you've discovered the missing piece to the JSESSIONID "puzzle".
checks the IP address too
I have copied your filter, and I also redirect the old urls for purging from within the filter:
//redirect to URL without jsessionid,
StringBuffer url = httpRequest.getRequestURL();
String queryString = httpRequest.getQueryString();
String redirectURL = url+(StringUtils.isNotEmpty(queryString) ? "?"+queryString : "");
wrappedResponse.setHeader(HEADER_LOCATION, redirectURL);
wrappedResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
return;
However, I have not really dug into the HTTP specification very deeply. THis seems to work, but no idea if it is 100% correct. Also the
httpRequest.isRequestedSessionIdFromURL()
thing seems a bit unreliable: I think it only works if the client has stated that it doesn't accept cookies? Good enough for the search engines, but not for the hackers. But I found no other way to identify URLs with jsessionid, as the jsessionid doesn't show up in the parameters of HttpServletRequest.
If the application does not generate the session until AFTER a person has been authenticated, then an attacker should not be able to get the user to folllow a session-encoded URL which would have them log in.
I have tried using this filter and get the following message from the browser when cookies are disabled "This problem can sometimes be caused by disabling or refusing to accept cookies." any suggestions? This is the only filter in the web.xml. The framwork we are using is Spring webflow.
We have also tried http://tuckey.org/urlrewrite/ with no success.
Any help would be appreciated.
Here above mentioned coding deployed in my application.but its not have more impact which JsessionID still exist.Actually i am using weblogic server which not take in account of filter coding,so please come up with more answer for this query and should i implement Servlet-api.jar file which 2.3 version file for this coding .
with regards
Karthik
The Above codin what kind of method can i add it to hide jsessionid in url passing
with regards
Karthik
Just to say this: the method encodeURL() is actually in the object HttpServletResponse.
Not in the request, like the section "Every link on your site needs manual intervention" says.
BTW, can you officially release this work under some kind of non-GPL license? I'd like to package up your filter for a Grails plugin.
HttpServletRequest.encodeURL()
=>
HttpServletResponse.encodeURL()
JSessionId or cookies are to authenticate or understand a client and its session, and that is the purpose of having either of them for a web or app server. If we are confusing that we wouldnt want to have JSessionId isnt it destroying the actual purpose of authenticating a session by the app server??
I have only one question. I am using 2 tomcat & using apache as a load balancer. if I removed jsession id then how it will definitely breaks & 2 different sessions will starts in two nodes. Other thing we are using secure & non-secure one. Now I have to create session in non-secure url so the session can move to secure one.
So while the author is right in that putting this very sensitive moniker in plain-text in the URL is dangerous, remember that it is the fact that the moniker is plain-text, in any form, is the problem. SSL won't help you in a MITM attack, if there is malware on the end-point, etc... the application must encrypt the moniker.
Hope this helps.
Cheers.
-Darrell Teague-
Subayai Inc.
inurl:jsession
used to return 235,000,000 entries but nowadays it returns less than 20,000 and I can't see any of the jsession format specified in the servlet container spec.
This leads me to believe that google has taken advantage of the fact that ALL web apps running in servlet containers use the same parameter 'jsession' and so it google can uniformly deal with that in a good way.
I have a small recommendation: Initially I tried to open the link "http://randomcoder.com/articles/jsessionid-considered-" because of a line break in the email that contained it and received a pretty horrible servlet container exception.
Just to let you know,
Torsten.
Large Community, respect all!
I am using ATG9.1 and trying to create clean url for the bots. I am able to do that using catalog item link droplet but it get appended with ugly JSessionId. I tried the code given above to avoid encoding of url but no luck. I am using Jboss-EAP-4.2 with ATG9.1.
I have created my custom filter java class using above code and added the same in web.xml.
But i am still seeing JSessionId is appending in my clean urls.
Any one has any clue about the same?
Thanks & regards,
Jack
This is clearly a completely made-up argument. SEO fine, ugliness of URLs fine, but don't pretend it's about security. Nothing like this is possible and for any situation not involving the user actively copy/pasting the URL to an attacker (and with very lax other security) there is no difference in having that data in a cookie, since that data is also easily and readily available if the URL is.
Ok?
http://weblog.yatskevich.com/post/21326120441/session-tracking-in-servlet-3-0
http://www.mojavelinux.com/blog/archives/2006/09/improved_session_tracking/