Using HttpWebRequest with Credential Manager
A little know fact is that the .NET framework will use the stored credentials in the Credential Manager when accessing a network resource if the credentials exist for that particular resource (host). I intend to clear up how this functionality works for the HttpWebRequests (you could extend this to WebService calls as well).
In Internet Explorer, you can access a website that challenges you for credentials by filling in a username and password when the dialog is presented and asks you for authentication. If you check ‘Remember my password’ these credentials will be stored in protected storage (Credential Manager). You can see the stored credentials by running this command: Control Keymgr.dll. These credentials are stored in protected storage in each user’s My folder. You can store these credentials by using control Keymgr.dll, by using the interface available in Internet Explorer or by using C++/C and the Credentials API’s (Such as CredWrite). If you do not check ‘Remember my password’ then these credentials are not stored in Credential Manager and .NET will not be able to use these credentials.
The .NET framework can use the currently logged on user’s credentials to authenticate but if you have a web site that requires different credentials you can use store these credentials in Credential Manager and .NET will use these credentials instead of the currently logged on user credentials. To illustrate this I put together a real simple sample.
The target web server in my sample is jpsandershv2003. I only enabled Windows Integrated Authentication in IIS on that box. I then created a User ID JPSGuest on that IIS machine. Finally I wrote a real simple .NET console application to create an HttpWebRequest and get the response.
HttpWebRequest aReq;
HttpWebResponse aResp;
aReq = WebRequest.Create("https://jpsandershv2003/") as HttpWebRequest;
aReq.UseDefaultCredentials = true;
aResp = aReq.GetResponse() as HttpWebResponse;
aResp.Close();
When you run this code it will first look in the credential manager first for the credentials for attaching to jpsandershv2003 and if found use those, otherwise the Currently Logged on User credentials will be used. In my example since I was logged on as jpsanders to my machine I expect that the credentials used should be my domain credentials jpsanders. I turned on logging and ensured the IIS log was logging the username and sure enough, jpsanders was authenticated in IIS.
My next goal was to see if I could change this default behavior and make it log on using the jpsandershv2003\JPSGuest user credentials. Using the command control keymgr.dll I was able to add the credential as a Windows logon credential:
Now when I ran the console application I saw success in the IIS log! I was logging in now as JPSGuest.
Taking a fiddler (https://fiddlertool.com) trace of Internet Explorer also confirmed I was using the stored credentials and these were NTLM credentials.
Domain: jpsandershv2003
User: JPSGuest
Host: JPSANDERS3
Can I store credentials and have it use Kerberos? In theory yes, simply type domain based credentials in. However I could not test this because I only have one logon to my domain and you cannot save your currently logged on credentials in the Credential Manager.
What about basic and digest? NO, this would be a huge security hole. Can you tell me why (it should be obvious)?
Note that the credentials used are independent of the PORT used. So if I change the server to bind HTTP traffic on port 8089 then change my code to access https://jpsandershv2003:8089 the code will still access the web site using the stored credentials.
To revert back to the interactive user, simply delete the credentials stored for jpsandershv2003.
Further observations
What is the second radio button for (A Web site or program credentials)? This is for programs that are writing against the credentials API directly. Some examples of this are Terminal Server (RDP) and Windows Live credentials. Here are some great links about the APIs:
https://msdn.microsoft.com/en-us/library/aa374789(VS.85).aspx
https://msdn.microsoft.com/en-us/library/aa480470.aspx
NOTE: Because the credentials are stored in the context of the logged on user, this technique will not work for non interactive user accounts such as the service accounts and ASP.NET applications.
Please send me a quick note if you found this Blog helpful!
Comments
Anonymous
September 06, 2009
What about WCF service calls that use message security with the UserName clientCredentialType. It works fine if we use Windows credential type but not UserName. I have entries for all kinds of hosts, both the domain that is being connected to and the certificate identity used to validate the certificate, nothing works. Is there a way to manually talk to the credential manager and extract password data or somehow tell WCF to use it?Anonymous
September 08, 2009
There are API's for CredMan. They are listed here: CredRead: http://msdn.microsoft.com/en-us/library/aa374804(VS.85).aspx Vista changes: http://msdn.microsoft.com/en-us/library/cc540483.aspx Pre Vista used Pstore but I am pretty sure after IE 8, everything will be stored in CredMan.Anonymous
September 08, 2009
The comment has been removedAnonymous
September 10, 2009
Mike, I assume you are talking about Internet Explorer? Is that true? You should note what occasions this works and for which ones it does not to further troubleshoot this.Anonymous
May 27, 2011
JP, First thanks for writing this up you're one of the few who've delved deep into this. Now we tried this for connecting to a SQL Server backend on port 1433. Works great for interactive accounts. For a service account we temporarily turned it into an interactive account, stored the credential, and then disallowed interactive again. (This was before I saw your blog!) It didn't fail in a way that you would think it might. It worked, and you could track the "aliased" login(s) as event ID 4648 in the OS Security log. However at some point 1-3 hours in it suddenly fails and starts getting errors. The errors are quite correct as it reverted to using the actual context instead of the one Credential Manger maps to. No mystery if it had just failed to work at all, but working for a while and then "timing out" without any message or event??? We were using it to connect a Web App to SQL so we've found if you reset IIS then it starts working again, once more for 1-3 hours.Anonymous
May 30, 2011
Mike Hilsher, Strange that it worked for a while... Without debugging this further it would be difficult to determine why it worked for a time. Perhaps it is using a Kerberos ticket and when you reset it is able to get the initial ticket and then after the timeout period the ticket is no longer valid and that is why you start getting the errors. I would expect that time period to be more definitive however instead of such a wide range. If indeed you are using Kerberos you could try enabling Kerberos logging to troubleshoot this further: support.microsoft.com/.../262177 . Don't forget to turn it off after your investigation! -JeffAnonymous
September 19, 2011
Is there anyway to force the WCF service to use the users credentials (jpsanders in your example) instead of the stored credentails (JPSGuest)??