grizzly
grizzly copied to clipboard
Session timeout does not tolerate system time changes
The Session
object has built-in handling for timeouts, but it uses System.currentTimeMillis()
as its source of time. This is affected by operating system time changes, which has a number of strange/problematic effects:
- A time change forward shortens or immediately invalidates the session
- A time change backward lengthens the session or makes it effectively perpetual
In our application we are using the session timeout to invalidate the user's authentication, so this behavior has some pretty undesirable effects and possible security implications.
Devices that are connected to an NTP server may be affected by this behavior shortly after startup, when the time resynchronizes with the server. On some devices it may also be possible for an unprivileged user to modify the system time as a way to circumvent authentication time limits on the server.
Would it be possible to use System.nanoTime()
as a monotonic time source for the session expiration calculation?
Scenarios
The scenarios below assume the following request handling code:
public void service(Request request, Response response) throws Exception {
final Session session = request.getSession();
if (session.isNew()) {
System.out.println("New session used");
session.setSessionTimeout(TIMEOUT_MILLIS);
} else {
System.out.println("Old session used");
}
}
Time moves backward
Scenario
final long TIMEOUT_MILLIS = 10_000; // 10 seconds.
- Run server using the code above
- Send first request
- Change system time by
-2
minutes - Wait 15 seconds
- Send second request
Expected
Output should be:
New session used
New session used
No request is made to the server within the 10 second timeout. The session should expire, and the next request should trigger the creation of a new session.
Actual
Output is:
New session used
Old session used
The expiry calculation uses the underlying system time (which was moved backwards), so the session is not yet expired even though more than 10 seconds have passed.
Time moves forward
Scenario
final long TIMEOUT_MILLIS = 60_000; // 1 minute.
- Run server using the code above
- Send first request
- Change system time by
+2
minutes - Send second request
Expected
Output should be:
New session used
Old session used
A request is made to the server within the 1 minute timeout. The session should still be valid, and should be reused.
Actual
Output is:
New session used
New session used
The expiry calculation uses the underlying system time (which was moved forwards), so the session is immediately expired. A new session is created as a result.
@danielfrankcom Can you create a reproducer and a PR with suggested enhancements?