As @Arminus said in a comment, this is not session fixation. Session fixation is actually relatively rare today, because it relies on one behavior now (thankfully) widely recognized as insecure/stupid: when you log in, the server preserves the old token (JSESSIONID, in this case) rather than generating a new one. An attacker can abuse that by knowing the old token value (for example, because they were the last user) and then logging out and waiting for a new user to come along; when the new user logs in, they keep the old token.
A security vulnerability requires that somebody do something that they are not authorized to do. In your scenario, how the heck does the nonAdmin user find the Admin's JSESSIONID? If they're the same person, then the user is (presumably) already authorized to log in as an Admin - they did so on the other machine, after all - so no security bypass has occurred. If they aren't the same person, either the nonAdmin stole the Admin's JSESSIONID (in which case the security vulnerability is in how they stole it, which might be your fault or might be because the Admin user's computer has spyware), or the Admin user shared the JSESSIONID with the nonAdmin (which means the Admin is, essentially, deputizing the nonAdmin as a temporary user of the Admin's account).
Authentication tokens (JSESSIONIDs, in your case) have a number of critical properties. The following list is in no particular order. The relevant ones here are highlighted:
- They are securely generated (i.e. don't use
java.util.Random, which has predictable output; use
java.security.SecureRandom). Some implementations use encrypted user-info strings instead of random tokens; this has some pretty bad security risks.
- They are long enough that they can't be brute-forced (64 bits is generally seen as borderline; 128 bits and up is good).
- They are never re-used, so an attacker who sees an old one can't use it to get somebody else's session. Since keeping track of all the old session tokens is infeasible, the solution is again to use a long-enough (random) token that the odds of a collision are infinitesimal (and if you express the odds against it happening using words like "billion", it's not infinitesimal enough).
- They must be checked in a constant-time manner (otherwise brute-forcing a valid token becomes linear in its length, rather than exponential).
- They must not be shared across privilege levels, or sessions for a given user. Any time a user re-authenticates, the old token must be invalidated and a new one used instead.
- Token invalidation must occur on the server. If a user logs out or their session expires (or otherwise ends), the server must no longer recognize the token as valid. Simply deleting the token off the client is not sufficient.
- Tokens must never be sent in a URL, only in headers or if necessary in message bodies. Even over HTTPS, URLs tend to leak (they may be recorded by proxies, included in referrer headers, or saved in browser history, among other risks). If a secure value absolutely must be sent in a URL, it must be single-use only and invalidated after the first attempt to use it, whether or not the user completed their action, and should also time out very quickly.
- Tokens must never be sent over insecure connections. If your site supports HTTPS, then the token must never be sent over a non-HTTPS connection (an easy way to ensure this is have the token in a cookie, and set the
secure flag on that cookie). If your site doesn't support HTTPS, then treat sessions as convenience only; there is no real security there. (Yes, looking at you, SE default post-authentication behavior...)
I'm probably forgetting some, but a comprehensive list is not the core point here. Still, as you can see, there are a bunch of things (some related, some independent) required for a secure authentication token, which is just one component of a secure site. You have to do them all; one weak link is usually enough for an attacker to get in.