One of the cool things about using sessions and only storing the session id as a cookie is that even if a user’s cookies are stolen using an cross site scripting attack, it is much harder to fool the site into thinking your someone else. When a session id is stolen, the session id will usually only be useful for a limited amount of time as sessions usually expire after about 15 or 20 minutes. Once the session has expired, the session id is useless. This gives an attacker a very limited time frame to successfully launch an attack. In addition, many session systems also have additional checks such as user agent and IP address making stealing a session id even more pointless.
Some authentication systems don’t use sessions and store a username and hashed password in a cookie and check that on every page load. If the cookie is stolen and the attacker created an identical cookie on his/her machine, the attacker will successfully gain access to the victim’s account. Hashing the password using MD5 doesn’t help; the attacker won’t know the victim’s plain text password but will still have access to the site as the script only checks for valid cookies.
Persistent Login
Persistent logins were one of the most heavily requested features for this site. The only reason that I hadn’t implemented it up to now is because I was unsure how to implement it without making the script more vulnerable to possible attacks using cross site scripting.
Invision Board uses a session and username/password cookie combo. On a typical login, three cookies are set: username, password and session ID. If (somehow) this cookie was stolen using a cross site scripting flaw and the attacker set an identical cookie, the attacker would have full access to the victim’s account. The session ID stolen does not still need to be valid – if the attacker attempted to use the stolen cookies to login the week after, the system would just create a new session. This isn’t really a security vulnerability in itself as a well written script should be XSS-proof but it makes attacks a lot easier if a cross site scripting hole was found.
The solution I’ve implemented is a bit different; users who don’t turn on persistant login get one simple session ID cookie. This session only lasts for a short time so there is a very short timeframe to attack. Users who enable persistent login get two additional cookies: Agent ID and Auth ID. The agent ID is a unique identifying string for the user agent (browser), and the authentication ID is a 32-character string which the script will check against the next time the user visits. When the user next visits, the script will check the agent ID and auth ID against the database. If there is a successful match, the user is automagically logged in. The auth ID will then change making the old auth ID useless. The auth ID also changes on every 1 in 20 page load afterwards.
This net result is the system should be less vulnerable to a stolen cookie attack. The time frame for the stolen cookie to be used is reduced – a stolen username and password cookie will be valid until the user changes his/her password (which is almost never). A stolen agent ID and auth ID will often be valid for no more than a few minutes. Tied with a couple of additional checks, a successful exploitation of a cross site scripting attack can be made a lot harder.