Going around Single Sign On - c#

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.

Related

Asp.Net Identity with 2FA: List of Trusted Browsers

I'm working on a project with Asp.Net MVC 5 and Asp.Net Identity and I'm using two factor authentication. For the login I use:
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
which is the default code that came with the new project. However, I also need the ability for a user to "trust" or "remember" a browser, similar to how banks can indicate if this was the first time you have signed in from a particular browser/pc.
My question is around the RememberBrowser property on the sign in method and what .NET Identity does with this data. I want the list of saved browsers and the ability to revoke access to one/and-or all of them. Is that possible within the Identity framework? Also, can I tell if a browser has been "trusted" before by some type of lookup?
Edit:
Maybe it's a good idea to save the browser info in the database and check on login instead of the cookie? That way it can be shown as a list with the ability to delete it. What I'm looking for is what to save and how to integrate it with the Asp.Net Identity without having a security risk.
Edit 2
Here's an example from a website that is already using this:
Edit 3
Maybe this can be implemented as another step for authentication. So basically we'll have a 3 factor authentication:
First user logs in with user/pass
Then we'll check if the 2FA is enabled and get the code if necessary
We get the user's aser agent and IP and check the database if it's new. Then notify if necessary.
So I'm guessing an new cookie should be added to save browser's info. However, we should be able to invalidate this cookie along with the 2FA cookie.
RememberBrowser sets a cookie that allows the 2FA step to be skipped. There is no central way to track this though it would be easy enough to log, however the results may not be accurate because people can delete cookies manually. There's no way to invalidate it I believe but it doesn't really matter as you can invalidate their session and the user is will be required to login with their password again.
Not sure whether saving browser info adds value as browser info is gonna be same for different users (using same browser and version) unless you save requestor IP as well; and saving requestor IP has too many complications.
How about adding a custom claim to the token if user has set RememberBrowser and then do your logic based on this custom claim? For eg, set a custom claim your_claim_name and set a Guid.NewGuid() to it if RememberBrowser is true. Also save the username, this guid and status flag in database . When a request comes, check whether your custom claim is present, if yes query the table with the custom claim value and username to check whether the entry is still active.
You can either delete the entry or soft delete (set the status) the entry for an user so that when next request comes you can perform your required logic.

How to login to Facebook without a login dialog in C#

I use the some WinForm client sample from Problems when migrating from Facebook SDK v5.4 to alpha v6
to see how I can login into Facebook.
My question: Is it possible to login into Facebook without ANY dialog?
private void btnLogin_Click(object sender, EventArgs e)
{
// open the Facebook Login Dialog and ask for user permissions.
var fbLoginDlg = new FacebookLoginDialog(AppId, ExtendedPermissions);
fbLoginDlg.ShowDialog();
// The user has taken action, either allowed/denied or cancelled the authorization,
// which can be known by looking at the dialogs FacebookOAuthResult property.
// Depending on the result take appropriate actions.
TakeLoggedInAction(fbLoginDlg.FacebookOAuthResult);
}
you are not allowed to store user login/password in the database and use it for "automatic" login - as per Facebook policy:
I. Features and Functionality
...
3 - You must not include functionality that proxies, requests or collects Facebook usernames or passwords.
therefore the answer is - NO, you cannot do that, and you are not allowed to do that.
This is just an idea and it wouldn't fit in the comment box.
So you'll need the user to initially input their data. Storing it in a database or file somewhere on the server so that it is accessable. I'd say the best way to go about it is for first time visitors to have them log in and then prompt if they would like to save crednetials or whatever. If yes then it would take their username and password as well as say their windows login and encrypt them and store them in a database. Now, when a user uses the WinForm, it would need to first check their windows login using windows authentication. After finding their login name you would go through your data and find their login then pull their username and password, automatically populate the field and log in.
Try implementing something similar to that.

Why does a new IE6 window make me log in again with Forms Authentication?

I use the forms authentication in my asp.net application and I protect all the pages using:
deny user=*
And when a user logs in, I use:
FormsAuthentication.RedirectFromLoginPage(UserName.Text, false);
Now if I use IE6 when I open a window and login it works, but then if I open a new window ,I have to login again. It seems that a new IE6 window will open a new session or cookie (I am not sure) - how can I avoid this?
There are multiple approaches. I believe the impact for the user should be as little as possible.
You could store the last logged in, or last database action timestamp in your database. Doing so, you can verify if the last action the user had was within a number of minutes. Additionally, you could store the username ( not password ) in a cookie on the client. Next time the client opens a new session, you know the username, verify on database that the last database activity was within a number of minutes, and bypass the login obligation.
Second approach involves changing startup parameters of the clients browser, so that new windows share the session. I do not know whether this is available on all browers ( and versions ) and if you are capable of doing this.
redesign your web application so new windows don't need to be opened, unless they are from within the opened window. If they are opened from an existing, logged in window, you can send a hash key in query string, which bypasses the login procedure.
These are just a few possibilities which come to mind at this point.. If you should require more possibilities, just ask :-)
Do you mean deny user="?"? * means all users, while ? means anonymous users.

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

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.

reusable "save credentials" dialog (like IE's or Vista's) in .NET or Win32

Lately I've been working in an office with a wireless network which uses an annoying authentication scheme: every few hours, you need to open up a browser and type a username/password into an authentication web page, or you lose network access. (When the time expires, your next browser request will redirect to the auth page, and if your creds pass muster, then you'll be redirected back to the page you were trying to get to originally).
This kind of annoyance may be OK for an airport or coffee shop wireless, but in an office it's infuriating-- especially if you're working with network services (e.g. SVN, email) which suddenly stop working every few hours unless you bring up a browser.
So I have written a tiny C# console app which will log in for me by sending an HTTP request to the login form with my credentials.
This is obviously insecure-- my password is sitting inside my source code for all to see. I'd prefer to be able to save my credentials using the same mechanism that IE, for example, uses to save and re-fill passwords in web forms.
Ideally, I'd like a re-usable component for entering, saving, and retrieving credentials(including the UI with an optional "save creds" checkbox) so that my app can simply do something like this (in pseudocode):
// retrieve any saved credentials from some secure place
Credentials creds = GetCreds(some parameters go here);
// if none stored, then show the user an "enter and optionally save credentials" dialog
if (creds == null)
creds = GetCredsDialog(some parameters go here);
// POST to the authentication page
if (creds != null)
{
string authUrl = "https://somehost/login/";
string postDataPattern = "post data pattern here";
// use SecureString here instead?
string postData = string.Format (postDataPattern, HttpUtility.HtmlEncode(creds.Username), HttpUtility.HtmlEncode(creds.Password));
WebClient wc = new WebClient();
string html = wc.UploadString (authUrl, "POST", postData);
// TODO: if html indicates login failure, clear stored credentials
// and ask for new creds. then retry.
}
Essentially I want to shift the burden of securely storing creds from my app to Windows, under the assumption that the Windows guys will be better at this than I will be. :-)
I'm not looking for iron-clad security here, just something comparable to what IE is using to secure my other stored passwords for other websites. I just don't want to keep plain text passwords in my code!
Of course, the right solution here is to work with the IT department to get them to get a real authentication scheme for Wireless, but in the meantime I'm on my own.
A .NET solution would be preferable, but a Win32 solution would be OK too-- I could simply port the app to C++ without much trouble.
To store the credentials, use the ProtectedData class in System.Security.dll.
By passing DataProtectionScope.CurrentUser, no other user will be able to decrypt the data.
EDIT: For the dialog, you can use the CredUIPromptForCredentials API function
See here for a .Net wrapper.

Categories