SessionSecurityToken.ValidTo and TokenCacheItem.Expires mismatch - session

We're writing a custom SessionSecurityTokenCache so that our FedAuth cookies are valid across a webfarm with IsReferenceMode = true. We are also trying to enable sliding sessions, but it opens up a few questions around token expiration:
In Global.asax, we are handling the SessionAuthenticationModule_SessionSecurityTokenReceived event to check the remaining time on the token and reissue it if it is due to expire (within 5 mins, for example). In here, when we check token.ValidTo, it does not equal the expiryTime of the TokenCacheItem that we retrieved from cache. Why? How is TokenCacheItem.Expires supposed to be used?
In the AddOrUpdate method you're required to override, there's an expiryTime parameter; what is the intended use of this? Also asked here.

Related

Why $time from $lock=Cache::lock('name', $time) should be greater than the updating Cache time?

I placed this code inside a Route::get() method only to test it quicker. So this is how it looks:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function(){
$lock = Cache::lock('test', 4);
if($lock->get()){
Cache::put('name', 'SomeName'.now());
dump(Cache::get('name'));
sleep(5);
// dump('inside get');
}else{
dump('locked');
}
// $lock->release();
});
If you reach this route from two browsers (almost)at the same time. They both will respond with the result from dump(Cache::get('name'));. Shouldn't the second browser respond be "locked"? Because when it calls the $lock->get() that is supposed to return false? And that because when the second browser tries to reach this route the lock should be still set.
That same code works just fine if the time required for the code after the $lock = Cache::lock('test', 4) to be executed is less than 4. If you set the sleep($sec) when $sec<4 you will see that the first browser reaching this route will respond with the result from Cache::get('name') and the second browser will respond with "locked" as expected.
Can anyone explain why is this happening? Isn't it suppose that any get() method to that lock, expect the first one, to return false for that amount of time the lock has been set? I used 2 different browsers but it works the same with 2 tabs from the same browser too.
Quote from the 5.6 docs https://laravel.com/docs/5.6/cache#atomic-locks:
To utilize this feature, your application must be using the memcached or redis cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server.
Quote from the 5.8 docs https://laravel.com/docs/5.8/cache#atomic-locks:
To utilize this feature, your application must be using the memcached, dynamodb, or redis cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server.
Quote from the 8.0 docs https://laravel.com/docs/8.x/cache#atomic-locks:
To utilize this feature, your application must be using the memcached, redis, dynamodb, database, file, or array cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server.
Apparently, they have been adding support for more drivers to make use of this lock functionality. Check which Cache driver you are using and if it fits the support list of your Laravel version.
There is likely an atomicity issue here where the cache driver you are using is not able to lock a file atomically. What should happen is that when a process (i.e. a php request) is writing to the lock file, all other processes requiring the lock file should at least wait until the lock file available to be read again. If not, they read the lock file before it has been written to, which obviously causes a race condition.
I saw this question I asked, well now I can say that the problem I was trying to solve here was not because of the atomic lock. The problem here is the sleep method. If the time provided to the sleep method is bigger than the time that a lock will live, it means when the next request it's able to hit the route the lock time will expire(will be released). And that's because let's say you have defined a route like this:
Route::get('case/{value}', function($value){
if($value){
dump('hit-1');
}else{
sleep(5);
dump('hit-0');
}
});
And you open two browser tabs with the same URL that hits this route something like:
127.0.0.1:8000/case/0
and
127.0.0.1:8000/case/1
It will show you that the first route will take 5sec to finish execution and even if the second request is sent almost at the same time with the first request, still it will wait to finish the first one and then run. This means the second request will last 5sec(from the first request) plus the time it took to run.
Back to the asked question the lock time will expire by the time the second request will get it or said differently run the $lock->get() statement.

getting/setting browser_id with Products.BeakerSessionDataManager

I'm having a problem associating a browser_id to a session when using Products.BeakerSessionDataManager. I'm working on Plone 5.
As far as I understand Zope sessions, as soon as .getSessionData() is called on a session data manager, a session data container is created if it did not exist. Furthermore, this data will contain a token, which is the same as the browser_id associated with the browser making the request. And finally, a cookie is set on the response with the name _ZopeId (and the value is the same as the token). Thus, when I use the default session data manager that come with Zope, I get this:
ipdb> context.session_data_manager.getSessionData()
id: 14737473151418102847, token: 38878600A7nh90DE9ao, content keys: []
However, when I use Products.BeakerSessionDataManager, the same call gives me this:
ipdb> context.session_data_manager.getSessionData()
{'_accessed_time': 1473745441.437582, '_creation_time': 1473745441.437582}
Moreover, no cookie is set.
Perusing some old Zope docs, I found a reference to getContainerKey(), so I thought that might get me the browser_id. However, the returned value is different on every request, so that does not work. Also, calling .getBrowserIdManager().getBrowserId() on the session_data_manager throws an error, because Beaker does not support browser id managers.
I want to set a cookie, and I want a token. I'm doing this so that I can identify anonymous clients in a voting application, so that they will not cast multiple votes (at least not in the same session - there are other mechanisms to allow voting only when certain other conditions are met).
Am I misunderstanding the machinery, or am I missing something?

Nancy authentication session timeout

I am using Nancy.Authentication.Forms. The users are vociferously complaining that their sessions are timing out too quickly (less than 5 minutes). I've searched high and low but cannot find how to set the session timeout time to one hour. Any help is greatly appreciated.
Thanks,
Ross
I'm not 100% sure what could be causing it because of the little information provided. But I think your problem is probably due to the encryption/decryption of the cookie.
Cookie Encryption differs on application startup
By default, a new key is created every time you start the application, which would cause decryption of the cookie to fail, causing authentication to fail, and requiring you to re-authenticate to create a new cookie.
By setting your own keys, every after restart the decryption will always be the same. This is VERY important if you have a load balancer.
https://github.com/NancyFx/Nancy/wiki/Forms-Authentication#a-word-on-cryptography
Since I also did not find an answer, I would like to post my research result:
WithCookie accepts an expiration DateTime.
https://github.com/NancyFx/Nancy/blob/a9989a68db1c822d897932b2af5689284fe2ceb8/src/Nancy/ResponseExtensions.cs
public static Response WithCookie(this Response response, string name, string value, DateTime? expires);
In my case it would be:
return Response.AsRedirect(redirectUrl)
.WithCookie(SecurityExtensions.CookieName, newSession.Id.ToString(), DateTime.Now.AddHours(1));
For the session duration itself I didn't find anything yet.

ServerXMLHTTP Timeout in less than 2 seconds using default timeouts

I have a question involving a "timeout" when sending an HTTPS "GET" request using the ServerXMLHTTP object.
In order to fool the object to send the request with the logged in user's id and password, I set it up to use a dummy proxy and then excluded the domain of the URL (on the intranet). So variable url_to_get contains .mydomain.com, while the proxy address is actually "not.used.com".
// JScript source code
HTTP_RequestObject = new ActiveXObject("Msxml2.ServerXMLHTTP.6.0");
// Using logged in username authentication
HTTP_RequestObject.open("GET", url_to_get, false);
HTTP_RequestObject.setProxy(2, "not.used.com", "*.mydomain.com");
try
{
HTTP_RequestObject.send();
}
catch (e)
{
}
In the catch block, I log an exception of "(0x80072EE2) The operation timed out". This is timestamped 1 to 2 seconds after a log message right before the open.
A retry will work as expected, and it can do it over and over again. Is this something on the server side? Or is it a result of the proxy?
Well this was painful and embarassing. I figured out the root cause of the issue I was seeing with the timeout. I had set the timeouts to "defaults" that were 3 orders of magnitude smaller than the real defaults. So even when I increased them to what I thought were really large values, I was still shorter than the defaults.
I had skimmed through the wording on the Microsoft page describing the setTimouts() method and misinterpreted the timebase of the parameters. I assumed seconds while it was actually milliseconds.
During the process of debugging this issue, I duplicated the code using the alternative COM object, "WinHttp.WinHttpRequest.5.1", and in verifying the equivalent API, SetTimeouts(), discovered the faux pas.
I did learn a few things in the process, so all is not lost, "WinHttp.WinHttpRequest.5.1" has a SetAutoLogonPolicy() [3] method that allows me to skip the hokey "proxy" foolishness required with "Msxml2.ServerXMLHTTP.6.0" to force it to send the user's credentials to an intranet server. I also fiddled with Fiddler [4] and learned enough to be dangerous!
Hopefully someone else can learn from my mistake and find this useful in debugging their own issue in the future.
Here are some inline links since I don't have enough rep to post more than two:
[3] : msdn.microsoft.com/en-us/library/windows/desktop/aa384050%28v=vs.85%29.aspx
[4] : fiddler2.com
Msxml2.ServerXMLHTTP.6.0 can be used via a proxy. It took me a while but persistence paid off. I moved from WinHttp.WinHttpRequest.5.1 to Msxml2.ServerXMLHTTP.6.0 at the advice of Microsoft.
Where varHTTP is your object reference to Msxml2.ServerXMLHTTP.6.0 you can use
varHTTP.setproxy 2, ProxyServerName, "BypassList"
Hope this helps and you pursue onwards with Msxml2.ServerXMLHTTP.6.0.

ASP.NET MVC3 Forms Authentication user logon session renew

I have an AJAX method to call on server to return ".ASPXAUTH" cookie expiration time.
It works properly when the auth cookie presents.
Besides I want to renew user logon session with another AJAX call. I have a blank method "RenewSession" which is just for to make a call to the server. Is there any way to do this using Forms Authentication?
The problem is in that when I make a request to server to my "RenewSession" method to renew the session Response.Cookies array is always containing 0 items. But actually when the ".ASPXAUTH" cookie expiration time gets to 0 it renews.
So can anyone explain is it a browsers' or ASP.NET/MVCs' behaviour?
Maybe I need sliding expiration to be set to "true"?
Or maybe in my renew method I should re-login the user and put a new cookie in the response?
Thank you!
FormsAuthentication expiration is really a matter of two parts:
the expiration of the authentication ticket
the expiration of the cookie containing the ticket
If you want to leave sliding expiration off, and renew the ticket manually, you need to renew the ticket and return a new authentication cookie to the browser.
The Response.Cookies array is empty unless you (or other code) add something to it. It's only meant for adding cookies that are new or whose contents/expiration/whatever have changed. An empty Response.Cookies only means that nothing has changed - the browser will keep the cookies it already has (until they expire) and still send them on the next request.
The standard way of modifying cookie contents or expiration is to take a cookie the browser sent (from Request.Cookies), modify it, and then add it to Response.Cookies.
Here's a bit of sample code for manually renewing the authentication cookie (disclamer: Test thoroughly and think):
// You could also get the ticket from
// Request.Cookies using FormsAuthentication.Decode
FormsIdentity identity = HttpContext.Current.User.Identity as FormsIdentity;
if (identity == null) return; // User isn't authenticated
// Renew the ticket - you could also create a new ticket manually
// (see * below for an example), if you want to get rid of ASP.NET's
// rather confusing renew-if-old policy:
FormsAuthenticationTicket ticket =
FormsAuthentication.RenewTicketIfOld(identity.Ticket);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
encryptedTicket
);
// Better keep this (see * below):
cookie.Secure = FormsAuthentication.RequireSSL;
cookie.HttpOnly = true;
// Isn't a security issue if this is set too long - the ticket contained
// within will still expire after the set time, and the server will timeout
// the auth session on the next request.
// But let's just keep cookie and ticket in sync:
cookie.Expire = ticket.Expiration;
// Add cookie to response to send the changes to the browser:
HttpContext.Current.Response.Cookies.Add(cookie);
Note that FormsAuthentication.RenewTicketIfOld() won't always renew the ticket. It will only renew if less than half of the expiration time is left. I.e., if your timeout in web.config is set to 20 minutes and RenewTicketIfOld is called 7 minutes after the ticket was created, the ticket won't be renewed, and there'll still be 13 minutes left. If it's called after e.g. 12 minutes, it will be renewed to 20 minutes.
The reason for this is because RenewTicketIfOld is used by slidingExpiration on every request and so would send back a new cookie on every request (to reset the expiration to [timeout] minutes). Only sending a new ticket cookie back when at least half the time has elapsed gets rid of a lot of cookie overhead - at the expense of being confusing to developers and end users.
*) On cookie.Secure, see Hanselman: Weird Timeouts - this simply makes sure that if RequireSSL is set in web.config, the cookie will honor that, which avoids many a debugging nightmare if you ever move the site to SSL.

Resources