How can I convince Internet Explorer to allow authentication as another user? - c#

Thanks for reading and for your thoughts; this is a hairy problem, so I thought I'd share to see if it is actually a fair challenge for more seasoned developers than ourselves.
We're developing a web application for a corporate Microsoft Active Directory environment, and we use Windows Authentication provided by IIS to authenticate users for single-sign-on, alongside Forms Authentication. I know IIS complains when both are enabled, but it works very well, and every site we've deployed at has had no weird quirks to work around - until now.
The new site has "shared" machines, logged in permanently with a generic account that has read-only access to the applications they need to use. This means that we can't differentiate between users who should have different permissions to the application; we need some way of prompting the user for authentication details.
First try was some serious googling; nobody else in the world seemed to have our problem except for a few misguided souls who had asked questions into the ether and received no response.
After a bit of brainstorming and nutting out the way IIS's authentication works, it seemed that the most straightforward way to approach the problem was to issue a 401 Unauthorized in response to a user known to be a shared account. Initial tests here seemed fruitful, yielding successful changes of username inside the browser, however a prototype at the site did not prompt for credentials, and the browser kept the same account details. We also hit on the IE-specific javascript
document.execCommand("ClearAuthenticationCache")
which, again, worked in the lab but not onsite. Further experiments with IE security settings onsite revealed that the browser would automatically reauthenticate if the webapp site was excluded from the Intranet Zone, regardless of the method used to trick the browser into prompting the user for new account details.
Now we're stuck. We've got workaround options for getting it going on time, but they're definitely not the "right" answers:
require users to log out of the shared account before logging into our app (...yuck)
exclude our webapp from Intranet Zone on all machines
provide a non-SSO login service for users
I'm convinced that there's a canonical way to do this - a known pattern, a common base problem that's already been solved, something like that - and I'm very interested to hear what sort of inventive methods there are to solve this sort of problem, and if anyone else has actually ever experienced anything remotely like it.

We ended up settling on a solution that submits a query to the LDAP directory the server knows about. It means having to accept the user's password, but no other solution was solid enough to run in a production environment.
Hopefully this helps someone. .NET Framework 3.5+ required.
using System.DirectoryServices.AccountManagement;
private static bool IsLdapAuthenticated(string username, string password)
{
PrincipalContext context;
UserPrincipal principal;
try
{
context = new PrincipalContext(ContextType.Domain);
principal = Principal.FindByIdentity(context, IdentityType.SamAccountName, username) as UserPrincipal;
}
catch (Exception ex)
{
// handle server failure / user not found / etc
}
return context.ValidateCredentials(principal.UserPrincipalName, password);
}

Could you not create a page to which the shared accounts are denied access. Then do a redirect to that page, with a return URL encoded in the query string, at any point where you need the user to reauthenticate with a non-shared account? This should trigger the browser to put up the usual login dialog.
After the user reauthenticates, the new page should just redirect back to the return URL in the query string.

Related

ASP.NET CORE 2.1 External Login

Am new to external logins in coding, so have started to get my head around the concepts but am still running into some issues.
Effectively there are 2 sites, both hosted on the same server, and as I load one up (Lets call it MySite), it should redirect to the second Site (Lets call it LogOn), where a user can logon using their credentials. LogOn then redirects back to MySite, with the credentials which can be used to see if they have the credentials to view this site, and shown messages or data dependent on their permissions
However right now, I can get it to redirect to LogOn. I've been experimenting getting it to redirect to Microsoft login and Google also, which redirects fine, but when I set up a WsFederation and add it to my authentication services I'm not quite sure what information to put in the Wtrealm section and metadataaddress options. Hopefully I have given enough information for someone to understand where I'm running into issues.
My code is shown below
services.AddAuthentication("MyCookieAuthenticationScheme")
.AddWsFederation("TestWs", WsOptions =>
{
WsOptions.Wtrealm = "http://SiteOnServer??";
WsOptions.MetadataAddress = "???";
WsOptions.RequireHttpsMetadata = false;
})
Any help would be greatly appreciated, just couldn't find a great deal of documentation taking you through the process.
Thanks in advance!

Going around Single Sign On

Situation:
I have an application where our domain users can register their domain account via an Active Directory interface. They can then unlock their account or reset their password via this application. These two "functionalities", the registration/administration & the unlock/reset, take place on different servers. The whole process of unlocking or resetting uses two-factor authentication via a token app on the mobile phone. The only way to link the token to the app is by scanning a QR code (or typewriting the code) which is shown in the registration process. So far so good.
Now it can happen that we have to reset the mobile phone of a user completely, for example for sending it to repair. Even with a backup, the data in the token app cannot be restored. So far, the only possibility to get the QR code back, is to completely delete the registration in our service and re-register again, thereby generating a new QR code. The process of registration takes a while, as you have to think of several security questions (and answers). This is not very user-friendly.
My task now is to implement a function that allows you to show the QR code. This page can be accessed from the "administration" page each user has for their account. The whole site uses single sign on, which of course makes sense, as you don't want to enter the password every time you want to edit a security question for example.
Problem:
With single sign on, every person can walk up to another user's computer and if it isn't locked, show and scan their QR code, change the security questions so they know the answers, reset the victim's password and log into their account. This has to be prevented of course, as it poses a huge security risk.
My approach was to prompt the user's credentials and check whether the user is the same as via SSO by clearing IE's authentication cache via JavaScript. I use the Windows Security window for the credential validation. This way, I don't have to worry about the AD interface.
To check if it's the same user that logged in, I use a cookie with the username from SSO. Then, in the controller, I check if it's equal to User.Identity.Name. If not, I set the cookie with the username from SSO and the rest of the response:
if (cookie.Value.equals(User.Identity.Name))
{
// do some irrelevant stuff
return View();
}
else
{
Request.Cookies["cookieName"] = User.Identity.Name;
Response.AppendHeader("Connection", "close");
Response.StatusCode = 401;
Response.StatusDescription = "Unauthorized";
Response.Clear();
Response.Write("Unauthorized Access");
Response.End();
}
This works just fine. There is one problem though: If you click on cancel in the Windows Security window, you'll be redirected to a "unauthorized access" page. You can then, however, refresh the page and TADA! Single Sign On logged you in automatically and you or the attacker can therefor see the QR code.
Question:
How can I go around SSO in this specific scenario? So far I have only found solutions saying that I should turn off SSO which would affect the whole application.
Sorry
I know this is a long question, my English might not be clear in some cases and I possibly left out some information unintentionally. Feel free to ask for more information.

New Authentication-System from ASP.NET Core 1.0 (aka ASP.NET 5)

I'm still trying to tinker around with the new ASP.NET Core MVC6 (former vNext). This time I tried to implement my authentication against an Active Directory instead of an Entity-Framework Database.
Since I found so little Documentation, I took the very well done ASP.NET-Template and started to alter it for my needs.
I also use ReSharper to decompile the Parts, since I'm really interested in the Details.
To not go too much into the details, I'm a bit worried I'm doing not the stuff Microsoft intended:
I left the SignInManager as it is and removed the UserManager, since
return await _signInManager.PasswordSignInAsync(model.UserName, model.Password, false, false);
Internally calls the UserManager. Now it gets quite tricky: To provide an alternative store to Entity Framework I need to implement
IUserPasswordStore<ApplicationUser>
Which has around 15 Methods, most of them seem never to get called for my simple Login-Check.
Furthermore, I also need to implement
IRoleStore<IdentityRole>
Which has another 10 Methods, although I don't even want Role-base Security at the moment!
Furthermore, the Verification of the Password does require a Hash, here is the decompiled Method:
protected virtual async Task<PasswordVerificationResult> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user, string password)
{
string hashedPassword = (string) await store.GetPasswordHashAsync(user, this.CancellationToken);
return hashedPassword != null ? this.PasswordHasher.VerifyHashedPassword(user, hashedPassword, password) : PasswordVerificationResult.Failed;
}
According to other SO-Threads, there is no way to get the Password of the AD, so I can't implement that properly as well.
I think the way to go would be to overwrite the SignInManager as well, but this seems to get quite a huge amount of Code, the most not even needed. Also the 2 Store-Interfaces seem to hurt SOLID quite alot, since the interfaces are so big.
Am I dong there something fundamentally wrong?
The reason we don't provide an AD version is security.
By implementing one you either now have an AD open to brute force attacks, because you're not disabling accounts (as that could take admin rights, and no-one should run their web app as a domain admin), or you do disable accounts and now, with a few requests I can lock out your users out of AD, which not only affects your web app, but their desktop computers.
Now add on top that in case of a breach I am now authenticated to your AD and, well, that will make attackers happy, but not your network admins.
As an aside, the password check doesn't need hashed passwords, simply authenticate against AD inside VerifyPasswordAsync and return the appropriate result.
My recommendation is either you look at integrated authentication if you are running in an intranet only environment, or Windows Azure AD if you are wanting to run in the cloud. Both of those are markedly more secure than attempting to leverage AD into an HTML forms based scheme.

Federated Log off keeping user logged on application

I have an application that has integration with ACS / ADFS. I'm having a quite strange behaviour.
I'm logging off the user, Abandoning the session, and redirecting to ACS with Cleanup param.
The user is effectively logged off from ADFS and from ACS but on return to the application, redirect to the Reply parameter, the user is still logged in the application.
The WIF code does not execute again, and no token is present, only IClaimsPrincipal is still present and the Session is present too.
Anyone has heard of this behaviour or something similar?
Thanks
Although this should be executed internally by the framework, I found that clearing the ACS Cookies manually resolved the issue.
WSFederationAuthenticationModule fam = FederatedAuthentication.WSFederationAuthenticationModule;
string famResult = WSFederationAuthenticationModule.GetFederationPassiveSignOutUrl(fam.Issuer, fam.Reply, null);
fam.FederationConfiguration.CookieHandler.Delete();
Issue resolved.
Thanks to everyone.

Accessing user info from a one way trust

I have two domains, MINE and THEIRS. MINE is my local domain and has a one way trust with THEIRS (using LDAPS port 636), so that MINE trusts THEIRS but THEIRS does not trust MINE. I can add users from THEIRS to groups in MINE, and have users from THEIR log into machines and applications on the MINE network. The trust appears to be working properly.
I am writing a little .Net application (not ASP.Net) to test connectivity over the WAN. We have one app that isn't seeing users from THEIRS in groups in MINE. Other apps, like SharePoint, work fine.
I tried using ASP.Net 4 option with System.DirectoryServices.AccountManagement objects, like PrincipalContext, UserPrincipal, GroupPrincipal, etc. Quick code snippet
PrincipalContext domainContext = GetDomainContext(DomainName, ConnectionPort,
UseSpecifiedCredentials, Credentials);
GroupPrincipal theGroup = GroupPrincipal.FindByIdentity(domainContext,
IdentityType.SamAccountName, GroupName);
PrincipalCollection theUsers = theGroup.Members;
var users = from u in theUsers
select u.Name;
return users.ToArray();
It all works GREAT when I connect directly to MINE. The issue comes in with connecting to THEIRS. Either the 1 way trust of the LDAPS traffic is returning the error:
System.DirectoryServices.Protocols.LdapException: The LDAP server is unavailable.
So I switch to .Net 2 variations using DirectoryEntry, DirectorySearcher, etc. This actually works against THEIRS domain.
List<string> userNames = new List<string>();
string searchString = string.Format("(sAMAccountName={0})", GroupName);
SearchResult result = SearchAD(DomainName, ConnectionPort, searchString);
I can connect directly to the THEIRS domain, using some impersonation in the code.
When I query the groups in MINE, I get back the SID for the users from THEIRS, not a user account.
The following users are a member of testGroup:
CN=S-1-5-21-....,CN=ForeignSecurityPrincipals,DC=MINE,DC=local
CN=S-1-5-21-....,CN=ForeignSecurityPrincipals,DC=MINE,DC=local
I tried the impersonation on this as well, running it as a user from THEIRS but no luck.
How can I get user info from THEIRS when the user is in MINE? Do I have to take the above CN/SID and query THEIRS domain? What am I missing in the .Net 4 stuff?
I assume you have your ASP.NET machine running in MINE.
Your System.DirectoryServices.AccountManagement approach should just work if you make sure you use a domain user account from THEIR domain to run the application. In normal one-way trust configuration (unless you are doing selective authentication trust), the domain user account from THEIR should have permissions to read from both MINE and THEIR.
To make sure you use a domain user from THEIR domain, you can simply set the AppPool identity. Of course, you can use impersonation to do it too.

Categories