Asp.net delegation - asp.net

I am making a .Net Web API that gets data by calling an SQL server. The user is authenticated via Windows Authentication (Kerberos). I would like the user credentials to be passed to the SQL server via delegation, but the SQL server sees an anonymous user.
This is what I have done:
IIS application:
Windows Authentication and asp.net impersonation enabled. Anonymous and forms authentication disabled.
Enable kernel mode authentication is checked.
Providers: Negotiate, Kerberos.
Use app pool credentials: True.
Application pool:
Managed pipeline mode: Classic.
Identity: Network service.
In AD, the computer the web server runs on is set to "Trust this computer for delegation to any specific service (Kerberos only)"
The connection string to the SQL server contains Integrated Security=SSPI;
Edit: In my web.config I have
<system.web>
<authentication mode="Windows" />
<identity impersonate="true" />
</system.web>
and
<security>
<authentication>
<windowsAuthentication enabled="true">
<providers>
<clear />
<add value="Negotiate" />
<add value="Kerberos" />
</providers>
<extendedProtection tokenChecking="None" />
</windowsAuthentication>
<anonymousAuthentication enabled="false" />
</authentication>
</security>
The generic HOST spn is set for the machine.
From the browser I access the web application via http://machinename.domain.net.
I would expect in this setup that my IIS application is run under the machine account?
When I catch a request in the debugger on the web server, I can see that WindowsIdentity.GetCurrent().Name is the account of the user browsing the web application and WindowsIdentity.GetCurrent().AuthenticationType is set to "Kerberos". So that should be good.
However WindowsIdentity.GetCurrent().ImpersonationLevel is only set to "Impersonate". I would have expected it to be set to "Delegate"?
When I make a request to the SQL server, I get "Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'" so obviously the user credentials are not passed to the SQL server.
I hope someone can see what I am doing wrong. I really need a push in the right direction.

For future reference if someone runs into the same issue:
The issue was that we tried from Chrome. It works in IE, but on Chrome the registry change mentioned in this post was needed: Kerberos delegation doesn't work in chrome

You should be able to set the Authentication to ASP.NET Impersonation within IIS. You will probably be required to set the following in your web.config file too, as part of < system.web> section.
<identity impersonate="true" />
This may be required in the < system.webServer> section to, although not always recommended due to security concerns.
<validation validateIntegratedModeConfiguration="false" />

Related

Where (how?) does IIS store custom application pool identities?

I have an application pool that I use for development… and I have it running under my credentials (so I don't have to worry about permission/access issues). Two things make me think my credentials might be just sitting in a file (or registry entry)… which is worrisome:
When I change my password, I have to update the stored credentials
The setup dialog has a confirm password field
If IIS was just storing some authentication token or something, I would expect to only enter my password once (because authentication was happening immediately).
Anyone know where my credentials are being stored? Are they just encrypted using some system key then pulled out and used when the app pool spins up?
Here is the dialog where I'm entering the identity's credentials:
I open that dialog from the app pool's Advanced Settings:
Other Info
IIS 7.5 on Windows 7
I am using virtual accounts for other application pools, but that's not what I'm using here: I'm using actual Windows account credentials
UPDATE
Based on nicolas-dietrich's response, I found the following…
The application pool credentials (and general settings) for IIS 7.5 are stored in %systemroot%\System32\Inetsrv\config\applicationHost.config.
Encryption is handled by AesProtectedConfigurationProvider, which is the standard (?) way to protect sensitive config info (like db connection strings or–you know–passwords)
Here are the relevant sections with sensitive/irrelevant info replaced by ellipses (…):
<configProtectedData>
<providers>
<!-- … -->
<add name="IISWASOnlyAesProvider" type="Microsoft.ApplicationHost.AesProtectedConfigurationProvider" description="Uses an AES session key to encrypt and decrypt" keyContainerName="iisWasKey" cspProviderName="" useOAEP="false" useMachineContainer="true" sessionKey="…" />
</providers>
</configProtectedData>
<system.applicationHost>
<applicationPools>
<add name="DefaultAppPool" queueLength="5000" managedRuntimeVersion="v4.0" />
<add name="GeneralDev" queueLength="5000" autoStart="true">
<processModel identityType="SpecificUser" userName="mydomain\myusername" password="[enc:IISWASOnlyAesProvider:…:enc]" />
</add>
<!-- … -->
<applicationPoolDefaults managedRuntimeVersion="v4.0">
<processModel identityType="ApplicationPoolIdentity" loadUserProfile="true" setProfileEnvironment="false" />
</applicationPoolDefaults>
</applicationPools>
<!-- … -->
</system.applicationHost>
Hopefully, safe enough? ¯\_(ツ)_/¯
In IIS6 the AppPool identities were stored within the IIS metabase (%systemroot%\System32\Inetsrv\metabase.xml) in an encrypted string located under W3SVC/AppPools//WAMUserPass.
That was not so secured though as it was possible to decrypt and to show it as plain text (http://www.jasonsamuel.com/2010/04/28/how-to-get-the-iusr-and-iwam-user-account-passwords-on-an-iis-server/)

Login failed for user '...' in ASP.NET WebAPI

The client application and the sql server instance are on separate machines. I'm working with Entity Framework and have created the Data Model for the database. Locally the application works fine with the database. But after the application has been deployed on IIS, I cannot access to the database. I'm getting the error message:
The underlying provider failed on Open.
Login failed for user 'domain\account'.
In the Web.config of the WebAPI is the connectionString defined as follows:
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Windows" />
</system.web>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<connectionStrings>
<add name="QuReContext" connectionString="metadata=res://*/Models.QuReModel.csdl|res://*/Models.QuReModel.ssdl|res://*/Models.QuReModel.msl;provider=System.Data.SqlClient;provider connection string="data source=spartak,2500;initial catalog=First_DB;User Id=domain\account;Password=MyPW;MultipleActiveResultSets=True;"" providerName="System.Data.EntityClient" />
</connectionStrings>
I've tried many kinds of solutions from threads in stackoverflow. But nothing has helped. I'm using the ASP.NET WebAPI 2, the Entity Framework 6, IIS 7.5 on a remote Webserver with Windows Server 2008 R2 and the SQL Server 2014 where my domain account is defined for access the database. This domain account I've written in the connection string of Web.config.
On IIS is created in Default Web Site "MyClientApp" and in this application is an extra application "RestApi" available. The Application Pool is defined as follows: Identity: ApplicationPoolIdentity, Managed Pipeline Mode: Integrated, Under the authentication of the application "RestApi" is just Windows authentication and basic authentication enabled.
Do anyone has an idea?
You can set up SQL Server to support two authentication modes:
Integrated (Windows) security
Mixed (Windows + SQL Server logins)
The first mode uses the current user credentials to try to login in SQL Server. In this case, the credentials are the ones from the process running the ASP.NET application, in other words, the app pool user. To specify this kind of login you need to include this in your connection string: Integrated Security=SSPI. (I've seen on some occassions that, apart from specifying integrated security, people specify user and password, but I'm not sure if you can override the current user credentials. In fact, I think that only worked in Windows CE).
The second mode supports integrated security, and also SQL Server logins, where the user and passwords are managed by SQL Server itself, and have nothing to do with Windows users and passwords. To authenticate a SQL Server login you must specify the user ID and password as you're doing in your query string: User Id=myUsername; Password=myPassword;
You should read about integrated and mixed security mode in SQL Server. This is quite an old information, but still applies.

Azure Distributed Cache sessionstate expires directly

I'm using the following configuration on a cloud services instance in Azure for ASP.NET sessionstate in the Web.Config:
<sessionState mode="Custom" customProvider="DistributedCacheSessionStateStoreProvider">
<providers>
<add name="DistributedCacheSessionStateStoreProvider" type="Microsoft.Web.DistributedCache.DistributedCacheSessionStateStoreProvider, Microsoft.Web.DistributedCache" cacheName="default" useBlobMode="true" dataCacheClientName="default" />
</providers>
</sessionState>
And the datacacheclient's configuration:
<dataCacheClients>
<dataCacheClient name="default" isCompressionEnabled="false">
<autoDiscover isEnabled="true" identifier="InternetWebRole" />
<localCache isEnabled="true" sync="TimeoutBased" objectCount="100000" ttlValue="300" />
</dataCacheClient>
</dataCacheClients>
When I create a new deployment to Cloud Services, the process was succesfully executed. But when I login on the website, the session only lasts for 1 or 2 requests. Clicking to another page redirects me back to the login-page. The authentication of my profile is succesful, but the session is expired almost immediately. I'm using forms-authentication using the normal ASP.NET membership provider:
<authentication mode="Forms">
<forms loginUrl="/Login" timeout="120" />
</authentication>
Strange thing is that 3 or 4 hours later, the problem solves itself. I can login again and it keeps my session for two hours straight. I've kept the instance running now for the weekend and still everyting is working fine. But as soon as I delete the deployment and create a new one the problem starts all over again.
Is there anyone that recognize this issue with the same sessionstate provider?
I've solved the issue by adding a machine key to the web.config, which is the same for all cloud services instaces.

Client Authentication via X509 Certificates in asp.net

I have an asp.net application and I need to authenticate users using X509 certificates. That is, the user must install a certificate issued by me so that he can browse my website and I can identify which user is, by this certificate.
I have already configured SSL on IIS, but it's not what I'm looking for right now, and I don't know where to start.
How can I achieve this in asp.net c#?
To create a secure authentication mechanism you would use both client certificates and username / password. The reason is that a certificate is something that can be stolen (copied) but a password is something that is only known by the person. An alternative could be a certificate on a smartcard, protected by a PIN.
To use client certificates in ASP.NET applications you need to do the following:
Step 1: In IIS Manager, open your application or web site, choose SSL Settings and choose both Require SSL and Require Client certificate.
Now when the user opens your web site, the browser will prompt him to select a client certificate that will be used in the communication.
Important At this point you have to make sure that the certificate is issued by someone you trust (since anyone can create their own self-signed certificates).
Step 2: Add a configuration item (either web.config, database etc.). In this list you would add the thumbprints of the whole CA (certificate authority) chain for your client certificates.
<add key="ClientCertificateIssuerThumbprints" value="4901f5b87d736cd88792bd5ef7caee91bf7d1a2b,0113e31aa85d7fb02740a1257f8bfa534fb8549e,c9321de6b5a82666cf6971a18a56f2d3a8675602"/>
Step 3: Create a classic username / password login page. Verify the username/password.
Step 4: Add the following code to your login page:
var x509 = new X509Certificate2(this.Request.ClientCertificate.Certificate);
var chain = new X509Chain(true);
chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline;
chain.Build(x509);
var validThumbprints = new HashSet<string>(
System.Configuration.ConfigurationManager.AppSettings["ClientCertificateIssuerThumbprints"]
.Replace(" ", "").Split(',', ';'),
StringComparer.OrdinalIgnoreCase);
// if the certificate is self-signed, verify itself.
for (int i = chain.ChainElements.Count > 1 ? 1 : 0; i < chain.ChainElements.Count; i++)
{
if (!validThumbprints.Contains(chain.ChainElements[i].Certificate.Thumbprint))
throw new UnauthorizedAccessException("The client certificate selected is not authorized for this system. Please restart the browser and pick the certificate issued by XXXXX");
}
// certificate Subject would contain some identifier of the user (an ID number, SIN number or anything else unique). here it is assumed that it contains the login name and nothing else
if (!string.Equals("CN=" + login, x509.Subject, StringComparison.OrdinalIgnoreCase))
throw new UnauthorizedAccessException("The client certificate selected is authorized for another user. Please restart the browser and pick another certificate.");
Only when both the password and the certificate have been checked, the user should be allowed in the system.
Assuming you have IIS 7.0 or higher, you can configure Client Certificate Mapping Authentication
Using Active Directory (Extremely easy, leaves the mapping work to the AD server)
<location path="Default Web Site">
<system.webServer>
<security>
<access sslFlags="Ssl, SslNegotiateCert" />
<authentication>
<windowsAuthentication enabled="false" />
<anonymousAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<basicAuthentication enabled="false" />
<clientCertificateMappingAuthentication enabled="true" />
</authentication>
</security>
</system.webServer>
</location>
Or using IIS (More configuration needed in IIS, needs access to the client certificate, but works standalone, no roundtrips to the AD). In this case, you specify (one or more) user credentials and
map each user to a certificate's public key to a user whose credentials you specify, or
map multiple certificates to a user based on values in the certificate's fields
Configuration (many to one):
<location path="Default Web Site">
<system.webServer>
<security>
<authentication>
<windowsAuthentication enabled="false" />
<anonymousAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<basicAuthentication enabled="false" />
<iisClientCertificateMappingAuthentication enabled="true"
manyToOneCertificateMappingsEnabled="true">
<manyToOneMappings>
<add name="Contoso Employees"
enabled="true"
permissionMode="Allow"
userName="Username"
password="[enc:AesProvider:57686f6120447564652c2049495320526f636b73:enc]">
<rules>
<add certificateField="Subject"
certificateSubField="O"
matchCriteria="Contoso"
compareCaseSensitive="true" />
</rules>
</add>
</manyToOneMappings>
</iisClientCertificateMappingAuthentication>
</authentication>
<access sslFlags="Ssl, SslNegotiateCert" />
</security>
</system.webServer>
</location>
(Sample configuration rather shamelessly copied from the samples on the iis.net documentation pages, which are quite elaborate.)
Or you can configure your application to use Claims-Based Authentication with a Security Token Service (STS) that authenticates clients based on client certificates. ADFS 2.0 can fullfil this role, or if it is not available, you could look at the Thinktecture Identity Server.

Basic authentication for the web service

I'm having problems to set up the basic authentication for the web service.
I'm writing standard web service (not WCF) using .NET 4.0
In the web.config I have the following settings:
<configuration>
<system.web>
<identity impresionate="true" />
<authentication mode="Windows" />
</system.web>
...
</configuration>
The web service has one method:
[WebMethod(Description = "Returns currently logged in user.")]
public string WhoAmI()
{
return "You are logged in as: " + System.Threading.Thread.CurrentPrincipal.Identity.Name;
}
The test server is virtual machine located on the same domain as my machine. When I access it, I don't get anything for the CurrentPrincipal.Identity.Name.
I also tried to access the web service using the soapUI. I entered my username, password and domain but I still can't get it to work.
Any help would be appreciated.
On IIS (inetmgr) you can find Authendication Method window.(WebService1->Properties->Directory Security->Authendication Controls->Edit Button)
You can choose Authendicated access. (Diggest,Basic,Integrated Authendication)
After that changing your method will work as you want.
Does it work if you change impresionate to impersonate?

Resources