Cookie decryption issue between 2 domains - asp.net

I am working on some aspnet C# 3.5 webapp. We use IIS7 on our test server.
This webapp communicates with SharePoint 2007 3.0 SP2 by webservices. We have a machineKey in both web.config (the one of the webapp and the one of the Sharepoint site). The two sites are on two different server.
This communication is working fine.
We have also some home-made SSO: the use in the webapp clicks on a link then SharePoint opens in a new window and the user is already logged in.
This SSO is managed by a "shared" cookie in both apps (same name, same domain).
<authentication mode="Forms">
<forms domain="crm.local"
enableCrossAppRedirects="true"
loginUrl="*** url to login page ***"
name=".CrmAuth"
protection="All"
slidingExpiration="true" timeout="200">
</forms>
In SharePoint, we developed a RedirectModule which decrypts the auth ticket from the cookie and log the user.
HttpCookie authCookie = app.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
FormsIdentity identity = new FormsIdentity(authTicket);
GenericPrincipal principal = new GenericPrincipal(identity, null);
}
But, all of a sudden, this SSO has broken. When SharePoint tries to decrypt the cookie, an exception is thrown:
[HttpException (0x80004005): unable to validate data]
System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo, Boolean signData) +1008
System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo) +91
System.Web.Security.FormsAuthentication.Decrypt(String encryptedTicket) +246
Nothing has changed on both server. What can be the cause?
What can I do to fix it?

It was due to a change in some reverse proxy box on local network...

Related

ASP.NET / IIS loading X509Certificate.LoadCertificateFromFile

I am trying to load a certificate in a class that is hosted in IIS.
The code that i used is:
X509Certificate2 privateCertificate = new X509Certificate2(#"C:\Temp\file.pfx", "mycertpass");
Later on in my code when privateCertificate is used it causes a problem on the production environment. This problem does not occur on the local environment, the exception is as follows:
Exception information:
Exception type: CryptographicException
Exception message: An internal error occurred.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromFile(String fileName, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags)
at Medapp.PaymentResponse.CreateRepository()
at Medapp.PaymentResponse.Page_Load(Object sender, EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
I thought it might be a permission on folder problem however i have added "everyone" with full access temporarily and this did not solve the problem either.
Any ideas would be appreciated.
Make sure C:\Temp\file.pfx path is accesible. probably you want to test Server.MapPath("~/folder/" + filename).
Also check out the identity of your app pool and make sure that the Load user profile option is turned on, otherwise the crypto susbsystem won't work.
Try specifying X509KeyStorageFlags
var certificate = new X509Certificate2(KeyFilePath, KeyFilePassword,
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);

Impersonation Issue on IIS7 - Entity Framework Trusted_Connection ASP.NET

I'm developing an ASP.NET Application that connects to a Database (on another machine). When I try to connect from my machine there are no problem, when I deploy it on the destination environment, EF returns an exception:
The underlying provider failed on Open.System.Data.SqlClient.SqlException (0x80131904): Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'. at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) at System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK) at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, SqlConnection owningObject) at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, TimeoutTimer timeout) at
My configuration is:
<authentication mode="Windows" />
<customErrors mode="Off"></customErrors>
<identity impersonate="true" />
<security>
<authentication>
<windowsAuthentication useKernelMode="false">
<extendedProtection tokenChecking="None" />
</windowsAuthentication>
</authentication>
</security>
And my connection string is:
<add name="pmgbicopEntities" connectionString="metadata=res://*/copDB.csdl|res://*/copDB.ssdl|res://*/copDB.msl;provider=System.Data.SqlClient;provider connection string="Data Source=[NAME]\devde;Initial Catalog=[NAME];Integrated Security=True;Trusted_Connection=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" />
If I remove the database instruction the this.Page.User is my current Account (and this is right!), but when I open a new connection via EF I become "Anonymous"
I tried with impersonation like:
WindowsIdentity winId = (WindowsIdentity)HttpContext.Current.User.Identity;
WindowsImpersonationContext ctx = null;
try
{
ctx = winId.Impersonate();
//EF SOMETHING
}
catch (Exception exx)
{
}
finally
{
if (ctx != null)
ctx.Undo();
}
But I get the same exception "Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'."
I tried to change the application pool user, tried to impersonate manually but without any success.
The IIS configuration has some kerberos stuff that I tried to Enable and Disable but nothing change.
Do you have any suggestion? Do I have to enable something like "trust" between machines? For my development machine from "fake IIS" I have no problem but my user is enabled on the DB.
Thank you in advance.
What is your end goal here: pass through the user's credentials to SQL or use a predetermined account to connect to SQL?
Here are the settings I've had to configure Kerberos to work (which allows you to use the user's account permissions):
IIS Settings:
-Physical Path Credentials: Application User (pass-through authentication)
-Physical Path Credential Logon Type: Clear Text
-Application Pool ID: Domain\ServiceAccount
-Integrated Windows Authentication: Enabled
-Windows Authentication Providers: Negotiate
-useAppPoolCredentials (found in Configuration Editor): True
SPN created for the ServiceAccount:
SETSPN -L Domain\ServiceAccount
HTTP/Our.WebSiteURL.com
If you want don't want to use a domain service account replace that with Domain\MachineAccount in both the SPN and Application Pool.

Proper creation of a cross-domain forms authentication cookie

I'm just creating a simple test between two server. Basically if a user has already authenticated I want to be able to pass them between applications. I changed the keys to hide them
I have three questions:
What is the proper way to validate the cookie across domain application. For example, when the user lands at successpage.aspx what should I be checking for?
Is the below code valid for creating a cross domain authentication cookie?
Do I have my web.config setup properly?
My code:
if (authenticated == true)
{
//FormsAuthentication.SetAuthCookie(userName, false);
bool IsPersistent = true;
DateTime expirationDate = new DateTime();
if (IsPersistent)
expirationDate = DateTime.Now.AddYears(1);
else
expirationDate = DateTime.Now.AddMinutes(300);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
userAuthName,
DateTime.Now,
expirationDate,
IsPersistent,
userAuthName,
FormsAuthentication.FormsCookiePath);
string eth = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, eth);
if (IsPersistent)
cookie.Expires = ticket.Expiration;
cookie.Domain = ".myDomain.com";
Response.SetCookie(cookie);
Response.Cookies.Add(cookie);
Response.Redirect("successpage.aspx");
}
My config:
<authentication mode="Forms">
<forms loginUrl="~/Default.aspx" timeout="2880" name=".AUTHCOOKIE" domain="myDomain.com" cookieless="UseCookies" enableCrossAppRedirects="true"/>
</authentication>
<customErrors mode="Off" defaultRedirect="failure.aspx" />
<machineKey decryptionKey="#" validationKey="*" validation="SHA1" decryption="AES"/>
What is the proper way to validate the cookie across domain application.
For example, when the user lands at successpage.aspx what should I be checking for ?
There shouldn't be anything to check. Forms authentication mechanism will retrieve the ticket from the cookie, check if it is valid. If not present, or invalid, user will redirected to ~/Default.aspx .
This will work provided your cookie matches the configuration of your web.config
Is the below code valid for creating a cross domain authentication cookie ?
I think you shouldn't try to override the settings of your web.config by manually handling the cookie. I think there are better ways for handling cookie persistence (see below for web.config) and you are just implementing a part of the Forms authentication API (loosing web.config for SSL for example )
here, your manual cookie is not HttpOnly : you could for example be subject to cookie theft through XSS
FormsAuthentication has its own way of handling the cookie (see the TimeOut attribute description in http://msdn.microsoft.com/en-us/library/1d3t3c61%28v=vs.80%29.aspx) Your cookie persistence mechanism will be overwritten by this automatic behavior
Your code should just be :
if (authenticated)
{
bool isPersistent = whateverIwant;
FormsAuthentication.SetAuthCookie(userName, isPersistent );
Response.Redirect("successpage.aspx");
}
Do I have my web.config setup properly?
It should be ok for the domain attribute, as long as you want to share authentication among direct subdomains of mydomain.com (it won't work for x.y.mydomain.com), and mydomain.com is not in the public suffix list ( http://publicsuffix.org/list/ )
I would change the timeout and slidingExpiration attributes to :
<forms loginUrl="~/Default.aspx" timeout="525600" slidingExpiration="false" name=".AUTHCOOKIE" domain="myDomain.com" cookieless="UseCookies" enableCrossAppRedirects="true"/>
I guess it is a good way to handle the choice between one year persistent cookies and session cookies. See https://stackoverflow.com/a/3748723/1236044 for more info

"Unable to validate data" using FormsAuthentication between different web apps

I have two .NET web applications running on the same server - sentinel (hosted at https://sentinel.mydomain.com/) and fortknox (at http://www.mydomain.com/fortknox)
Sentinel is an authentication 'portal'. FortKnox is a 'proof of concept' app that uses forms authentication but has the loginUrl set to https://sentinel.mydomain.com/login (along with a special Application_EndRequest handler to qualify the ReturnUrl). Sentinel is written in .NET 4.0 using MVC 4 and Razor; FortKnox is ASP.NET MVC 2 using .NET 2.0.
I'm using ASP.NET FormsAuthentication with the cookie domain set to .mydomain.com so that cookies set by sentinel.mydomain.com will be sent with requests to www.mydomain.com, and vice versa. The cookie part is working perfectly - both applications are getting the same .ASPXAUTH encrypted forms ticket.
The problem is that, on our production servers, fortknox can't decrypt cookies created by sentinel - even though they have identical machine keys. Even when both apps are running on the same physical box, it doesn't work.
A user hits fortknox, they're redirected to sentinel, they log in, the cookie is set, they're redirected back to fortknox, and then I get "Unable to validate data":
Exception: Unable to validate data.
at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo, Boolean signData)
at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo)
at System.Web.Security.FormsAuthentication.Decrypt(String encryptedTicket)
at FortKnox.Web.MvcApplication.Application_BeginRequest()
The machine keys are identical - I've gone as far as including this chunk of (nasty!) code in the mark-up of each page:
try {
var cookie = Request.Cookies[".ASPXAUTH"].Value;
Response.Write("Cookie: " + cookie + Environment.NewLine);
var ticket = FormsAuthentication.Decrypt(cookie);
Response.Write("Ticket name: " + ticket.Name + Environment.NewLine);
} catch (Exception x) {
Response.Write("Exception: " + x.Message + Environment.NewLine);
Response.Write(x.StackTrace);
}
Response.Write("<hr /></pre>");
var machineConfigMachineKey = (MachineKeySection)WebConfigurationManager.OpenMachineConfiguration().SectionGroups["system.web"].Sections["machineKey"];
var webConfigMachineKey = (MachineKeySection)WebConfigurationManager.OpenWebConfiguration("").SectionGroups["system.web"].Sections["machineKey"];
Response.Write("<pre>");
Response.Write("<b>machine.config decrypt: </b>" + machineConfigMachineKey.DecryptionKey + "<br />");
Response.Write("<b>web.config decrypt: </b>" + webConfigMachineKey.DecryptionKey + "<br />");
Response.Write("<br />");
Response.Write("<b>machine.config validate: </b>" + machineConfigMachineKey.ValidationKey + "<br />");
Response.Write("<b>web.config validate: </b>" + webConfigMachineKey.ValidationKey + "<br />");
Response.Write("</pre>");
Response.Write("<hr />");
and verified that the machine keys being used at runtime are exactly the same.
What's especially frustrating is that this has been working on our development and staging servers, and has only failed in production. The only difference between the servers is that the production boxes have Windows Updates installed frequently whilst our dev/staging boxes are potentially missing some updates; they're otherwise identical (cloned from the same image and created using the same setup scripts)
So... same server; same machine key. ASP.NET 4 sets a FormsAuthentication cookie. ASP.NET 2 app can't decrypt it. Bug only happening on certain servers; on others, it's working. At this point, I'm completely stuck... any ideas?
EDIT: Live server has been brought right up to the latest patch level. I have tried applying
<add key="aspnet:UseLegacyEncryption" value="true" />
as both true AND false, to both the login app and the fortknox app. Still no luck...
Any chance it has something to do with the old 2010 padding oracle security patch - http://weblogs.asp.net/scottgu/archive/2010/09/28/asp-net-security-update-now-available.aspx? Try setting
<add key="aspnet:UseLegacyEncryption" value="true" />
to force the patched servers to act like they used to before the patch?
(or, you know... patch your servers. Your choice.)
Did you try all the following keys? http://support.microsoft.com/kb/2425938
<add key="aspnet:UseLegacyEncryption" value="true" />
<add key="aspnet:UseLegacyMachineKeyEncryption" value="true" />
<add key="aspnet:ScriptResourceAllowNonJsFiles" value="true" />

ASP.NET MVC 3 AntiForgeryToken and custom MachineKey configuration

We've run into some issues with ASP.NET MVC 3 AntiForgeryToken HTML helper when having a custom configured MachineKey in Web.Config. The error is easy to reproduce if you change your MachineKey to the following (taken from Microsoft HowTo-guide on how to configure machine key).
<machineKey
validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7
AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
validation="SHA1"
decryption="AES"/>
The exception thrown by AntiForgeryToken is as follow:
[IndexOutOfRangeException: Index was outside the bounds of the array.]
System.Web.Configuration.MachineKeySection.SetInnerOuterKeys(Byte[] validationKey, Byte[]& inner, Byte[]& outer) +11499173
System.Web.Configuration.MachineKeySection.ConfigureEncryptionObject() +228
System.Web.Configuration.MachineKeySection.EnsureConfig() +287
System.Web.Configuration.MachineKeySection.HashData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length) +46
System.Web.Security.MachineKey.Encode(Byte[] data, MachineKeyProtection protectionOption) +58
System.Web.Helpers.AntiForgeryDataSerializer.<.ctor>b__2(Byte[] bytes) +13
System.Web.Helpers.AntiForgeryDataSerializer.Serialize(AntiForgeryData token) +365
System.Web.Helpers.AntiForgeryWorker.GetAntiForgeryTokenAndSetCookie(HttpContextBase httpContext, String salt, String domain, String path) +326
System.Web.Helpers.AntiForgeryWorker.GetHtml(HttpContextBase httpContext, String salt, String domain, String path) +28
System.Web.Helpers.AntiForgery.GetHtml(HttpContextBase httpContext, String salt, String domain, String path) +75
System.Web.Mvc.HtmlHelper.AntiForgeryToken(String salt, String domain, String path) +48
Is this a bug in the ASP.NET MVC 3 Html Helper to generate the AntiForgeryToken? Or am I missing something in regards to configuring machine keys?
It looks like I screwed up the keys - using http://aspnetresources.com/tools/machineKey I was able to generate a valid machineKey config section.

Resources