2

I have a .Net assembly (sadly we're stuck in .Net 3.5) which may be used either:

  1. In a .Net application (console app, Windows Forms app) run locally on the system, or

  2. In an IIS web application using Windows authentication

...and need to get the "current user".

Normally for #1 I'd use System.Security.Principal.WindowsIdentity.GetCurrent(), but that won't work for #2 because it'll be the user account the IIS app is running under (the app pool, for instance); and for #2 I'd use Page.User.Identity in the handler for the request, but in this case, I don't have access to that page.

I suppose I could have a method that let an IIS application pass the Page to the assembly, and the User property isn't virtual, so in theory a malicious app couldn't try to feed me the wrong user identity, but it seems pretty dodgy. Is there a better way?

The goal is to identify the user via Windows authentication (either via login [#1] or IIS using Windows auth [#2]). If I'm going about it completely wrong, I'm all ears for how to do that properly. :-)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • How about `System.Web.Httpcontext.Current.User`? – DavidG Nov 05 '15 at 17:31
  • @DavidG: I got *most* of the way there, I found `HttpContext.Current.Request.LogonUserIdentity`. Is that just another path to `HttpContext.Current.User`? – T.J. Crowder Nov 05 '15 at 17:32
  • I don't think so, see this http://stackoverflow.com/questions/2186881/differences-between-page-user-identity-vs-request-logonuseridentity – DavidG Nov 05 '15 at 17:34
  • @DavidG: Fantastic, then on first blush I'd say A) That's my answer, would you post it please? And B) My answer below is wrong (although in my testing, it did give me the authenticated user, not the user the app was running under). I'm going to double-check, but I think that's the case. Massive thanks! – T.J. Crowder Nov 05 '15 at 17:36
  • No matter what, a malicious app using your assembly could always be in control of what user they feed you. For example they could probably use fakes and cause the `System.Security.Principal.WindowsIdentity.GetCurrent()` call to return something different if it wanted. No offense intended but I'd question what it is you're trying to accomplish and see if there isn't another way, for instance forcing the caller to be responsible for providing the identity principal. – lc. Nov 05 '15 at 17:37
  • Why not use `Thread.CurrentPrincipal.Identity`? – haim770 Nov 05 '15 at 17:38
  • @lc.: How would that be less risky? Wouldn't they then just feed me whatever identity they wanted? (This is an area of ignorance for me, you can probably tell.) – T.J. Crowder Nov 05 '15 at 17:39
  • Ha not sure I actually did anything worthy of posting in an answer! – DavidG Nov 05 '15 at 17:40
  • @DavidG: Well, it (may) answer the question. :-) That's worthy. – T.J. Crowder Nov 05 '15 at 17:42
  • @haim770: Would that work? (He says, scurrying off to check.) If so, would it be more, less, or just as susceptible to tampering (see lc's comment)? My guess is "just as," but as I said to him/her, this is an area of ignorance for me. – T.J. Crowder Nov 05 '15 at 17:42
  • @T.J.Crowder, At least in IIS environment, I see no potential risk (unless your entire application is compromised). Once authenticated, IIS automatically (by default) assigns the authenticated user (by `FormsAuthentication` or any other supported methods) to `Thread.CurrentPrincipal`. – haim770 Nov 05 '15 at 17:45
  • @T.J.Crowder, As per your edit, "doing that properly" would probably mean (and I presume you know that) not relying on some external assembly's static property but to explicitly pass the relevant data from the calling method. And that's before mentioning the (undesired) need to reference `System.Web` in a desktop application – haim770 Nov 05 '15 at 17:47
  • @haim770: But if I'm not in control of the calling method, how does that help? The goal here is a public API? Do I just need to accept that I have to delegate responsibility for that to the consumer of the API? (And yes, I wasn't happy with that dependency either.) – T.J. Crowder Nov 05 '15 at 17:50
  • @T.J.Crowder It wouldn't, which was my point: I don't believe you truly have control of what is happening, so I would steer clear of pretending that you do. Of course I have no idea your particular use-case but it seems like a potentially problematic way to implement security if you assume the identity principal you are obtaining is 100% authentic. By having the caller pass you user information it reinforces the fact that it is information obtained from someone else's machine and should be treated with the same grain of salt as any user-provided data. And sanitized/verified accordingly. – lc. Nov 05 '15 at 17:50
  • @lc.: So basically, what I was just saying to haim: I have to accept that responsibility for this has to be delegated to the consumer of the assembly? I can't correctly ensure that identity myself? I find that very, very odd. Windows uses integrated auth all over the place, surely it must be possible for me to ask Windows, reasonably reliably modulo Windows itself having been hacked, who the user is? Is it the IIS aspect that makes that a challenge? (Apologies for the back-and-forth. And thank you for the time you've taken so far.) – T.J. Crowder Nov 05 '15 at 17:55
  • @haim770: Oddly (to me), Thread.CurrentPrincipal.Identity's `name` is blank (not null) and its .IsAuthenticated is `false` when running in a desktop app (not IIS). Did you mean I should use that only in the IIS case? (And thanks for the help so far, much appreciated.) – T.J. Crowder Nov 05 '15 at 17:57
  • And FWIW if it's not mission-critical for you to have an "untampered" identity principal (note this is "tampered" in your assembly's eyes -- in the eyes of the calling code it is functioning as designed!) then it doesn't matter who provides the information anyway. So if it's easier to just delegate responsibility than to reference `System.Web` et al and try to resolve whether you're running as IIS or a real user, or a user impersonating another user, or who knows what. In any public API I've seen, the consumer is always responsible for passing it credentials. – lc. Nov 05 '15 at 17:58
  • @T.J.Crowder, I would say that there *is* a generic way to obtain the current authenticated user (both in IIS and desktop environments) and that would be `Thread.CurrentPrincipal`. But, the problem here (as pointed by @lc.) is the security concerns (in case of desktop application, as I see it). – haim770 Nov 05 '15 at 18:01
  • @haim770: Except [see above](http://stackoverflow.com/questions/33551014/get-windows-or-iis-user-without-access-to-page/33551075#comment54882610_33551014), `Thread.CurrentPrincipal` seems not to be set when running (in my case) a Windows Forms app (with no explicit code in it to set it). – T.J. Crowder Nov 05 '15 at 18:03
  • I think I've probably done an X/Y here. I'll post the real question: Is it possible to get the current identity of A) The logged-in user in the case of a desktop app, and B) The authenticated user in the case of an IIS Windows auth app, from a .Net assembly in a secure way? – T.J. Crowder Nov 05 '15 at 18:05
  • @T.J.Crowder, I just tried it too and you're right. Sorry. FWIW, It does work in IIS. – haim770 Nov 05 '15 at 18:06
  • @haim770: Not a problem, I really appreciate your taking the time. – T.J. Crowder Nov 05 '15 at 18:07
  • I'm not sure how integrated security works deep down in the bowels of the OS and SQL Server might even have some black magic up its sleeve. But since SQL Server is running in a separate process it's in a better position than your assembly would be from a security standpoint. My guess is it uses Windows API calls to get the owner of the calling process by process id. And I think that saying the IIS user is not what you want and you instead want the signed-in user on the page makes it more complicated. Food for thought: if IIS were running as a real user, how would you know which user to choose? – lc. Nov 05 '15 at 18:07
  • @lc.: For my use-case, it's the user that authenticated with the page, not the user running the IIS app. – T.J. Crowder Nov 05 '15 at 18:08
  • @lc., We're not talking about the Application Pool user, but the authenticated user for the Http request (which is also automatically set on the current Thread). – haim770 Nov 05 '15 at 18:09
  • Barring the security implications, and from @haim770's comment you should be able to get it from either the user on the Thread or if there's nothing there,`System.Security.Principal.WindowsIdentity.GetCurrent()`. My only point is to make sure you're not making any assumptions about the validity of what you're getting insofar as you will see whatever user identity the calling code and machine wants you to see. If you really need it to be secure you might need to look at a Windows API call or see if there's a way you can query IIS out-of-process (which it may not let you do for security reasons) – lc. Nov 05 '15 at 18:16
  • Sorry I couldn't be of more help. :( – lc. Nov 05 '15 at 18:17
  • @lc.: Re where to get it: That was my take-away as well. Do you have any actual reference for `System.Threading.Thread.CurrentPrincipal` or `System.Security.Principal.WindowsIdentity` genuinely being hackable? I would expect given MS's .Net reliance, that would be Locked Down Hard. – T.J. Crowder Nov 05 '15 at 18:18
  • @lc.: You've definitely been helpful, and thank you again. – T.J. Crowder Nov 05 '15 at 18:18
  • @haim770: I think `Thread.CurrentPrincipal` combined with `System.Security.Principal.WindowsIdentity` is the answer to the question I asked -- would you post it as an answer, please? The security aspect can be a separate question. – T.J. Crowder Nov 05 '15 at 18:19
  • @T.J.Crowder No I don't have any actual evidence. But to play devil's advocate I can see a reason to *not* lock it down to enable unit testing (using their own fakes/mocking framework even) - imagine tests which rely on getting certain values out of these properties. I would imagine they *are* locked down inter-process though. The problem here lies in that your assembly is not its own process, making it basically be a slave to whatever the calling code wants. As far as I understand things at least. – lc. Nov 05 '15 at 18:25
  • @lc.: FYI, I've [asked the question](http://stackoverflow.com/questions/33563758/is-this-use-of-system-security-principal-windowsidentity-reasonably-secure). – T.J. Crowder Nov 06 '15 at 09:51
  • @T.J.Crowder thanks for letting me know. I'm quite curious of the true answer – lc. Nov 06 '15 at 09:53
  • @lc.: Good instincts: http://stackoverflow.com/a/33565884/157247 – T.J. Crowder Nov 06 '15 at 16:15

1 Answers1

4
  • HttpContext.Current.User would obviously work in IIS environment only.
  • Thread.CurrentPrincipal would contain the actual authentication data in IIS but would remain empty for desktop application. The advantage here is that it will spare you from the need to add a reference to System.Web assembly (in desktop application).
  • System.Security.Principal.WindowsIdentity.GetCurrent() would work for desktop application but in IIS it will return the Application-Pool user, not the authenticated user for the HTTP request.
haim770
  • 48,394
  • 7
  • 105
  • 133
  • Perfect, thank you. You've helped me avoid an avoidable dependency on the `System.Web` assembly in (potentially) a desktop app! – T.J. Crowder Nov 05 '15 at 18:27
  • Sadly, [`Thread.CurrentPrincipal` can trivially be spoofed by the calling code](http://stackoverflow.com/a/33565884/157247). :-| – T.J. Crowder Nov 06 '15 at 16:16