I'm hoping all of you .NET devs out there can help me with this dilemma. I currently manage an ASP.NET intranet site at my company. To authenticate our users with Active Directory, we have code similar to the following:
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, string name, string user, string password) {
bool credsOK = pc.ValidateCredentials(string user, string password);
//Check if the creds come back valid
if(credsOK) {
//Do Stuff
}
}
My concern is that someone with access to the code could potentially set a breakpoint at the if statement after the call to ValidateCredentials, add a watch on the password variable, and thus be able to see the user's password in plain text, which to me is dangerous and insecure, especially in a domain context.
So a couple of questions:
1) Why does ValidateCredentials take credentials as plain strings vs. more secure data types?
2) What are some best practice ways I could pursue authenticating the user against AD using their credentials, without using ValidateCredentials()?
Any help you can provide is greatly appreciated.
Thanks,
-rk15000
My thinking on this, if someone has access to the source code and put breakpoints for harvesting passwords, there is probably bigger concerns to worry about. They are after all going to need to be in a position to be able to attach remote debuggers to your production server(s).
To answer your question though. You will probably need to go down the route of separate sign-in services that would isolate the authentication form your application using some SSO tool like ADFS maybe. However based on the example you've given it's unclear how much of an impact that is going to have
Related
I have made an application in C# that sends mail and SMS ( SMS via an API). It works perfectly, but my concern is that someone will decompile my application, which has the credentials to both the sending emailaccount and the API. The API works with
private void sendSMS(String username, String password, String destination, String body, String sender)
{
String browserURL = "http://api.messagebird.com/api/sms?username="
+ username + "&password=" + password + "&destination="
+ destination + "&body=" + body + "&sender=" + sender;
_w.Navigate(browserURL);
}
Just by decompiling or using wireshark the "hacker" can send SMS as he desires, while the application is designed to send him an SMS on special events like a security breach in his house. also I don't want the hacker be able to change the E-mail's password, so other users don't get their email anymore.
How can I prevent this?
The code is designed to run on the customers own computer/server and alert him for example if there was motion detection.
I thank you all for you answers. I choose to let the customer fill in their own credentials for mail and SMS services, just to avoid having a security breach.
Do not put secrets in code. Put them in an encrypted, safe place, on disk and read it when needed.
Obfuscation is exactly what it says: it hides rather than it protects.
Also: do not send credentials unencrypted over the internet.
The safest way to protect your password is simply to not store it all, at least locally.
As soon as you store anything client-side the safety of the data is only as strong as the safety of the hardware it's sitting on. For example, if someone were to install your app on a public machine (e.g. internet cafe) anyone who knows what they are doing has the potential to get access to your password. It's less of a problem if you can be sure that the app is only going to be installed on private machines and only used by "good" users (which, ultimately, you can't).
How secure you need this password to be is really down to you. The questions you really need to ask yourself are
What sort of damage could be done if they did manage to get the password?
What measures do I have in place to detect any sort of misuse? (e.g. IP logging etc.)
What procedures do I have in place if someone was misusing the API? (e.g. password change)
The 3rd point poses a few problems when storing the password locally. If you detected misuse of the API and consequently changed the password, how can you cascade those changes down to the clients?
For me, the safest way to avoid all these issues is to have your app query your API server and have it return some sort of authentication token (aka API token). This token would then be passed along with any request back to your (hopefully, secure) server which validates the token and, if authorised, forwards the SMS request onto the SMS server.
Consider using System.Security.Cryptography.ProtectedData
This class provides access to the Data Protection API (DPAPI)
available in Microsoft Windows 2000 and later operating systems. This
is a service that is provided by the operating system and does not
require additional libraries. It provides protection using the user or
machine credentials to encrypt or decrypt data.
The class consists of two wrappers for the unmanaged DPAPI, Protect
and Unprotect. These two methods can be used to encrypt and decrypt
data such as passwords, keys, and connection strings.
http://msdn.microsoft.com/en-us/library/system.security.cryptography.protecteddata.aspx
For a given application I have a .aspx login form backed by a C# code behind file.
In the code behind I am using the following "home grown" method:
private bool AuthenticateUser(String username, String password)
{
bool validated = false;
try
{
PrincipalContext pc = new PrincipalContext(ContextType.Domain, "domnet.domad.com", "dc=domnet,dc=domad,dc=com");
IdentityType ADElement;
UserPrincipal up;
//Try first with no #DOM.COM - this should work for SamAccountName values:
username = username.ToUpper().Replace("#DOM.COM", "");
ADElement = IdentityType.SamAccountName;
up = UserPrincipal.FindByIdentity(pc, ADElement, username);
validated = pc.ValidateCredentials(username, password, ContextOptions.Negotiate);
//If SamAccountName fails try UserPrincipalName with #DOM.COM
if (!validated)
{
username = username + "#DOM.COM";
ADElement = IdentityType.UserPrincipalName;
up = UserPrincipal.FindByIdentity(pc, ADElement, username);
validated = pc.ValidateCredentials(username, password, ContextOptions.Negotiate);
}
//Put username into session
if (validated)
{
Session["Username"] = username.Replace("#DOM.COM", "");
}
}
catch (Exception) //login failure...
{
validated = false;
}
return validated;
}
This works fine for the application but I have other applications that need authentication too.
I really don't want to copy / paste the login files into ever application.
So my most basic question is what are my options to centralize the authentication code between applications?
In the future I will also be looking to:
Verify not only username/password but also AD group membership.
Once user is authenticated no more log in screens between apps. (SSO)
It seems to me I am not the first person to run into this problem.
I would prefer to use an out of the box solution vs. developing my own if possible.
You could:
Enable Windows authentication in IIS and set your Web.Config to use Window authentication http://weblogs.asp.net/scottgu/archive/2006/07/12/Recipe_3A00_-Enabling-Windows-Authentication-within-an-Intranet-ASP.NET-Web-application.aspx
Setup an ADFS claims authentication server and have all your applications use claims based authentication http://blogs.msdn.com/b/alextch/archive/2011/06/27/building-a-test-claims-aware-asp-net-application-and-integrating-it-with-adfs-2-0-security-token-service-sts.aspx
Have one application that all others redirect to for authentication and set an encrypted cookie that the other applications can read to verify authentication Encrypt cookies in ASP.NET
One approach would be to create a Core project (.dll/library) that contains the common parts that you wish to share between your applications, and then to reference that project in your applications.
Ie: Say that you have 2 applications: A and B you would create three projects A, B and Core. In project A and B simply add a project reference to the Core library. Now you can access any method in core from both A and B.
This approach works well with SVN and similar version control systems and you will find it is a very flexible way of working. The hard part is to identify what is really common code and and to make as general as possible.
#Baxter Not sure if my answer comes a bit late as this question was posted a few days ago, but am looking into the same problem of implementing centralized session and authentication management in my MVC 3 application, and I believe the following link would be of great interest to you:
http://www.codeproject.com/Articles/246631/ASP-NET-MVC3-Form-Authentication
The author of the article corresponding to the link above, factors out the authentication functionality into a separate DLL and uses dependency injection to use application context to utilize the external 'security' DLL. I am planning to use this approach to centralize the security mechanism and reuse it in 3 different MVC 3 web applications, so it is still research in progress, and will update this answer accordingly with what i find :)
You can refactor this method out into a separate project (meaning a different dll) and reference that project from any web application where you want to use this code.
An alternative if you are using Windows Authentication is to grab their SID, query AD for a piece of information that is shared between AD and the application's user table (we use the email address) and check to see if the user table has an entry with that email address.
This way, by logging onto their workstation, they are essentially pre-logged into any application using this authentication method. You just have to make sure that when you create a new user account (at the application level) you capture the info that you want to check for authentication (this is why we use the email address - everyone knows their company email).
This works really well with the Core library method suggested by Avada Kedavra. This method also allows you to have each application maintain its own user base (although it will also work well with a central user database).
My current application uses the PrincipalContext class to authenticate a default user (filled in in the Web.config dedicated section) against a LDAP (and more precisely an Active Directory):
ldapContext = new PrincipalContext(AdContextType, AdHost, AdContainer,
ContextOptions.SimpleBind,
AdDefaultUser, AdDefaultPassword)
This authentication then allows to search for any other user (via UserPrincipal.FindByIdentity(ldapContext, username)). BUT I have to change this, since having a user in clear in the Web.config is not acceptable for my client. OK, I can understand that :-))
So I have several solutions:
manage to encrypt the corresponding part of the Web.config (the membership part, with providers etc)
use the account from the application pool of the IIS 7 on which the application is deployed. This implies to retrieve a Principal from IIS (I guess this point should not be so hard), then use it for authentication against the AD; but I cound not find any method for that, neither via the PrincipalContext class not via the ActiveDirectoryMembershipProvider one.
Do you know how to manage any of these 2 solutions, or do you think of any other ?
Thanks a lot !!
I think not the username is your problem, more the password? As far as I can tell the second solution want work. There is no way that I know to do this.
For the first solution you can encrypt the username and the password and store the encrypted values in a string. After loading you will need to decrypt the strings. But your solution will not be safe against disassemble.
You should also think about if you need a high security implementation then you may store the encryption key in a secure store. Then you will be saver against disassemble.
I'm working on a WCF Service that is called by our other softwars to send bug information to our database. The problem is that, since it is an online service, it isn't safe, so I was wondering if it's possible to the service to request a password (i.e. when we call the service, we have to configure the password or something like that).
I googled about it, but it all seemed so complex for such a simple thing ... can you guys help me out?
Edit:
The idea is to authenticate through my software, without the need of a user login.
Another option is to implement your own security. Here's a basic example.
WCF Service
In your service, change it's ServiceBehavior's InstanceContextMode to PerSession and ConcurrencyMode to Single
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
public class SomeService : ISomeService
{
// ...
}
Add a Username and Password property in your service.
public string UserName { [OperationContract] get; [OperationContract] set; }
public string Password { [OperationContract] get; [OperationContract] set; }
Add a private method for checking a security.
public void CheckSecurity()
{
if ((this.UserName == null || this.Password == null) ||
this.UserName == "username" && this.Password == "password"))
{
throw new FaultException("Unknown username or incorrect password.");
}
}
Then call the CheckSecurity method in each of your service class constructor method.
public SomeServiceMethod1()
{
this.CheckSecurity();
// some method codes
}
Client Application
In your client application code, set the service username and password for every instance, or create a static class that will do this for you.
You might also try to use encryption in the username and password to add security.
Note that this is just to add another option for you that might fit your needs but you should always try to use the Microsoft way of doing things.
You can use the ASP.NET Membership provider to authenicate clients. There is an article on MSDN describing how to achieve that.
Unfrotunately security is never simple. For your requirements, have a look at UserNamePasswordClientCredential and UserNamePasswordServiceCredential. They might be sufficient.
All of the answers below are good, but it will depend on who's consuming your WCF service.
For example, I know a WCF service that's consumed by a iPhone application via REST, and some of the schemes above would be very difficult if not impossible to implement. It used something similar to stefan's solution of a session key passed in the message header.
There are many ways to handle this scenario.
Fredrik and stefan's answers together could yield an authentication method and authorization (via token) but you would have to write the login page, db, code, checks, enforcement, token creation, token embedding and token validation logic yourself.
PaulF's suggestion is probably less work because you can use features built into WCF to carry the credentials in the message transport. Could skip the token creation, token embedding and token validation logic pieces.
If you plan on supporting many different devices I would recommend looking into OpenID, Windows Identity Foundation, Azure Authentication Service, etc.
- These are a bit more complicated to setup but provide a solid, standards based and flexible method for passing around credentials. Passive Federation is a good keyword if you want to go that route.
Unfortunately, as Paul said, security is never simple. You have to think about how you authenticate, what you need to know about the person in the service/app, how you pass those credentials other apps/services securely, how they know you are really you and vica versa... Once you get federated authentication setup you no longer have to worry about a lot of those things for this service and others going forward...
Authenticating the software which is generating requests to a service is inherently a very difficult problem, far harder than authenticating a human user. The problem arises from the need to store secret information to be used to construct the credentials sent as the basis for authentication.
When authenticating a human user you can rely on them being able to store a secret (such as a PIN or password) in a data store (the wet grey matter between their ears) which only they have access to.
When you are trying to authenticate some body of software executing in an environment over which you have no control, there is no equivalent storage place for a secret which gives you any assurance that only your software has access to it. So in general there is no way for your software to generate a request which reliably proves that it was generated by your software and not something else.
Your only options are:
Find some way to provide a secret store to the software at runtime, such as a PIN-protected smart card which has to be used in conjunction with your client application.
Rely on security by obscurity, accepting that this can only make spoofing a bit harder and more time-consuming for an attacker, but will not defeat someone determined to break your authentication scheme.
What you need is to implement custom password validation.
Here are good articles about that:
UserNamePasswordValidator and CodeProject.
We use same thing in our project, but API key is used instead of Username. Password can be anything in this case.
Background:
This is really a general best-practices question, but some background about the specific situation might be helpful:
We are developing a "connected" application for the iPhone. It will communicate with the backend application via REST services. In order to not have to prompt the user for a username and password every time they launch the application, we will expose a "Login" service that validates their username and password on initial launch and returns an authentication token that can be used for future web service requests for real data. The token may have an expiration time after which we'll ask them to re-authenticate with their username/password.
The Question:
What are the best practices for generating this sort of token to be used for authentication?
For example, we could...
Hash (SHA-256, etc) a random string and store it in the database for the given user along with an expiration date. Do a simple lookup of the token on subsequent requests to make sure it matches.
Encrypte the user id and some additional information (timestamp, etc) with a secret key. Decrypt the token on subsequent requests to make sure it was issued by us.
This feels like it must be a solved problem.
Based on the feedback from the other answers to this question, additional research, and offline discussions, here is what we ended up doing...
It was pointed out pretty quickly that the interaction model here is essentially exactly the same as the model used by Forms Authentication in ASP.NET when a "remember me" checkbox is checked. It's just not a web browser making the HTTP requests. Our "ticket" is equivilant to the cookie that Forms Authentication sets. Forms Authentication uses essentially an "encrypt some data with a secret key" approach by default.
In our login web service, we use this code to create a ticket:
string[] userData = new string[4];
// fill the userData array with the information we need for subsequent requests
userData[0] = ...; // data we need
userData[1] = ...; // other data, etc
// create a Forms Auth ticket with the username and the user data.
FormsAuthenticationTicket formsTicket = new FormsAuthenticationTicket(
1,
username,
DateTime.Now,
DateTime.Now.AddMinutes(DefaultTimeout),
true,
string.Join(UserDataDelimiter, userData)
);
// encrypt the ticket
string encryptedTicket = FormsAuthentication.Encrypt(formsTicket);
Then we have an operation behavior attribute for the WCF services that adds an IParameterInspector that checks for a valid ticket in the HTTP headers for the request. Developers put this operation behavior attribute on operations that require authentication. Here is how that code parses the ticket:
// get the Forms Auth ticket object back from the encrypted Ticket
FormsAuthenticationTicket formsTicket = FormsAuthentication.Decrypt(encryptedTicket);
// split the user data back apart
string[] userData = formsTicket.UserData.Split(new string[] { UserDataDelimiter }, StringSplitOptions.None);
// verify that the username in the ticket matches the username that was sent with the request
if (formsTicket.Name == expectedUsername)
{
// ticket is valid
...
}
Building your own authentication system is always a "worst practice". That's the kind of thing best left to professionals who specialize in authentication systems.
If you're bent on building your own "expiring ticket from a login service" architecture rather than re-using an existing one, it's probably a good idea to at least familiarize yourself with the issues that drove the design of similar systems, like Kerberos. A gentle introduction is here:
http://web.mit.edu/kerberos/dialogue.html
It would also be a good idea to take a look at what security holes have been found in Kerberos (and similar systems) over the last 20 years and make sure you don't replicate them. Kerberos was built by security experts and carefully reviewed for decades, and still serious algorithmic flaws are being found in it, like this one:
http://web.mit.edu/kerberos/www/advisories/MITKRB5-SA-2003-004-krb4.txt
It's a lot better to learn from their mistakes than your own.
Amazon.com uses a HMAC SHA-1 message token to authenticate and authorize requests. They use this for a fairly large commercial service, so I'd be liable to trust their engineering decisions. Google publishes the OpenSocial API which is somewhat similar. Based on Google and Amazon.com using similar and openly published approaches to securing web requests, I suspect these are probably good ways to go.
Either of the two answers you've provided will suffice. You may find frameworks out there that do this for you, but the truth is it's not that hard to build. (Every company I've worked for has rolled their own.) The choice of database-stored tokens versus encrypted data "cookies" is an architectural decision -- do you want to incur a database lookup on every page view, or would you rather chew up CPU with cookie decryption? In most applications, using encrypted cookies provides a performance win at scale (if that's a concern). Otherwise it's just a matter of taste.
Since you're using WCF, you have a variety of options if using CFNetwork -- for instance NTLM or Digest Authentication:
http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/Concepts/Concepts.html#//apple_ref/doc/uid/TP30001132-CH4-SW7
I know this doesn't answer your specific question, but I have also been faced with this problem (iPhone - Tomcat) and decided to use the authentication services on the web server as much as possible. There's no significant penalty for including the authentication information with each request in most cases. A quick Google turns up lots of blog posts about WCF and RESTful services (and some related questions on StackOverflow).
Hope this helps!
You should implement:
OAuth2 Implicit Grant - for third-party applications https://www.rfc-editor.org/rfc/rfc6749#section-1.3.2
OAuth2 Resource Owner Password Credentials — for your own mobile application https://www.rfc-editor.org/rfc/rfc6749#section-1.3.3
which are exactly the workflows, from OAuth2, that you are looking for. Do not reinvent the wheel.
This simply sounds like a session identifier with a long expiration time. The same principles used for this in web applications could apply here.
Rather than encoding information, session identifiers are randomly chosen from a very large space (128 bits). The server keeps a record associating the session identifier with the user and other desired information such as expiration time. The client presents the session identifier over a secure channel with each request.
Security relies on the unpredictability of the session identifiers. Generate them with a cryptographic RNG, from a very large space.