Use the ASP.NET request user for Log4net log entries - asp.net

My ASP.NET site is using Integrated Authentication with impersonation turned off. I have added a "%username" to my "conversionPattern" in the web.config to add the user name to each logging entry. However this will use the application pool identity's user name, and not the current visiting user's name.
Any ideas on how best to do this? Do I have to write a custom appender? I don't mind any minor performance penalties as this is a small intranet site. Thanks!

There's built in ASP.NET pattern converters in recent versions of log4net:
%aspnet-request{REMOTE_ADDR} and %aspnet-request{AUTH_USER}
https://stackoverflow.com/a/7788792/74585
You can also access the contents of
aspnet-cache
aspnet-context
aspnet-session
https://logging.apache.org/log4net/log4net-1.2.13/release/sdk/log4net.Layout.PatternLayout.html

An alternative (and easier) solution you may want to take a look at is to use the ThreadContext class (formerly implemented as the MDC and NDC classes). You could have something like this inside an HttpModule (before the request arrives at your page) or anywhere else before you log your first message:
ThreadContext.Properties["user"] = username;
And then include the following in your conversionPattern:
%property{user}

Related

Asp.net Sessions Getting Crossed / Mixed Up

Few weeks ago we had one of our customers contacting us saying that sometimes when he creates an activity it gets created under someone else's name!
We did some troubleshooting and couldn't find anything. We asked the user to contact us the next time he was experiencing these issues. He did contact us and we were able to do a gotomeeting with him and see the issue with our own eyes.
It was not only the activities, he was recognized as someone else in the application. He had access to everything that other person should had access to. That was when we realized we are having a session mixed up issue.
A little bit about our code:
Like any other application we have a simple login page that user enter email and password and we authenticate them against our database and if they are valid we call FormsAuthentication.SetAuthCookie() to save current user id in the cookie and we let him in.
BL.User currentUser = BL.User.Authenticate(txtUsername.Text, txtPassword.Text);
if (currentUser != null)
{
this.Session["NumberOfLoginTried"] = "0";
FormsAuthentication.SetAuthCookie(currentUser.UserID.ToString(), chRememberMe.Checked);
Response.Redirect(FormsAuthentication.GetRedirectUrl(currentUser.UserID.ToString(), false));
}
We also use the following piece of code to get logged-in user id (current user) in our application.
public static int GetCurrentUserID()
{
int userID = -1;
int.TryParse(HttpContext.Current.User.Identity.Name, out userID);
return userID;
}
And yes we did our homework and googled around and have seen the following two links:
http://lionsden.co.il/codeden/?p=446
ASP.NET Session Mix-up using StateServer (SCARY!)
We have disabled kernel-mode caching and user-mode caching for .aspx and .ascx files and this is still happening.
P.S- The app is running on Windows 2008 R2 with IIS 7.5. And we are NOT using cookieless session.
We have just had a very similar problem, which occured at random, seemingly un-reproducibly.
The problem turned out to be ASP.NETs Page caching mechanism - in our case the <%# OutputCache tag in particular.
There was a line we had used
<%# OutputCache NoStore="true" Duration="1" %> that basically meant if two users accessed the same page within 1 second of each other they would see the same page (including the logged in username of the other user). So if they refreshed said page, they got the correct information.
In our case, changing said line to
<%# OutputCache NoStore="true" Duration="1" VaryByParam="*" %>, disabling kernel caching in IIS as in this link (http://lionsden.co.il/codeden/?p=446)
and adding the following lines to the Page_Load event of the page in question:
Response.CacheControl = "private";
Response.ExpiresAbsolute = DateTime.Now.AddDays(-1d);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Seems to have solved the problem for us. Hopefully this helps someone else with a similar issue.
We had the same problem and it was caused by the <clientCache/> setting in IIS, which by default fails to add the Cache-Control: private HTTP header. The lack of this header meant that our Forms Authentication cookies were being cached by downstream proxy servers! So when our site got busy, all of a sudden a load of users would suddenly get logged in as the wrong user! Nightmare.
if removing the <%# OutputCache NoStore="true" Duration="1" VaryByParam="*" at all (in all ascx files being in the line from Master to aspx too !!!) prevented from cross-sessions. having only one ascx with outputcache directive loaded, cross-sessions occured.
It did not matter in my case if using sessionstat InProc ore StateServer, if having cookieless or cookie sessions.
We had the same problem at the company I work at. We also realized that it was caused by output caching, which resulted in sending someone else's SessionId to the wrong person.
We have now added the following <caching> element to our web.config.
<configuration>
[...]
<system.webServer>
[...]
<caching enabled="false" enableKernelCache="false">
</caching>
</system.webServer>
[..]
</configuration>
We can't guarantee this will solve it, because the problem is almost impossible to reproduce, but based on our research, this should solve it.
Strangely, the link to a Microsoft article describing this problem that can be found on the internet gives some general page as if the page has been deleted.
But there is this Microsoft article that seems to describe the same issue voor IIS 6:
An ASP.NET page is stored in the HTTP.sys kernel cache in IIS 6.0 when the ASP.NET page generates an HTTP header that contains a Set-Cookie response
Which describes the symptom as:
Consider the following scenario. A Microsoft ASP.NET page contains the <%# OutputCache %> directive. Additionally, the ASP.NET page generates an HTTP header that contains a Set-Cookie response. In this scenario, the ASP.NET page is stored in the HTTP protocol stack (HTTP.sys) kernel cache in Microsoft Internet Information Services (IIS) 6.0. Therefore, multiple users who access the same page may receive identical cookies.
Update
I found this really good article at Microsoft Premier Developer blog that explains a lot:
ASP.Net Session Swapping – Why it happens and what can be done about it?
Because you all disabled kernel-mode caching, I like to point out some other thinks.
1) To correctly use the HttpContext.Current.User.Identity.Name, you first need to verify that your user is logedin by using the User.Identity.IsAuthenticated
2) in this point Session.Add("CurrentUser", currentUser); what are you actual try to save ?
Now I think that is the problem is on cache. The pages are stored somewhere in between your users and the one mix up with the other. Some of the headers that you can use to your page to avoid the cache on the middle proxy computers.
Response.Cache.SetExpires(DateTime.UtcNow.AddYears(-2));
Response.Cache.SetNoStore();
Response.Cache.SetValidUntilExpires(false);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ExpiresAbsolute = DateTime.Now.Subtract(new TimeSpan(1, 0, 0, 0));
Response.Expires = 0;
Response.CacheControl = "no-cache";
Response.AppendHeader("Pragma", "no-cache");
Also I say that if your pages have data that you do not wish to share among your user you need to use Secure HTTPS pages, and set your cookies to be available only on secure pages by adding <httpCookies httpOnlyCookies="true" requireSSL="true" /> on web.config
Also, check if you save your session on SQL server that you scheduled run the clean up routing every 1 minute.
To been able to find some more information I suggest to store some hidden text on the pages, eg the date-time of the rendered, maybe a the last 4 digit of the userID, and what else you may thing that can help you see if the page come from a cache or not.
Since this seems to fall into the extremely arcane problem territory, maybe it's time for a leap.
You could stop using the ASP.NET session to store your identifiers altogether.
You have a bunch of options of where you could stick this information instead. You could choose to encrypt it into the Forms Authentication ticket's UserData property (I've done this before in production, it works great for storing a key(s), csv of roles, or even small json objects). Past the forms auth ticket, you could write the information directly as your own cookie. You could also bypass cookies altogether.
If you choose to bypass the cookies, you're basically entering into similar territory of the cookieless ASP.NET sessions. You have a couple of options, you could make the user identifier be apart of every single url as a query parameter. Another option would be to create a HttpModule that would add a hidden form input into every page response that contains the logged in user's identifier.
If you go down the cookieless path absolutely make sure it's not possible to use your site as HTTP and every single request is HTTPS. Even more especially if you use the query parameter method.
If you checked that output caching is not the problem
There is already on answer from me here, but as it turned out my other solution (disabling the output cache) did not really solve our problem for us.
Since in the question it is stated that caching is turned off, the only other possible (AFAIK) bug that can produce this is what turned out to be the real culprit in our case: we use a private variable in a ActionFilterAttribute.
And because these are cached, storing private/personal data from a user in this way can also lead to session mix-up!
This answer describes what our problem was and how to fix it:
https://stackoverflow.com/a/8937793/1864395
Also, I think it's good to mention that we were able to reproduce the problem by running Apache JMeter with a scenario for several users at the same time. It's a really nice tool (although not really user friendly/intuitive) used for (among other things) stress-testing. It's probably the only way to diagnose session mix-ups!

WIF cross-domain on one IIS site, dynamically setting of realm

We have a lot of domains running on one IIS WebSite/AppPool.
Right now we are in the process of implementing SSO with Windows Identity Foundation.
in web.config the realm has to be set with
<wsFederation passiveRedirectEnabled="true" issuer="http://issuer.com" realm="http://realm.com" requireHttps="false" />
My problem is that the realm is dependent on which domain the user accessed the website on
so what I did is that I set it in an global action filter like this
var module = context.HttpContext.ApplicationInstance.Modules["WSFederationAuthenticationModule"] as WSFederationAuthenticationModule;
module.Realm = "http://" + siteInfo.DomainName;
My question is. When I set the realm like this, is it set per user instance
or application instance.
Scenario.
User A loads the page and the realm get set to domain.a.com.
User B is already logged in on domain.b.com and presses login.
Since user A loaded the page before User B pressed login, user A will hit the STS
with the wrong realm set.
What will happen here?
If this is not the way to set the realm per user instance, is there another way to do it?
I have already solved the problem.
I set PassiveRedirectEnabled to false in web.config
I set up the mvc project to use forms authentication, eventhough I dont.
I do that so that I will get redirected to my login controller with a return url everytime a controller with [Authorize] is run.
In my login controller I do
var module = HttpContext.ApplicationInstance.Modules["WSFederationAuthenticationModule"] as WSFederationAuthenticationModule;
module.PassiveRedirectEnabled = true;
SignInRequestMessage mess = module.CreateSignInRequest("passive", returnUrl, false);
mess.Realm = "http://" + Request.Url.Host.ToLower();
HttpContext.Response.Redirect(mess.WriteQueryString());
This is definitely not really how it should be, for me it feels like Windows Identity Foundation is lagging behind, both in documentation and microsoft technology wise, no examples for MVC.
For other MVC people i recommend them to not use the fedutil wizard, and instead write the code and configuration themself

FormsAuthentication: how to specify different cookie names for specific subdirectories/MVC controllers?

I'm using FormsAuthentication (with cookies) for users authentication, with the default cookie name (".ASPXAUTH").
What I need is a different login system for the "/Admin/" virtual directory (backed by an ASP.NET MVC controller, "AdminController")... as if the "/Admin/" directory was another web application, but without creating another web project inside my solution.
How can I customize, at runtime, the cookie name used by FormsAuthentication? The FormsAuthentication.FormsCookieName is readonly (and static...), and can be customized only once inside the web.config...
Should I create a custom FormsAuthenticationModule?
A controller filter, like the following, could be great:
[CustomFormsAuthenticationCookie("NewCookieName")]
public class AdminController : Controller
{
Trick is the underlying authentication framework really can't handle this--you can't have multiple forms authentication bits running. Easiest solution would be to break the admin bits off into a separate website which would end up living elsewhere and not get caught up in the public site's authentication.

Request.ServerVariables["LOGON_USER"] blank when using ASP.NET wildcard application mapping

When I set up wildcard application maps so that asp.net handles requests (setting executable path to C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll) LOGON_USER is blank when I access a page via site.com/directory/ - site.com/directory/default.aspx returns the correct LOGON_USER value.
How can I fix this without needing to take off application maps?
Simple test:
<%# Request.ServerVariables["LOGON_USER"] %>
Results in DOMAIN\UserName when application maps not set up, but a blank string when they are.
Edit:
The site uses Forms authentication for administration, but I need to do LDAP queries based on the currently logged in user (and to search Active Directory) - anonymous access is turned off and Windows Integrated Authentication turned on.
I've never seen that before, and I almost always wildcard map, but is there any reason you're not using Request.User (or the Page's User helper property) instead of the string-based variable? I'm not sure if that'll help, but it is at least more safe from typoes.

Is there a way to find out if a web service has security?

I've got a web service:
http://machine001/Services/conversionService.asmx
This web service has one method in it called convert(string pInput).
I am trying to find out if there is a way to figure out, without logging into machine001 and without actually calling the convert method, if this web service has security applied.
If I am able to reach http://machine001/Services/conversionService.asmx, see the service description, create the proxy class and instantiate the web service object from any client does that mean there is no security?
Use your browser and go to:
http://machine001/Services/conversionService.asmx?wsdl
And see if the description contains WSE Security declarations. And to you're last paragraph, yes if you can do all of that and you did not do anything else to authenticate, it is unsecured.

Resources