ASP.NET processing unauthenticated file request in site with Windows Authentication: How? - c#

I have a curious problem with a legacy ASP.NET web application using Windows Authentication. A particular page is crashing, and an inspection of the page and the site logs indicates the page is crashing because the request is not properly authenticated - no Windows identity is being requested by IIS or supplied by IE 11.
The page has a curious path; it took a few minutes to decode how it was originally assembled. The initial request is not for a specific page, but is merely a folder-only URL that is routed to Default.aspx. The handler checks the query string and redirects to specific pages accordingly.
The initial request to the site is authenticated, as evidenced by the IIS site logs. The page to which the request is redirected (Response.Redirect) does not authenticate. The absence of the Windows authentication challenge leaves the site with no automatic identity to the targeted page, leading to the page crash (code depending on the identity fails). The sequence goes this way:
Original URL: /sitename/folder/?parameter1=value&parameter2=value
IIS issues the authentication challenge, and the authenticated user is shown in the logs - eg, domain\user
The request is then handled by folder/Default.aspx (default page as defined in IIS)
Default.aspx.cs inspects the query string, and routes the request to (eg) OtherPage.aspx via Response.Redirect.
OtherPage.aspx is requested, and the request is logged - with no authentication, and no challenge
OtherPage.aspx.cs crashes (no user credential)
I am trying to theorize how or why ASP.NET is even permitting the unauthenticated file request. I have tried to reproduce the behavior in a test environment, and have been unable to do so. I have suspected that "Automatic logon in Intranet zone" might have been disabled, or that stored local credentials may be present but somehow causing a conflict, but neither of those scenarios panned out. The former did result in a failed authentication attempt and a proper 401 response from the server (the target page was not fired in a test environment).

Further research into this question has led to a solution if not a 100% dissection of the cause.
The users experiencing the problem were accessing the target site via a link in an email message. The link, for some unknown reason, inhibited the credential exchange between IE and IIS until the site URL was placed in the "Local Intranet" sites list of IE. This allowed the "Automatic logon in Intranet sites only" option to apply which, in turn, allowed the authentication to work.
The reason this is not a "100% dissection" is because these users were accessing the site previously, wherein authentication worked when the site was accessed conventionally. Exactly how the email message link inhibited the authentication exchange is not known. At the moment, I theorize that some security setting inhibits authentication when originating from an email link unless the specific site URL is explicitly qualified as a trusted or Intranet site.
Thanks for your consideration.

Related

Windows Authentication for Blazor Server app - login popup

I am building a Blazor Server app using .NET 6.0.11 and deploying using Http.Sys . The server and clients are all on the same Windows domain. I'd like to authorize users based on their domain login, instead of building a username/password database and infrastructure specific to this app.
The code in Program.cs to enable Windows Authentication, based on code from the official documentation:
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
builder.Services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
builder.WebHost.UseHttpSys(options =>
{
options.Authentication.Schemes =
AuthenticationSchemes.NTLM | AuthenticationSchemes.Negotiate;
options.Authentication.AllowAnonymous = false;
});
}
// ...
app.UseAuthentication();
app.UseAuthorization();
I view the authentication state using the first code sample from ASP.NET Core Blazor authentication and authorization.
If I launch the application on http://localhost:55555 then the page loads instantly and it shows my domain and username successfully.
However, if I launch the application as http://mycomputername.our.domain:55555, and access it via Chrome or Edge on the same machine, then a popup appears asking for username and password:
Note: I had to run a netsh http add urlacl url=.... one time only; the command was suggested by an exception message generated on first run after changing the launch URL.
Entering my domain login username and password is accepted, and the previous code sample does show my username successfully. I also noticed the following behaviour:
setting AllowAnonymous = true; means it will not prompt at all for the username/password; the page just proceeds with the user not authenticated.
If the username/password is not entered correctly (for a user on the domain) then HTTP error 401 is generated instantly, it never tries to execute the Blazor default error page for example.
I hope the authentication is being done between the client and the domain controller, not sending the user's password over the HTTP connection!
I am presuming the above behaviour will be the same for other domain uses on different machines accessing this server, although have not tested that yet.
My questions:
is it possible to skip the username/password popup , and just get the domain user that is already logged in on the client machine and doing the access?
(If not) would deploying to IIS instead of Http.Sys change anything?
There seem to be Blazor-based solutions discussed on this SO thread , but I can't see how to use them because the popup always appears as soon as any page is attempted , before any page is rendered; and if login fails, HTTP 401 error is generated with none of the Blazor pages being executed.
Footnote: I was using HTTP.sys instead of Kestrel due to documentation indicating that Kestrel did not support Windows Authentication; however it is working using Kestrel for me now, along with the information from the Accepted answer -- not sure what the story is there.
This is a client-side issue.
If I launch the application on http://localhost:55555 then the page loads instantly and it shows my domain and username successfully.
However, if I launch the application as http://mycomputername.our.domain:55555, and access it via Chrome or Edge on the same machine, then a popup appears asking for username and password:
That's because the browser recognizes localhost as a (somewhat trustworthy) server within your intranet, but considers mycomputername.our.domain to be a (potentially hostile) Internet service. For security reasons, Chrome and Edge only use your Windows credentials with servers within your own intranet by default.
To determine which group an URL belongs to, Chrome, Internet Explorer and Edge use Windows's own "Intranet zone" settings. To add your URL,
search for "Internet options" in the Windows Start Menu, then
navigate to Security/Local intranet/Sites/Advanced.
If you want to test with a non-Chromium based browser, here's how to configure Firefox. Firefox manages its own list of URLs where Windows authentication is allowed:
How to configure Firefox for NTLM SSO (Single-Sign-On)?
I hope the authentication is being done between the client and the domain controller, not sending the user's password over the HTTP connection!
If the client and the server agree to use Kerberos, sure: The client communicates with the domain controller to authenticate and to get a service ticket and then uses that ticket to access your service.
If they don't agree, they will use NTLM. In this case, authentication happens between the client and your server. However, NTLM won't send your password in plain text either, but rather uses it as part of a challenge-response mechanism.
is it possible to skip the username/password popup , and just get the
domain user that is already logged in on the client machine and doing
the access?
IMHO, this is not possible with "windows authentication", because the popup dialog that you see is a special browser feature to enable the windows authentication.This one created in order to support internal application with domain authentication. I don't know of any way to access that browser behavior.
(If not) would deploying to IIS instead of Http.Sys change anything?
Haven't done a deployment with Http.Sys, but according to the documentation it says that Http.Sys supports windows authentication. The deployment would be different since for Http.Sys you need to write the configuration in code, unlike the IIS.

Logout of MVC4 application

I'm continuing someone else's development AND I don't know much about current practices for authentication in MVC applications.
The only things that are set about authentication are in the application's IIS configuration:
.NET Authrization Rules = Allow, All Users
Authentication:
Anonymous Authentication: Enabled
ASP .NET Impersonation: Disabled
Windows Authentication: Enabled
With this configuration, on the server, the browser asks me for a login/password. I enter my network login.
Then I can get identify the user with Request.RequestContext.HttpContext.User.Identity...
On local computer where I login using the same login/password: no user is logged in the application (Request.RequestContext.HttpContext.User.Identity.Name == ""). If I disable Anonymous Authentication, the browser simply keeps re-asking for loginpassword infinitely.
My first problem is that I would like to be able to log out on server.
From scarse info I got here and there I have already tried:
FormsAuthentication.SignOut(); --> does nothing
WebMatrix.WebData.WebSecurity.Logout(); --> Exception, tries to access a database (I got this from one of the VS2012 templates but I didn't think it would apply to my context).
if(this.Request.RequestContext.HttpContext.Session != null)
this.Request.RequestContext.HttpContext.Session.Clear(); --> Session is null, so this does nothing.
So, how can I log out in order to relog as a different user ?
(I would also like to be able to identify the user on local computer, but I think that should be asked in another topic.)
Removed FormsAuth logout method as the question is for Windows Auth... doh!
Update:
To get the logged in user name try the IPrincipal Controller.User:
User.Identity.Name
Doh my bad your using Windows Authentication...
In which case it's the browser that is caching the credentials not the server / IIS so clearing the session won't achieve anything.
Taken from here:
"The user credentials are being cached by the client browser, not by IIS. To
force the client user to enter credentials again, you would need to send an
appropriate 401 status message in response to the next client request.
However, doing this would run counter to very legitimate user expectations
of how Windows authentication is supposed to work, so you may want to
reconsider. When Windows user credentials have already been accepted by a
server (either via a login dialog or automatic submission under IE
configuration for the target site or zone), a 401 is only expected if a
requested resource cannot be accessed under the previously supplied
credentials. When you send a 401 after any credentials have been previously
accepted, the user should expect that they need to use different credentials
from their initial login. If you're expecting the same credentials, then
user confusion should be anticipated.
All in all, if you really want to force a new login, perhaps a different
authentication mode might be more appropriate."
For an IE only workaround see this SO post.

URL rewrite in ASP.NET application

How do I redirect url based on register client in c# .net or asp.net 4.0. For example if client registers as "client1" and our website is www.mycompany.com for every page client proceeds should get www.client1.mycompany.com.
More detailed example:
For example another client created is Client2. The pages i have created in general is like
"www.mycompany.com/product.aspx"
"www.mycompany.com/categories.aspx" should be shown as
"www.client2.mycompany.com/product.aspx" and
"www.client2.mycompany.com/categories.aspx" respectively
I have searched on web and found for static pages or using Gloabal.asax during startup of application but haven't found any thing after user logged in.
I have done something similar before in a few sites and there are a couple methods you could use. Assuming that you have a url setup so that all subdomains ( *.url.com) will send any user to your server and you have IIS setup to handle them all (i.e. no host header required, just IP) in the same site you can use one of the following methods:
After login simply send the user to that url. Since .Net won’t care the url the server knows how to render it, then it should be that simple. This assumes all your navigation uses relative paths and you must enable cookie sharing for that domain. This is required if the cookie for login was give on 1.url.com and you send them to 2.url.com You can share cookies in the same domain, requires a little work, but can be done.
Create a generic login page that does a web service request back to the server to see if the user can login. If he or she can have it send back to the browser a command, along with the correct url, that tell the clients browser to post directly to that sites login page (send username, password). This will login them into their site and assign the cookies correctly all from one simple login page. You could even make an external login page that only exists for this purpose. In the end all the generic page did was see if they could login and the sent their credentials to the correct page that did the login. I recommend this be done in a post with ssl for security reasons.
I hope that makes since.
There's a project called UrlRewritingNet which I use - it's pretty old but the source is available so you could recompile it for 4.0.
Link is at http://urlrewriting.net/149/en/home.html

Forcing a custom HTTP 401 unauthorized page in ASP.NET

I've written a web application for internal use at work (not for the wider internet) that makes use of Windows authentication - ASP.NET interrogating Windows for the current set of credentials. An authentication method called from the Page_PreInit of protected pages throws a 401 error if the username is not found in an AD group I've set up.
I implemented Earlz' CustomErrorsFixer from Throwing an HttpException always sends back HTTP 500 error? as I too was only getting 500s back. Now my custom error pages are working, which is great.
Developing locally in Visual Studio development server, I've found that if I do not have access, I just go straight to the 401 error page. However! When I publish the site to an IIS server, if the user doesn't have access they get a Windows username/password prompt (the ugly, small one in XP). This is actually quite handy because it gives people who are logged onto a different domain a chance to enter the correct credentials.
At this stage, when the password prompt comes up, if the user hits Cancel or Escape, they go to the custom 401 page, which tells them how to go about requesting access. IF however they try to enter a username and password, which defaults to the wrong domain and are therefore incorrect credentials, they are shown the default IIS 401 page, which I'm very keen to avoid. Third scenario - if they enter CORRECT credentials, they are asked 3 times, and then shown the custom error page.
So, users see the custom 401 page if they are "authenticated", and the standard IIS one if they are not.
However, I've been finding that most people when prompted with a username/password box just enter the username/password without domain - which ends up being incorrect and therefore sending them to the non-custom IIS 401 page. Does anyone know how I can solve this? It's extremely annoying, because people need to see the custom 401 page in order to see which group they need to request access to!
In case it matters, the browser we all use is IE8 on XP or IE9 on Windows 7. Please let me know if I should post any code up.
IIS7 intercepts the 401 along with a few other HTTP status code by default.
Try this:
<configuration>
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>

Unsecured & secured HTTP

I have an application which uses 2 web sites (so I guess that is 2 applications...). The user logs on via a https website and is then diverted to a unsecure http website application on successful logon (using forms authentication). I (and others) have started getting the message
"The current web page is trying to open a site in your Trusted sites list. Do you want to allow this?"
when the user is redirected.
Is there a way to stop this in the server configuration or in the code ?
Thanks
If the user is accountable for any actions performed on the "unsecured site", it should not be unsecured. It's not safe to authenticate a user on HTTPS, then let them perform actions using that authentication over HTTP.
If someone is not worried about a man-in-the-middle, it doesn't make sense for them to use HTTPS at all. On the other hand, if a man-in-the-middle attack is a possibility (and in general I assume it is) then sending the session identifier cookie (or other credential) obtained via secure login over an insecure channel allows an attacker to steal it and forge requests to the service.
I think what you are doing at login is to post the login information from the secured page to the non-secured page, which in turn pops up that message.
What you could have done is for the secured login to post to a secured page, then redirect from there to the non-secured page. That should remove the message.
That message appears to be IE's trusted sites warning. There is no way to control it from a remote server, nor should there be as it would be a security risk.

Categories