Spring Security RememberMe Services with Session Cookie - security

I am using Spring Security's RememberMe Services to keep a user authenticated.
I would like to find a simple way to have the RememberMe cookie set as a session cookie rather than with a fixed expiration time. For my application, the cookie should persist until the user closes the browser.
Any suggestions on how to best implement this? Any concerns on this being a potential security problem?
The primary reason for doing so is that with a cookie-based token, any of the servers behind our load balancer can service a protected request without relying on the user's Authentication to be stored in an HttpSession. In fact, I have explicitly told Spring Security to never create sessions using the namespace. Further, we are using Amazon's Elastic Load Balancing, and so sticky sessions are not supported.
NB: Although I am aware that as of Apr. 08, Amazon now supports sticky sessions, I still do not want to use them for a handful of other reasons. Namely that the untimely demise of one server would still cause the loss of sessions for all users associated with it.
http://aws.amazon.com/about-aws/whats-new/2010/04/08/support-for-session-stickiness-in-elastic-load-balancing/

Spring Security 3 does not offer configuration of how the cookie is generated. You have to override the default behaviour:
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
/** Cookie expires on session. */
public class PersistentTokenBasedRememberMeServicesCustom extends
PersistentTokenBasedRememberMeServices {
/** only needed because super throws exception. */
public PersistentTokenBasedRememberMeServicesCustom() throws Exception {
super();
}
/** Copy of code of inherited class + setting cookieExpiration, */
#Override
protected void setCookie(String[] tokens, int maxAge,
HttpServletRequest request, HttpServletResponse response) {
String cookieValue = encodeCookie(tokens);
Cookie cookie = new Cookie(getCookieName(), cookieValue);
//cookie.setMaxAge(maxAge);
cookie.setPath("/");
cookie.setSecure(false); // no getter available in super, so always false
response.addCookie(cookie);
}
}
Make sure, you use this customized PersistentTokenBasedRememberMeServices for you're rememberMeService by adding the class name to it's bean configuration:
<beans:bean id="rememberMeServices"
class="my.custom.spring.PersistentTokenBasedRememberMeServicesCustom"/>

To have session work properly with load balancing I would have your session data stored in a sql database.
The cookie should always be a random value that expire. There are cases where you can store state as a cookie value and it not be a secuirty hazard, such as the users preferred language, but this should be avoided as much as possible. Turning HttpOnlyCookies on, is a great idea.
Read A3: "Broken Authentication and Session Management" in the OWASP top 10 for 2010. One important point in this section is that https must be used for the entire session. If the session is lasting for a very long time, then this is even more important.
Also keep in mind that "Remember Me" creates a large window in which an attacker can "ride" on the session. This gives an attacker a very long time (Months?) in which he can deliver a CSRF attack. Even if you have CSRF protection an attacker can still ride on a session with XSS and XmlHttpRequest (HttpOnlyCookies will prevent a full hijack). "Remember Me" makes other threats like xss, csrf, sniffing more serious. As long as these vulnerabilities have been addressed, then you shouldn't have a problem with real world hackers.
The easiest (and secure) approach to implement a "remember me" feature would be to modify the session timeout to make it very large (a few months). If the "remember me" checkbox is unchecked then store a session variable with a new timeout (1 day from login). Keep in mind that even if the cookie is deleted by the browser when it is closed the session still is active on the server side. If the session id stolen, then it can still be used.

Related

Session authentication with cookie: security issue

I have a simple "session authentication" mechanism:
each time a user logs in a session it's created on database server side, and it's session id is set encrypted as a "session cookie" to the client
on logout or browser instance closing, the session is either deleted or invalidated
I've read and understood that by stealing a cookie encrypting the session id within an active session one steals another identity, and for this reason permanent cookie should not be used.
Still, even if i used a cookie with an short expiration date the theft can still happen within it. Are there other steps to avoid this problem?
It's legit to just use a short expiration date (if so, what is a decent time frame?), or are there other complementary technique to work around this problem?
The best practice for session cookies is to not set an expiry time so that they are valid for the browser session. If you set an expiry time, the cookie becomes persistent, will be stored on disk on the client and so on - it increases the overall risk.
You are right that anybody that has the cookie can impersonate the user. To avoid that, you want to have the cookie set as httpOnly to avoid Javascript access (XSS) and secure so that it is only sent over https.
While there are depths to correct session management, it doesn't seem to make a lot of sense to encrypt the session id. What is the purpose of that encryption? The session id should just be a large random number (possibly encoded into a shorter string, but still), there should be no need to encrypt it.
The generic advice around session management is to not implement it yourself. There are too many pitfalls, and pretty much all frameworks (or languages) have a solid implementation that you can just use reasonably securely. Avoiding all the potential vulnerabilities is hard.

If I don't send session ID by url do I need to cycle my session id on login

I've heard that my site can get attacked by hackers who:
Go to my site to start a session.
Somehow get a client to go to my site with the same session ID
The client logs in
When the attacker comes back to my site with the session id he has full access to that clients account.
I can see this being done if the session ID is passed by url and it makes sense to cycle it (session_regenerate_id) but is this needed if I just use session_start and I don't put the session id in url at any point in time?
There are session fixation attacks other than session-ID-in-URL. In particular, browser controls over cross-domain cookies are weak.
If an attacker has control over foo.example.com, for example by means of an XSS hole in an application running there, they can write a session ID cookie with parameter domain=example.com, which will then be passed to your application running at bar.example.com and hey presto session fixation.
As a developer you often don't have any control over what other vulnerable applications might be running in neighbour domains, so it is best to assume cookie injection can happen and recycle sessions on a princpal change.
Well, if the session ID is only transferd by a cookie (is not in the URL and you do not accept one in the URL) then it is not that important to protect against session fixation attacks by recycling the session ID.
However it is still good practice, as this could also help against a session cookie which was laying around longer time (and potentially be placed by a former user). So with most security practices it is the same here: just do it, even if you cant find a way it might get exploited.

Session Fixation Am i secure?

I am trying to implement security for my project that prevents session fixation.
As i have no access to the component (a filter from a certain library, lets call it MagicFilter) that handles the whole session-creation and validation, i was trying to find out another way of possibly doing it.
Now, consider this scenario for my session:
User requests the login-page
MagicFilter sets a cookie with a JSESSIONID etc
other filters do some work ...
Java Spring MVC, so as last step before the user sees the LoginView i have access to stuff in my LoginController. Here i .invalidate() the session right before i return the view.
So basically the user never has a real and valid session-ID while at the login-page. Only after he logs in the MagicFilter assigns another session-ID which will then be sticked to, as i only invalide() the session-ID in my LoginController.
But this feels very rough and i kind of had to "hack" around the automatic process of the MagicFilter.
Can anyone see if this should be safe in terms of session fixation or not?

Protecting against XSRF attacks GWTP app

I am trying for protecting against XSRF attacks GWTP app. The problem facing like JSESSION easliy get in paroz testing tool, using that tools if user is already logged in and at the same time made same server request by paroz. it execute same transaction with updated value, which is a security problem.
To stop that one, Required to create per request new cookie and send from client to server.
#SecurityCookie
public static final String securityCookieName = getRandomString(); //Not work
For ClientModule
public class ClientModule extends AbstractPresenterModule {
#Override
protected void configure() {
bindConstant().annotatedWith(SecurityCookie.class).to(
NameTokens.securityCookieName);
And in DispatchServletModule
public class DispatchServletModule extends ServletModule {
#Override
public void configureServlets() {
bindConstant().annotatedWith(SecurityCookie.class).to(NameTokens.securityCookieName);
I want to generate cookie randomally instead of 'JSESSIONID'. How/where to do? And what is a proper way to regenerate cookie per request in GWTP?
For generic gwt, see XSRF protection
It's for RPC calls:
RPC XSRF protection is built using RpcToken feature, which lets a
developer set a token on a RPC endpoint using HasRpcToken interface
and have that token included with each RPC call made via that
endpoint.
You have to rewrite your rcp calls to be invoked in the callback that obtained the token but it's not so difficult to implement.
EDIT
I don't understand the need for a randomized cookie name. For the standard GWT protection, you have to specify a set name:
<context-param>
<param-name>gwt.xsrf.session_cookie_name</param-name>
<param-value>JSESSIONID</param-value>
</context-param>
The docs you cited for gwtp state explicitly:
To protect your application against XSRF attacks, as described in
Security for GWT Applications , you have to specify the name of the
security cookie you want to use. Do this by binding a string constant
annotated with #SecurityCookie both on the client and on the server.
I think it doesn't matter if the user is logged in. Malicious code can not read the JSESSIONID cookie (or whatever cookie you specify) and it needs the value of the cookie (sure it can send the cookie but that does nothing because malicious code need the value so it can calculate a unique token that you send every request). This is what the docs say:
Default XSRF protection implementation derives XSRF token from a
session authentication cookie by generating an MD5 hash of the session
cookie value and using the resulting hash as XSRF token. This
stateless XSRF protection implementation relies on the fact that
attacker doesn't have access to the session cookie and thus is unable
to generate valid XSRF token
So you do need to specify your cookie name in order to configure it to work, or GWT can not use the value of that cookie to generate the end point token that you obtain prior to every rpc call and include with every rpc call.
So while I don't think you need to implement your own XSRF protection since you are not using standard gwt, I do think you do need to follow the docs you cite to configure gwtp to use it's implementation of xsrf protection.

What is the best way to prevent session hijacking?

Specifically this is regarding when using a client session cookie to identify a session on the server.
Is the best answer to use SSL/HTTPS encryption for the entire web site, and you have the best guarantee that no man in the middle attacks will be able to sniff an existing client session cookie?
And perhaps second best to use some sort of encryption on the session value itself that is stored in your session cookie?
If a malicious user has physical access to a machine, they can still look at the filesystem to retrieve a valid session cookie and use that to hijack a session?
Encrypting the session value will have zero effect. The session cookie is already an arbitrary value, encrypting it will just generate another arbitrary value that can be sniffed.
The only real solution is HTTPS. If you don't want to do SSL on your whole site (maybe you have performance concerns), you might be able to get away with only SSL protecting the sensitive areas. To do that, first make sure your login page is HTTPS. When a user logs in, set a secure cookie (meaning the browser will only transmit it over an SSL link) in addition to the regular session cookie. Then, when a user visits one of your "sensitive" areas, redirect them to HTTPS, and check for the presence of that secure cookie. A real user will have it, a session hijacker will not.
EDIT: This answer was originally written in 2008. It's 2016 now, and there's no reason not to have SSL across your entire site. No more plaintext HTTP!
The SSL only helps with sniffing attacks. If an attacker has access to your machine I will assume they can copy your secure cookie too.
At the very least, make sure old cookies lose their value after a while. Even a successful hijaking attack will be thwarted when the cookie stops working. If the user has a cookie from a session that logged in more than a month ago, make them reenter their password. Make sure that whenever a user clicks on your site's "log out" link, that the old session UUID can never be used again.
I'm not sure if this idea will work but here goes: Add a serial number into your session cookie, maybe a string like this:
SessionUUID, Serial Num, Current Date/Time
Encrypt this string and use it as your session cookie. Regularly change the serial num - maybe when the cookie is 5 minutes old and then reissue the cookie. You could even reissue it on every page view if you wanted to. On the server side, keep a record of the last serial num you've issued for that session. If someone ever sends a cookie with the wrong serial number it means that an attacker may be using a cookie they intercepted earlier so invalidate the session UUID and ask the user to reenter their password and then reissue a new cookie.
Remember that your user may have more than one computer so they may have more than one active session. Don't do something that forces them to log in again every time they switch between computers.
// Collect this information on every request
$aip = $_SERVER['REMOTE_ADDR'];
$bip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$agent = $_SERVER['HTTP_USER_AGENT'];
session_start();
// Do this each time the user successfully logs in.
$_SESSION['ident'] = hash("sha256", $aip . $bip . $agent);
// Do this every time the client makes a request to the server, after authenticating
$ident = hash("sha256", $aip . $bip . $agent);
if ($ident != $_SESSION['ident'])
{
end_session();
header("Location: login.php");
// add some fancy pants GET/POST var headers for login.php, that lets you
// know in the login page to notify the user of why they're being challenged
// for login again, etc.
}
What this does is capture 'contextual' information about the user's session, pieces of information which should not change during the life of a single session. A user isn't going to be at a computer in the US and in China at the same time, right? So if the IP address changes suddenly within the same session that strongly implies a session hijacking attempt, so you secure the session by ending the session and forcing the user to re-authenticate. This thwarts the hack attempt, the attacker is also forced to login instead of gaining access to the session. Notify the user of the attempt (ajax it up a bit), and vola, Slightly annoyed+informed user and their session/information is protected.
We throw in User Agent and X-FORWARDED-FOR to do our best to capture uniqueness of a session for systems behind proxies/networks. You may be able to use more information then that, feel free to be creative.
It's not 100%, but it's pretty damn effective.
There's more you can do to protect sessions, expire them, when a user leaves a website and comes back force them to login again maybe. You can detect a user leaving and coming back by capturing a blank HTTP_REFERER (domain was typed in the URL bar), or check if the value in the HTTP_REFERER equals your domain or not (the user clicked an external/crafted link to get to your site).
Expire sessions, don't let them remain valid indefinitely.
Don't rely on cookies, they can be stolen, it's one of the vectors of attack for session hijacking.
Have you considered reading a book on PHP security? Highly recommended.
I have had much success with the following method for non SSL certified sites.
Dis-allow multiple sessions under the same account, making sure you aren't checking this solely by IP address. Rather check by token generated upon login which is stored with the users session in the database, as well as IP address, HTTP_USER_AGENT and so forth
Using Relation based hyperlinks
Generates a link ( eg. http://example.com/secure.php?token=2349df98sdf98a9asdf8fas98df8 )
The link is appended with a x-BYTE ( preferred size ) random salted MD5 string, upon page redirection the randomly generated token corresponds to a requested page.
Upon reload, several checks are done.
Originating IP Address
HTTP_USER_AGENT
Session Token
you get the point.
Short Life-span session authentication cookie.
as posted above, a cookie containing a secure string, which is one of the direct references to the sessions validity is a good idea. Make it expire every x Minutes, reissuing that token, and re-syncing the session with the new Data. If any mis-matches in the data, either log the user out, or having them re-authenticate their session.
I am in no means an expert on the subject, I'v had a bit of experience in this particular topic, hope some of this helps anyone out there.
There is no way to prevent session hijaking 100%, but with some approach can we reduce the time for an attacker to hijaking the session.
Method to prevent session hijaking:
1 - always use session with ssl certificate;
2 - send session cookie only with httponly set to true(prevent javascript to access session cookie)
2 - use session regenerate id at login and logout(note: do not use session regenerate at each request because if you have consecutive ajax request then you have a chance to create multiple session.)
3 - set a session timeout
4 - store browser user agent in a $_SESSION variable an compare with $_SERVER['HTTP_USER_AGENT'] at each request
5 - set a token cookie ,and set expiration time of that cookie to 0(until the browser is closed).
Regenerate the cookie value for each request.(For ajax request do not regenerate token cookie).
EX:
//set a token cookie if one not exist
if(!isset($_COOKIE['user_token'])){
//generate a random string for cookie value
$cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));
//set a session variable with that random string
$_SESSION['user_token'] = $cookie_token;
//set cookie with rand value
setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
}
//set a sesison variable with request of www.example.com
if(!isset($_SESSION['request'])){
$_SESSION['request'] = -1;
}
//increment $_SESSION['request'] with 1 for each request at www.example.com
$_SESSION['request']++;
//verify if $_SESSION['user_token'] it's equal with $_COOKIE['user_token'] only for $_SESSION['request'] > 0
if($_SESSION['request'] > 0){
// if it's equal then regenerete value of token cookie if not then destroy_session
if($_SESSION['user_token'] === $_COOKIE['user_token']){
$cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));
$_SESSION['user_token'] = $cookie_token;
setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
}else{
//code for session_destroy
}
}
//prevent session hijaking with browser user agent
if(!isset($_SESSION['user_agent'])){
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
}
if($_SESSION['user_agent'] != $_SERVER['HTTP_USER_AGENT']){
die('session hijaking - user agent');
}
note: do not regenerate token cookie with ajax request
note: the code above is an example.
note: if users logout then the cookie token must be destroyed as well as the session
6 - it's not a good aproach to use user ip for preventing session hijaking because some users ip change with each request. THAT AFFECT VALID USERS
7 - personally I store session data in database , it's up to you what method you adopt
If you find mistake in my approach please correct me. If you have more ways to prevent session hyjaking please tell me.
Try Secure Cookie protocol described in this paper by Liu, Kovacs, Huang, and Gouda:
As stated in document:
A secure
cookie protocol that runs between a client and a server
needs to provide the following four services: authentication, confidentiality, integrity and anti-replay.
As for ease of deployment:
In terms of efficiency, our protocol does not involve any database
lookup or public key cryptography. In terms of deployability, our protocol can be easily deployed on an existing web server, and it does not require any change to
the Internet cookie specication.
In short: it is secure, lightweight, works for me just great.
Ensure you don't use incremting integers for session IDs. Much better to use a GUID, or some other long randomly generated character string.
There are many ways to create protection against session hijack, however all of them are either reducing user satisfaction or are not secure.
IP and/or X-FORWARDED-FOR checks. These work, and are pretty secure... but imagine the pain of users. They come to an office with WiFi, they get new IP address and lose the session. Got to log-in again.
User Agent checks. Same as above, new version of browser is out, and you lose a session. Additionally, these are really easy to "hack". It's trivial for hackers to send fake UA strings.
localStorage token. On log-on generate a token, store it in browser storage and store it to encrypted cookie (encrypted on server-side). This has no side-effects for user (localStorage persists through browser upgrades). It's not as secure - as it's just security through obscurity. Additionally you could add some logic (encryption/decryption) to JS to further obscure it.
Cookie reissuing. This is probably the right way to do it. The trick is to only allow one client to use a cookie at a time. So, active user will have cookie re-issued every hour or less. Old cookie is invalidated if new one is issued. Hacks are still possible, but much harder to do - either hacker or valid user will get access rejected.
AFAIK the session object is not accessible at the client, as it is stored at the web server. However, the session id is stored as a Cookie and it lets the web server track the user's session.
To prevent session hijacking using the session id, you can store a hashed string inside the session object, made using a combination of two attributes, remote addr and remote port, that can be accessed at the web server inside the request object. These attributes tie the user session to the browser where the user logged in.
If the user logs in from another browser or an incognito mode on the same system, the IP addr would remain the same, but the port will be different. Therefore, when the application is accessed, the user would be assigned a different session id by the web server.
Below is the code I have implemented and tested by copying the session id from one session into another. It works quite well. If there is a loophole, let me know how you simulated it.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
String sessionKey = (String) session.getAttribute("sessionkey");
String remoteAddr = request.getRemoteAddr();
int remotePort = request.getRemotePort();
String sha256Hex = DigestUtils.sha256Hex(remoteAddr + remotePort);
if (sessionKey == null || sessionKey.isEmpty()) {
session.setAttribute("sessionkey", sha256Hex);
// save mapping to memory to track which user attempted
Application.userSessionMap.put(sha256Hex, remoteAddr + remotePort);
} else if (!sha256Hex.equals(sessionKey)) {
session.invalidate();
response.getWriter().append(Application.userSessionMap.get(sessionKey));
response.getWriter().append(" attempted to hijack session id ").append(request.getRequestedSessionId());
response.getWriter().append("of user ").append(Application.userSessionMap.get(sha256Hex));
return;
}
response.getWriter().append("Valid Session\n");
}
I used the SHA-2 algorithm to hash the value using the example given at SHA-256 Hashing at baeldung
Looking forward to your comments.
Let us consider that during the login phase the client and server can agree on a secret salt value. Thereafter the server provides a count value with each update and expects the client to respond with the hash of the (secret salt + count). The potential hijacker does not have any way to obtain this secret salt value and thus cannot generate the next hash.
To reduce the risk you can also associate the originating IP with the session. That way an attacker has to be within the same private network to be able to use the session.
Checking referer headers can also be an option but those are more easily spoofed.
Use SSL only and instead of encrypting the HTTP_USER_AGENT in the session id and verifying it on every request, just store the HTTP_USER_AGENT string in your session db as well.
Now you only have a simple server based string compare with the ENV'HTTP_USER_AGENT'.
Or you can add a certain variation in your string compare to be more robust against browser version updates.
And you could reject certain HTTP_USER_AGENT id's. (empty ones i.e.)
Does not resolve the problem completley, but it adds at least a bit more complexity.
Another method could be using more sophisticated browser fingerprinting techniques and combine theyse values with the HTTP_USER_AGENT and send these values from time to time in a separate header values. But than you should encrypt the data in the session id itself.
But that makes it far more complex and raises the CPU usage for decryption on every request.
If ISP hijack the certificate-verification, ISP will possibly initiate a Man-in-the-middle attack. Especially with a compromised certificate authorities.
So I believe you can not prevent session hijack from ISP. Especially when legal forces come with a fake certificate got from CA under law enforce.
You will need something outside the network to protect your session, for example one time pad. This is why one time pad so sensitive and can only be sold by few companies.
Be careful, one time pad may be exploited. Choose your one time pad with profession.
Protect by:
$ip=$_SERVER['REMOTE_ADDER'];
$_SESSEION['ip']=$ip;

Resources