How to change FormsCookieName at runtime in ASP.NET - asp.net

We would like to have the FormsCookieName of FormsCookiePath change per instance of our application. We have an application which has multiple instances on 1 server/domainname. Because of this we can only work in 1 application at the same time, since the cookies will overwrite eachother. Same for the Sessions btw.
Is there a way to dynamicly, for example in the Global.asax Application_Start, change this name? This would be usefull as we keep a license name in each application which could be used as the basis for the CookieName.
We already work with Web.config and extra files to overwrite Web.config values in external files using: <appSettings file="Web.AppSettings.Config">
But this requires manual actions which can be forgotten and are redundant since the settings can be retrieved from the database.
Thanks.

I had similar situation, I did the following. In the Application_Start, I checked to see if my cookie name needed change. This would occur after a new deployment for all applications where I have the same web.config for all.
protected void Application_Start(object sender, EventArgs e)
{
// determine unique cookie name per application
string cookieName = ...
// Get the web.config forms settings
Configuration c = WebConfigurationManager.OpenWebConfiguration("~");
AuthenticationSection auth = c.GetSection("system.web/authentication")
as AuthenticationSection;
// See if we have mismatch in web.config or in Forms cookiename
if (auth != null && auth.Forms != null &&
(auth.Forms.Name != cookieName
|| FormsAuthentication.FormsCookieName != cookieName
)
)
{
// Assign value in web.config for future restarts
auth.Forms.Name = cookieName;
// would be nice if this restarted the app, but it doesn't appear to
c.Save();
// This seems to restart the app
System.Web.HttpRuntime.UnloadAppDomain();
}
...
}
The web.config is modified on the application start and then the web app is restarted. Next time the web app comes up, cookie names are in sync and the reset code is skipped.

I have been struggling with Cookies with quite a few days. It has been an awesome learning experience.
So wanted to share the possible ways I found & discovered: There are several HACKs to modify Forms Authentication Cookie name:
You can automate the modification of cookie name under Authenticaiton secion of Web.Config file in Application_Start event in Global.asax. Thanks to Ron for sharing this. But I could not guarantee that the user whose identity would be used to run application domain have enough privileges to modify the file on disk or not. Hence I needed an improvised solution, so I devised following.
Thanks to ILSpy for letting me see inside the FormsAuthentication class, and many thanks to Reflection to let me modify the private field of a class. I used following code to modify the cookie name on run-time with following small piece of code and this worked like a charm !!!
protected void Application_Start(Object sender, EventArgs e)
{
// This will enforce that FormsAuthentication class is loaded from configuration settings for the application.
FormsAuthentication.Initialize();
// The new cookie name whatever you need can go here, I needed some value from my application setting to be prefixed so I used it.
string newCookieName = string.Format("{0}.ASPXAUTH", ConfigurationManager.AppSettings["SomeSettingThatIsUniquetoSite"]);
// Modifying underlying baking field that points to FormsAuthentication.FormsCookieName
Type type = typeof(FormsAuthentication);
System.Reflection.FieldInfo field = type.GetField("_FormsName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
field.SetValue(null, newCookieName);
}
Suggestions, loopholes are requested as this is my first answer on this forum.

According to MSDN, the FormsAuthentication.FormsCookieName property that stores the cookie name is a read-only property. This property must be read from the web.config.
Each instance will need a separate name in the web.config. I suggest including the name of the authentication cookie in your existing change management system.

Related

SessionID changing across different instances in Azure (and probably in a web farm)

I have a problem with an Azure project with one WebRole but multiple instances that uses cookieless sessions. The application doesn't need Session storage, so it's not using any session storage provider, but I need to track the SessionID. Apparently, the SessionID should be the same accross the WebRole instances, but it changes suddently w/o explanation. We are using the SessionID to track some data, so it's very important.
In order to reproduce the issue:
Create a Cloud Project.
Add a ASP.NET Web Role. The code already in it will do.
Open Default.aspx
Add a control to see the current SessionID and a button to cause a postback
<p><%= Session.SessionID %></p>
<asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
Add a event handler for button that will delay the response a bit:
protected void Button1_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(150);
}
Open Web.Config
Enable cookieless sessions:
<system.web>
<sessionState cookieless="true" />
</system.web>
Run the project, and hit fast and repeteadly the "PostBack" button for a while giving attention to the session id in the address bar. Nothing happens, the session id is always the same :). Stop it.
Open ServiceConfiguration.csfg
Enable four instances:
<Instances count="4" />
Ensure that in the Web.config there is a line related with the machine key that has been added automatically by Visual Studio. (at the end of system.web).
Rerun the project, hit fast and repeteadly the "Postback" button for a while and give attention to the session id in the address bar. You'll see how the SessionID changes after a while.
Why is this happening? As far as I know, if all machines share the machineKey, the session should be the same across them. With cookies there are no problems, the issue apparently is just when cookieless sessions are used.
My best guess, is that something wrong is happening when there are several instances, when the SessionID generated in one WebRole goes to another, is rejected and regenerated. That doesn't make sense, as all the WebRoles have the same machineKey.
In order to find out the problem, and see it more clearly, I created my own SessionIDManager:
public class MySessionIDManager : SessionIDManager
{
public override string CreateSessionID(HttpContext context)
{
if (context.Items.Contains("AspCookielessSession"))
{
String formerSessionID = context.Items["AspCookielessSession"].ToString();
// if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
// Debugger.Break();
return formerSessionID;
}
else
{
return base.CreateSessionID(context);
}
}
}
And to use it change this line in the WebConfig:
<sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />
Now you can see that the SessionID doesn't change, no matter how fast and for how long you hit. If you uncomment those two lines, you will see how ASP.NET is creating a new sessionID even when there is already one.
In order to force ASP.NET to create a new session, just a redirect to an absolute URL in your site:
Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));
Why is this thing happening with cookieless sessions?
How reliable is my solution in MySessionIDManager ?
Kind regards.
UPDATE:
I've tried this workaround:
User-Specified Machine Keys
Overwritten by Site-Level Auto
Configuration, but the problem
still stands.
public override bool OnStart()
{
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
using (var server = new ServerManager())
{
try
{
// get the site's web configuration
var siteNameFromServiceModel = "Web"; // update this site name for your site.
var siteName =
string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
var siteConfig = server.Sites[siteName].GetWebConfiguration();
// get the appSettings section
var appSettings = siteConfig.GetSection("appSettings").GetCollection()
.ToDictionary(e => (string)e["key"], e => (string)e["value"]);
// reconfigure the machine key
var machineKeySection = siteConfig.GetSection("system.web/machineKey");
machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]);
machineKeySection.SetAttributeValue("validation", appSettings["validation"]);
machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]);
machineKeySection.SetAttributeValue("decryption", appSettings["decryption"]);
server.CommitChanges();
_init = true;
}
catch
{
}
}
return base.OnStart();
}
I've also tried this about put a
session start handler and add
some data, but no luck.
void Session_Start(object sender, EventArgs e)
{
Session.Add("dummyObject", "dummy");
}
Bounty up!
In short, unless you use cookies or a session provider there is no way for the session id to pass from one web role instance to the other. The post you mention says that the SessionID does NOT stay the same across web roles if you don't use cookies or session storage.
Check this previous question for ways to handle state storage in Azure, e.g. using Table Storage
The machineKey has nothing to do with sessions or the application domain, it is the key used to encrypt,decrypt,validate authentication and viewstate data. To verify this open SessionIDManager.CreateSessionID with Reflector. You will see that the ID value is just a random 16-byte value encoded as a string.
The AspCookielessSession value is already checked by SessionIDManager in the GetSessionID method, not CreateSessionID so the check is already finished before your code gets executed. Since the default sessionstate mode is InProc it makes sence that separate web roles will not be able to validate the session key so they create a new one.
In fact, a role may migrate to a different physical machine at any time, in which case its state will be lost. This post from the SQL Azure Team describes a way to use SQL Azure to store state for exactly this reason.
EDIT I finally got TableStorageSessionStateProvider to work in cookieless mode!
While TableStorageSessionStateProvider does support cookieless mode by overriding SessionStateStoreProviderBase.CreateUnititializedItem, it fails to handle empty sessions properly in private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge,out object lockId, out SessionStateActions actions,bool exclusive). The solution is to return an empty SessionStateStoreData if no data is found in the underlying blob storage.
The method is 145 lines long so I won't paste it here. Search for the following code block
if (actions == SessionStateActions.InitializeItem)
{
// Return an empty SessionStateStoreData
result = new SessionStateStoreData(new SessionStateItemCollection(),
}
This block returns an empty session data object when a new session is created. Unfortunately the empty data object is not stored to the blob storage.
Replace the first line with the following line to make it return an empty object if the blob is empty:
if (actions == SessionStateActions.InitializeItem || stream.Length==0)
Long stroy short cookieles session state works as long as the provider supports it. You'll have to decide whether using cookieless state justifies using a sample provider though. Perhaps vtortola should check the AppFabric Caching CTP. It includes out-of-the-box ASP.NET providers, is a lot faster and it definitely has better support than the sample providers. There is even a step-by-step tutorial on how to set session state up with it.
Sounds tricky.
I have one suggestion/question for you. Don't know if it will help - but you sound like you're ready to try anything!
It sounds like maybe the session manager on the new machine is checking the central session storage provider and, when it finds that the session storage is empty, then it's issuing a new session key.
I think a solution may come from:
- using Session_Start as you have above in order to insert something into Session storage
- plus inserting a persistent Session storage provider of some description into the web.config - e.g. some of the oldest Azure samples provide a table based provider, or some of the newer samples provide an AppFabric caching solution.
I know your design is not using the session storage, but maybe you need to put something in (a bit like your Session_Start), plus you need to define something other than in-process session management.
Alternatively, you need to redesign your app around something other than ASP.NET sessions.
Hope that helps - good luck!
I experienced the same problem and after much research and debugging I found that the issue occurred because the "virtual servers" in the Azure SDK map the websites to different paths in the IIS metabase. (You can see this through through Request.ServerVariables["APPL_MD_PATH"].)
I just found this out now but wanted to post this so people could get working on testing it. My theory is that this problem may go away once it's published out to Azure proper. I'll update with any results I find.

Authentication and Security in my website - need advice please

I am using database with a list of username/passwords, and a simple web form that allows for users to enter their username/password.
When they submit the page, I simply do a stored procedure check to authenticate. If they are authorised, then their user details (e.g. username, dob, address, company address, other important info) are stored in a custom User object and then in a session. This custom User object that I created is used throughout the web application, and also in a sub-site (session sharing).
My question/problems are:
Is my method of authentication the correct way to do things?
I find users complaining that their session have expired although they "were not idle", possibly due the app pool recycling? They type large amounts of text and find that their session had expired and thus lose all the text typed in. I am uncertain whether the session does really reset sporadically but will Forms Authentication using cookies/cookiless resolve the issue?
Alternatively should I build and store the User Object in a session, cookie or something else instead in order to be more "correct" and avoid cases like in point #2.
If I go down the Forms Authentication route, I believe I cannot store my custom User object in a Forms Authentication cookie so does it mean I would store the UserID and then recreate the user object on every page? Would this not be a huge increase on the server load?
Advice and answers much appreciated.
L
It doesn't really care whether you use your own authentication system, or the default membership providers when using such a simple scenario.
You should avoid using the InProc session state when the app might recycle some times a day. Rather store your session into a database (SqlSessionState) or use a StateServer. Then the application pool can recycle all day without interferring with your sessions. Setting the session timeout to 60 minutes or something, will solve the remaining issues. Never use cookieless sessions (unless you know what you're doing), as they make it way too easy to steal one's session.
Just store it into the session (or profile if you use the default membership provider). Not only is a cookie easily readible, it is also limited to 4 KB.
No, you will have a profile where all the user information is stored. It doesn't really matter whether you use forms authentication or a custom system that stores it's data into SqlSessionState. The membership provider will store the Profile ID into a cookie, same as the session state will save the Session ID into a cookie.
You can use ASP.NET Membership, Roles, Forms Authentication, and Security Resources
I will give an example using c#
For reference Forms Authentication in ASP.NET 2.0
//code for checking user name & password
protected void btnlogin_Click(object sender, EventArgs e)
{
try
{
if (txtUserName.Text.Trim() != "" && txtPassword.Text.Trim() != "")
{
User obj = objUser.UserAuthenticate(txtUserName.Text.Trim(), txtPassword.Text.Trim());
if (obj != null)
{
//To set AuthenticationCookie of usertype "User"
SetAuthenticationCookie("User", obj.UserID.ToString(), obj.DisplayName);
HttpCookie usercookie = new HttpCookie("LoginTime");
usercookie.Value = DateTime.Now.ToString();
Response.Cookies.Add(usercookie);
HttpCookie namecookie = new HttpCookie("LoginName");
namecookie.Value = obj.DisplayName;
Response.Cookies.Add(namecookie);
}
else
{
lblMsg.Text = "Invalid Username or Password.";
}
}
else
{
//lblMsg.Visible = true;
}
}
catch (Exception ex)
{
//lblMsg.Visible = true;
throw ex;
}
}
private void SetAuthenticationCookie(string role, string userid, string name)
{
string userdata = "logintype=user|role=" + role + "|email=" + txtUserName.Text.Trim() + "|name=" + name;
FormsAuthenticationTicket faTicket = new FormsAuthenticationTicket(1, userid,
DateTime.Now, DateTime.Now.AddHours(+1),
false, userdata);
HttpCookie authCookie = new HttpCookie( FormsAuthentication.FormsCookieName,//"martinfernandez#ispg.in",
FormsAuthentication.Encrypt(faTicket));
authCookie.Expires = faTicket.Expiration;
Response.Cookies.Add(authCookie);
}
//code inside global.asax.cs
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.User != null && Context.User.Identity is FormsIdentity && Context.User.Identity.IsAuthenticated)
{
FormsAuthenticationTicket faTicket = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value);
string[] userdata = faTicket.UserData.Split("|".ToCharArray());
string logintype = "";
string email = "";
string uname = "";
string roleString = "";
foreach (string s in userdata)
{
string keyname = s.Split("=".ToCharArray())[0];
if (keyname == "logintype")
{
logintype = s.Split("=".ToCharArray())[1];
}
else if (keyname == "role")
{
roleString = s.Split("=".ToCharArray())[1];
}
}
string[] rolesArray = roleString.Split(";".ToCharArray());
Context.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(faTicket), rolesArray);
}
}
Just to touch on point #4 - it would be more efficient memory-wise to not store everyone's "User" object in memory, and re-create the object each HTTP request. This way you also re-verify the login details-- what if someone's account is compromised and the actual user changes their password to try and protect their account, but the "bad user" has already logged in? Under your security mechanism, the "bad user" can keep browsing away since the user data is cached and not re-verified each postback.
Here are the some general security measures that beginner and Advance Web Developer must follow.
#15 Steps To Secure Your Website
1 : Prevent Image Hotlinking (IMP)
Image hotlinking is the process of using someone else’s Image URL in our Website and using their bandwidth. In order to prevent this mystery, we can prevent access for external server by adding following line in code.
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourdomain.com [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]
2 : Prevent CSRF (Cross Site Request Forgery) Attacks
In order to prevent CSRF attacks on your GET and POST requests for form submission, you can use following 2 techniques.
The first it to include a random token with each request, this is a unique string that is generated for each session.
The second method is to use random name for each form field.
3 : Prevent Directory Access / Disable Indexing
Add the following line to your .htaccess file.
Options -Indexes
4 : Secure your Private and CMS Login with IP restrictions
IP restriction is a bit way advanced yet effective method to stop the unauthorized personnel to access a particular area of your website.
Here is an example htaccess code to IP restrict the access to a particular location.
ALLOW USER BY IP
<Limit GET POST>
order deny,allow
deny from all
allow from 1.2.3.4
</Limit>
5 : Protect your .htaccess file
You can write this below piece of code in your htaccess file which do not let any others access your htaccess file.
<Files ~ “^.*.([Hh][Tt][Aa])”>
order allow,deny
deny from all
satisfy all
</Files>
6 : Function access rule
By adding "_" as a prefix for Function name, we can prevent function to be called from The Web publicly. This is the best practice when we need some specific function to be accessed via AJAX only.
7 : Lock down your directory and file permissions
File permissions define who can do what to a file.
"Read" = 4 : View the file contents.
"Write" = 2 : Change the file contents.
"Execute" = 1 : Run the program file or script.
8 : Prevent Cron Job to be run from Web Browser
By adding, following line of code in your page, you can protect your page to be accessed from a web browser.
if( ! $this->input->is_cli_request() ) {
die("Only CLI Requests Allowed");
}
9 : Hide admin pages to be crawled by Google
You do not want your admin pages to be indexed by search engines, so you should use the robots_txt file to discourage search engines from listing them.
10 : Disable Right Click on Page if not require
Disabling “right-click” as a way to view your website source code by inspect element to secure Website content for general users.
11 : Use Strong Password for CMS
Keep practice to set random Password with the special character only.
12 : Make Admin Directory Tough to guess
It may happen that Hackers can use scripts that scan all the directories on your web server for giveaway names like ‘admin’ or ‘login’ etc. and your significant stuff may get leaked.
13 : Change your database table prefix
Add a prefix (the mixture of project name and year) which would be hard to presume for a secured side.
To illustrate,
A) BPM Supreme => bpm14_download
B) Glickin => gk15_admin
C) TravelWorthy => tw16_user
14 : Prevent User's Password, that's as important as yours
Regarding Password Encryption algorithm, Use sha1 algorithm instead of tradition algorithm Md5 which is the very old way and becoming less secure nowadays as per sources.
Reference : http://php.net/manual/en/function.sha1.php
15 : Hide Error Log
During development Mode, keep error reporting "ALL" and once we go LIVE change it to "0" without forgetting. Over here
Reference : http://php.net/manual/en/function.error-reporting.php
My advice would be to use asp.net membership and roles (Microsoft). It is a very good security solution - login security, roles (permissions) and is stored in a SQLServer database (not sure if it can be stored elsewhere).
I use it on my site and you can use membership controls straight out of the box (login forms, change password, etc.) or you can roll your own.
The only tricky bit I found was setting up the membership tables, views and stored procs in my dB (you download a dB script), but really it was fairly straightforward to implement.
Here's a link to asp.net membership and roles

Invalidating ASP.NET FormsAuthentication server side

I am experimenting with FormsAuthentication (using ASP.NET MVC2) and it is working fairly well.
However, one case I can't work out how to deal with is validating the user identity on the server to ensure it is still valid from the server's perspective.
eg.
User logs in ... gets a cookie/ticket
Out of band the user is deleted on the server side
User makes a new request to the server. HttpContext.User.Identity.Name is set to the deleted user.
I can detect this fine, but what is the correct way to handle it? Calling FormsAuthentication.SignOut in the OnAuthorization on OnActionExecuting events is too late to affect the current request.
Alternatively I would like to be able to calls FormsAuthentication.InvalidateUser(...) when the user is deleted (or database recreated) to invalidate all tickets for a given (or all) users. But I can't find an API to do this.
In the global.asax, add an handler for AuthenticateRequest. In this method, the forms authentication has already taken place and you're free to modify the current principal before anything else happens.
protected void Application_AuthenticateRequest(object sender, EventArgs e) {
IPrincipal principal = HttpContext.Current.User;
if (!UserStillValid(principal)) {
IPrincipal anonymousPrincipal = new GenericPrincipal(new GenericIdentity(String.Empty), null);
Thread.CurrentPrincipal = anonymousPrincipal;
HttpContext.Current.User = anonymousPrincipal;
}
}
Just implement the UserStillValid method and you're done. It's also a good place to swap the generic principal with a custom one if you need to.

Set Path dynamically in Forms Authentication

Here's the problem we facing.
In a hosted environment setup, we're hosting the same project multiple times. We currently manually specify a Path in the forms config section of our web.config. However, to smooth out our deployment process, we'd like to set the Path depending on the Virtual Directory name.
Is there a way for us to dynamically set the Path in the web.config?
There's an overload of FormsAuthentication.SetAuthCookie that takes the cookie path as a parameter, so if you're handling the login process yourself then you can just pass the path of your choice.
The problem is that the standard System.Web.UI.WebControls.Login will only use the default path value. You could, however, handle the LoggedIn event to fix the path...
void FixCookie( object sender, EventArgs args )
{
Response.Cookies[FormsAuthentication.FormsCookieName].Path = "/my-custom-path";
}

How can I determine the number of users on an ASP.NET site (IIS)? And their info?

Is there a way to determine the number of users that have active sessions in an ASP.NET application? I have an admin/tools page in a particular application, and I would like to display info regarding all open sessions, such as the number of sessions, and perhaps the requesting machines' addresses, or other credential information for each user.
In global.aspx
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["OnlineUsers"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] + 1;
Application.UnLock();
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate
// mode is set to InProc in the Web.config file.
// If session mode is set to StateServer or SQLServer,
// the event is not raised.
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] - 1;
Application.UnLock();
}
Note: The Application.Lock and Application.Unlock methods are used to prevent multiple threads from changing this variable at the same time.
In Web.config
Verify that the SessionState is "InProc" for this to work
<system.web>
<sessionState mode="InProc" cookieless="false" timeout="20" />
</system.web>
In your .aspx file
Visitors online: <%= Application["OnlineUsers"].ToString() %>
Note: Code was originally copied from http://www.aspdotnetfaq.com/Faq/How-to-show-number-of-online-users-visitors-for-ASP-NET-website.aspx (link no longer active)
ASP.NET Performance Counters like State Server Sessions Active (The number of active user sessions) should help you out. Then you can just read and display the performance counters from your admin page..
If you are using .net Membership you could use
Membership.GetNumberOfUsersOnline()
More about it: http://msdn.microsoft.com/en-us/library/system.web.security.membership.getnumberofusersonline.aspx
If you'd like to implement the same mechanism by yourself, you can define like a CurrentUserManager class and implement the singleton pattern here. This singleton object of class CurrentUserManager would be unique in the AppDomain. In this class you will create its self instance once, and you will prohibit the others from creating new instances of this class by hiding its constructor. Whenever a request comes to this object, that single instance will give the response. So, if you implement a list that keeps the records of every user (when a user comes in, you add him to the list; when he goes out, you remove him from the list). And lastly, if you want the current user count you could just ask the list count to this singleton object.
if you use sql server as the session state provider you can use this code to count the number of online users:
SELECT Count(*) As Onlines FROM ASPStateTempSessions WHERE Expires>getutcdate()
The way I've seen this done in the past is adding extra code to the Session_OnStart event in the Global.asax file to store the information in a session agnostic way, e.g. a database or the HttpApplicationState object. Depending upon your needs you could also use Session_OnEnd to remove this information.
You may want to initialise and clean up some of this information using the Application_Start and Application_End events.
The administration page can then read this information and display statistics etc.
This is explained in more depth at http://msdn.microsoft.com/en-us/library/ms178594.aspx and http://msdn.microsoft.com/en-us/library/ms178581.aspx.
You can use PerformanceCounter to get data from System.Diagnostics namespace. It allows you to get "Sessions Active" and much more. It allows you to get from local server as well as remote.
Here is an example of how to do it on local machine
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__");
Console.WriteLine(pc.NextValue());
}
or for remote server you would do:
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__", "ServerHostName.domain");
Console.WriteLine(pc.NextValue());
}
Performance Counters for ASP.NET provides full list of ASP.NET counters that you can monitor
Google Analytics comes with an API that can be implemented on your ASP.NET MVC Application.
It has RealTime functionality so the current amount of users on your website can be tracked and returned to your application.
Here's some information

Resources