ASP.Net web application trying to use Impersonation and Delegation to connect to SQL Server - asp.net

I'm trying to use Impersonation and Delegation in an intranet ASP.Net web-app in order to pass authenticated users' credentials onto a SQL Server.
The web server and SQL server are two separate machines, but in the same domain, so Delegation is required.
I've done the following:
set <authentication mode="Windows"/> and <identity impersonate="true"/> in my web-app's web.config.
enabled Constrained Delegation from the web server to the MSSQLSvc service on the SQL Server, in Active Directory.
enabled only Windows Authentication in the website, through IIS.
Apparently this should all work, but it doesn't (the SQL Server is denying access to the anonymous user - "Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'").
In IIS7, the Application Pool is set to use Integrated Pipleline Mode and is running with the NetworkService Identity. The website only has Windows Authentication enabled, Extended Protection is Off, Kernel-mode authentication is enabled, and NTLM is the provider.
All the web pages I've read seem to indicate that my setup should work. What am I missing?

I've discovered the answer:
The Windows Authentication provider in IIS7 must be set to Negotiate:Kerberos, not NTLM. This means that the Kernel-mode authentication setting must be disabled. This seems to be fine. I think I'm right in saying that Kernel-mode authentication is required when using a custom identity, i.e. one specific identity. Delegation can use an arbitrary number of identities. So all is well.
I've written a blog post about this too, which goes into a bit more detail.

No - it is not accurate to say you need Kerberos, an SPN, to trust the server for delegation, and that this is the ONLY way to do it. Yes, this is one way to do it (and you do need all of it to make it happen via Kerberos), but it is not the ONLY way, or even technically the most secure way or easiest way. Do you really want to have to do extra configurations and create a login for every web user to your DB in SQL? What if any one of those accounts is compromised? More accounts, more vulnerabilities.
No, create a Domain service account, instead, and let that access SQL. If your security guys lock down things, give that user these rights: Logon as a service, Logon as a batch job, and Allow logon locally. Or, if this is just to develop and test the theory or you don't care or can't find the settings or are still getting errors later on, and this might not get a large following, but give it local Admin (sometimes you gotta do what you gotta do - some security pros lock down things tighter than I would care to write about - can always troubleshoot security later to lock it back down). Then set that account as the custom account on the app pool and give that account a login in SQL. Give it dbo on just THAT ONE database.
On the website in IIS, set the authentication type as Windows. I've seen them say "Basic" in other blogs so Kerberos will work, but NTLM uses Windows authentication. In IIS 7, you may also want to enable ASP .NET impersonation. Personally, I've only tried this on IIS 6, but the principal is the same.
In the web.config, add this under <configuration>, which is a "peer" to <system.web>:
<connectionStrings>
<add
name="NorthwindConnectionString"
connectionString="Data Source=serverName;Initial
Catalog=Northwind;Integrated Security=SSPI;User
ID=userName;Password=password"
providerName="System.Data.SqlClient"
/>
</connectionStrings>
And in <system.web>:
<authentication mode="Windows"/>
<identity impersonate="true"
userName="domain\user"
password="password" />
Then read the string into your app like this:
using System.Configuration;
string connString = String.Empty;
if (ConfigurationManager.ConnectionStrings.ConnectionStrings.Count > 0)
{
connString = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
if (connString != null) // do DB connection stuff here
Console.WriteLine("Northwind connection string = \"{0}\"",
connString.ConnectionString);
else
Console.WriteLine("No Northwind connection string");
}
See http://msdn.microsoft.com/en-us/library/ms178411.aspx.
If it will not connect with the service account after filling in that account in the web.config for the impersonate tag and the SQL connection, you can then use impersonation methods using WindowsImpersonationContext (http://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext.aspx). Specifically, you want wic.Impersonate() and wic.Undo() after getting their token. You can read in the service account domain, name, and password from the web.config, in the form of AppKeys.
In short, there are ways around the issues. You can even encrypt the password in the web.config - both in the ConnectionString, and if you want to store it in an AppKey instead of directly in the "impersonate" tag, if you don't want plain text passwords in there (which I'd recommend against), and so you can have it for the creation of a Logon token, if you need to use the Impersonation methods (as I did).

Related

How to integrate an ASP.Net intranet with Active Directory?

I'm currently working on an intranet portal for my company, it will remain internal, only users with accounts on our domain will have access to it. At the moment, I use forms authentication method, and authenticate users against the Active Directory domain with System.DirectoryServices. I also interrogate the AD with System.DirectoryServices.AccountManagement to manage access rights depending on the Active Directory groups the user belongs to and I store some data in user sessions and everything works fine.
I've been asked to study whether Single Sign-On could be implemented in our context, all our users work in RDS without exceptions, so they always are in an authenticated session and the higher-ups would prefer not having to sign in at all. As far as rewritting the code to authentify and authorize users as explained here and here
I'm not too concerned.
I tried everything on a blank ASP.Net MVC5 project, to which I added an ADO.Net model with integrated security. My dev SQL Server is hosted on a remote server (not the dev desktop), the same server also hosts the dev IIS server. In production, both services will be on separate servers. This worked fine as long as I was debugging localy on my desktop, but once I published to the IIS server, I hit the first snag and couldn't get out of it.
When I changed settings in the IIS app to disable anonymous and enable windows authentication, IIS automatically changed <authentication mode="Forms" /> to <authentication mode="Windows" /> in the web.config file (as suggested here), but I couldn't make it work with Integrated Security=True or Integrated Security=SSPI, I get an error page saying [SqlException (0x80131904): Login failed for user 'DOMAIN\MACHINE$'.].
The connection string reads like: <add name="ASPNetProjectEntities" connectionString="metadata=res://*/Models.ModelASPNetProject.csdl|res://*/Models.ModelASPNetProject.ssdl|res://*/Models.ModelASPNetProject.msl;provider=System.Data.SqlClient;provider connection string="data source=MACHINE;initial catalog=DATABASE;persist security info=True;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
I tried to add the machine account DOMAIN\MACHINE$ to SQL logins and grant it rights to the database, but no dice. I tried to authorize delegation in the server AD account and activate identity impersonation, but that didn't work either. I can only make it work on the remote IIS server if I put a SQL user/pass in the connection string.
So I'm at loss what could be missing here to make it work with Integrated Security. Or will I have to put SQL credentials in the connection string and encrypt it on IIS?
You're confusing Integrated Security with the authentication mechanism of your website. If you use Integrated Security, the connection used from your web app to SQL Server will use the account that the application pool your web app is running as is using. To use Integrated Security, have a service account created in AD, set your app pool to run as that account, and grant the necessary permissions to that service account in SQL Server.
Note that whether you're using forms auth or integrated security isn't relevant to how the web app connects to SQL Server, unless you're using impersonation, which you should never use.

AD User Authentication

I am attempting to create an ASP.NET (.NET 3.5) website to connect to our Exchange 2010 server through Exchange Web Services, I am able to connect to EWS when I define the username, password and domain to authenticate with but I would like, if possible, to not include login details in my code.
In IIS I have enabled Integrated Windows Authentication for the site, in web.config of the site I have <authentication mode="Windows"/>.
The following code is what I have been woking with:
svc.UseDefaultCredentials = True
svc.Credentials = New WebCredentials()
svc.Url = New Uri(svcURL)
With the above code I am receiving the message:
When making a request as an account that does not have a mailbox, you
must specify the mailbox primary SMTP address for any distinguished
folder Ids.
When I attempt to use svc.Credentials = CredentialCache.DefaultNetworkCredentials (in place of svc.Credentials = New WebCredentials()) I receive the error message:
Unable to cast object of type 'System.Net.SystemNetworkCredential' to
type 'Microsoft.Exchange.WebServices.Data.ExchangeCredentials'.
As mentioned, the only thing that has worked is to define the user credentials to authenticate to by hardcoding user login details, which I would rather not do: svc.Credentials = New WebCredentials("username","password","domain")
Has anyone been able to authenticate to EWS using the credentials of the current logged in user in an ASP.NET website?
By default it is not possible to delegate a user's credentials from one server (the server on which you are hosting your ASP.NET site) to another (your Exchange server). This is known as a "server hop" and Windows will prevent it by default as a security measure.
You have a couple of options to work around this:
Using Kerberos: When Kerberos is enabled it makes it possible to delegate user credentials between servers when using Windows authentication. I do not know the exact details on how to set up Kerberos as I am only a humble developer but maybe your system administrator can assist you. AFAIK, you need to set up your ASP.NET server to allow user delegation.
Setting the user identity of your IIS application pool: If Kerberos is not an option, you may change the identity of the application pool that your ASP.NET site is running under. First define a new application pool in IIS manager. Then go to the Advanced Settings dialog for that application pool and set the identity to a domain user that is allowed to access your Exchange server. More info on the application pool identity here: http://technet.microsoft.com/en-us/library/cc771170(v=WS.10).aspx.
Setting the <identity> element: If you for some reason cannot change the application pool, you may try impersonation of your ASP.NET web site using the <identity> element in your web.config file. ASP.NET gives you the option of storing the credentials in the registry so that you do not have to put them directly in your web.config file. More info here: http://msdn.microsoft.com/en-us/library/72wdk8cc(v=vs.90).aspx
Using the <appSettings> ellement and encryption: The last option I can think of is to simply put the credentials in your web.config file as normal <appSettings> and then encrypt the entire <appSettings> section. You would then simply read the appSettings from your code using the AppSettingsReader class. .NET allows you to encrypt sections of the web.config file out of the box and you can read the settings without event noticing that the section is encrypted. .NET takes care of decrypting for you. More info here: http://msdn.microsoft.com/en-us/library/zhhddkxy.aspx

setting connection string

I have been building my web application with visual studio and sql server express and now I'm in the process of deploying it on a server. I need to change the connection string
This is what I have:
<add name="MySiteDBConnectionString"
connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\MySiteDB.mdf;Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient" />
This is what I need to replace it with:
<add name="LocalSqlServer"
connectionString="Data Source=DBServerName;Integrated Security=false;Initial Catalog=DBName;User ID=DBLogin;Password=DBPassword"
providerName="System.Data.SqlClient" />
The problem is that I don't know where or even if I set up a password for the database. What is integrated security?
I'm using linq-to-sql, do I also need to make some changes in the dbml file?
Thanks for some suggestions.
If you are not using Intergrated Security you will need to set up a user and password on the database server itself. If you are using shared hosting it is possible this will be provided for you. You can then replace DBLogin and DBPassword with your credentials.
Intergrated security uses your windows login/password details to authenticate against the database.
With your linq to sql if you are not passing in a connection string yourself you will need to use the designer to change the connection to the new database (or just overwrite the one in your config file).
Integrated security means that it will use credentials that the web site (app pool) is running under to access the database. You'd need to have SQL server set up to allow this account access to read/write your tables in order for this to work. If your web site is set up to run under the anonymous network account, then it's unlikely that this would be the case. If your web site runs under a domain account, then it's possible that you would need to use integrated security.
If you're not using integrated security, then your DB admin will have set up a SQL login for your application. This is the id/password that you need to use in your connection string. It's possible that multiple accounts have been set up, an admin account, a read/write account, a read-only account, execute SP account (these describe the permissions assigned to the account, not necessarily their names). In that case choose the appropriate one -- it's almost certainly not the admin account unless your DBAs know nothing about security.
FYI, if you use the User ID=...;Password=... format, you can omit the Integrated Security=false as it will assume a SQL login account and password.
'Integrated Security' mode is what was formerly called 'Use Trusted Connection': it uses the credentials of the currently logged on Windows user (in this case, most likely the IUSR_xxx account the web application is running as, or sometimes the computer account) as its login credentials.

ASP.NET web app can't use multiple impersonation for authenication

I have a asp.net app (uses windows authentication for access) which (stipulated by the security team) needs to connect to a remote SQL Server 2005 using integrated security.Because of the fact that it is remote SQL server I needed to impersonate a custom account (impersonating the original caller would not work) via :
<identity impersonate = "true" userName="domainname\user" password="password" />
This workes fine. The rub is my app also connects to an SSRS server for reporting needs using the ReportViewer control. The report server is on a separate server and the security team mandates that all calls to this server must be using the original window's account for auditing purposes. It seems my only option was to to try and separate my app into folders and use a "location" tag in my web.config and use separate identity tags. Such as:
<location path="Reporting">
<system.web>
<identity impersonate = "true"/>
</system.web>
</location>
Note: no username and password specified which means it should impersonate the original caller.
However to make matters even more complicated my app is a Masterpage/content page app. The master page makes calls to SQL to populate menus and such. Bottom line is the dual impersonation track is not working. I am ready to throw my hands up and declare that that this can not be done. If there was a way where I could have the app impersonate the original caller which would satisfy my SSRS auditing needs yet make connections to SQL server as the custom domain account. I cannot use SQL authentication: not allowed although that would solve this issue.
Have you tried the following setup:
Set impersonation to true. This is necessary for authentication into the application and for access to the SSRS to use current user logged in.
Use one connection string to SSRS that has Integrated Security set to true, so that the impersonated user passes straight through.
Use a second connection string, with the custom user name and password hard coded into the connection string. You can encrypt the connection string section of the web.config so that it isn't visible to human eyes, but the framework will automatically decrypt this on the fly when creating a connection.
I have a similar situation (need a specific account to retrieve specific data, but the general impersonation for the rest of the service functionality) and this setup is working.
EDIT: The general syntax for encrypting your web.config from the command prompt is:
aspnet_regiis -pef "connectionStrings" [PhysicalPathToApplication] -prov "DataProtectionConfigurationProvider"
Encryption is done on a machine per machine basis, so the encryption will have to be done on the specific server. You can pull up more documentation on this if needed.
You should be able to switch the impersonation on and off, so you can go back to using the default account running the site. I will have to check, it's been a while since I have done it.
This looks like a start as to how to do it:
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext =
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
//Insert your code that runs under the security context of the authenticating user here.
impersonationContext.Undo();
Essentially you just impersonate the appropriate user for the calls you need, and then "undo" the context and turn it off. It goes back to the default user after that.
Here is a link to the windows identity class:
http://msdn.microsoft.com/en-us/library/system.security.principal.windowsidentity.aspx

Retrieving the Windows username from a logged-in machine through an intranet application

How can an application, running on a production server, access the login username of the machine that a user is accessing an application from? For example, I am currently logged into my machine on the INTRA corporate intranet. My username will be INTRA\Username.
I have added specific usernames to a database and wish to check this intranet username against the database to restrict access to an application and leverage the username across the application.
Currently, I am using the following code to access the username:
Private username As String = Thread.CurrentPrincipal.Identity.Name
This is working great on localhost, but when authenticating against the database on a development server, I'm getting the following error:
Login failed for user 'NT
AUTHORITY\ANONYMOUS LOGON'.
Is this an incorrect approach? Is this even possible, or is it too much of a security issue? This application will be an internal intranet application running in an IE shop. Relevant pieces of web.config that already exist include:
<identity impersonate="true"/>
<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
</authorization>
<connectionStrings>
<add name="CONNSTR" connectionString="Initial Catalog=DATANAME;Data Source=servername;Integrated Security=True;" providerName="System.Data.SqlClient"/>
</connectionStrings>
When setting up your web application on the server, you need to go into the Document Security section (the name of it changes depending on what version of IIS your server is running, but it's something like that), turn off anonymous authentication, and turn on Windows authentication. That tells the server to request windows login authentication from the browser. (Perhaps someone who knows web.config files better than I [which is nearly anyone] can edit this to point to the relevant bit; I don't think it's impersonate but if I knew, I'd say. I've so far only done this via the UI.)
in your example, you are locating the username that your webserver is running under. What you are after is the username of the user accessing the page.
Try something like this:
How To: Use Windows Authentication in ASP.NET 2.0
If setting the directory security to Windows Authentication is not working, change it to Basic Authentication. You'll also need to specify the domain name to authenticate against. This was the only way we could get the security to propagate through from the IIS layer to the DB. Unfortunately this causes the username and password to be sent through clear text. Its not the best solution, but since things were on the Intranet, it worked while we work on updating our login procedure.

Resources