views:

738

answers:

5

An application I'm modifying has a Web Service, and one of the web methods on that web methods is used to authenticate a user against active directory. So the current code called by the AuthenticateUser web method looks something like this:

string domainAndUsername = aDomain + @"\\" + username;
string ldsPath = buildLdsPath(searchBase);
DirectoryEntry entry = new DirectoryEntry(ldsPath, domainAndUsername, 
    password);

try
{
    //Bind to the native AdsObject to force authentication.
    object obj = entry.NativeObject;

    DirectorySearcher search = new DirectorySearcher(entry);

    search.Filter = "(sAMAccountName=" + username + ")";
    search.PropertiesToLoad.Add("cn");
    SearchResult result = search.FindOne();

    // more code to validate the result, etc...
}

When I started looking at this code, the first thing that worried me is the arguments to the web method look like this:

[WebMethod]
public ResultObj AddRole(string roleToAdd, string username, string password)
{
    // code that calls above Authentication fragment...
}

So the current web service is expecting a password string, presumably sent in the clear over the network as XML, when the request is made to the service.asmx page.

Has anyone dealt with this type of issue before? Are there alternative Active Directory authentication mechanisms I could use that would avoid having to pass in a plain-text password? The best option I could come up with on my own is to invoke the WebMethod using an encrypted password, and have the code on the other side decrypt it. However, I'd prefer a better solution--e.g.: is there some way to do search for a DirectoryEntry using a one-way hash instead of a password?

Edit:

Additional Details: To this point I haven't considered SSL as this is a tool that is internal to our company, so it seems like overkill, and possibly problematic (it'll be running on a company intranet, and not externally visible). The only reason I'm even worried about the security of sending plain-text passwords is the escalating amount of (possibly password-sniffing) malware present even on company intranets these days.

+3  A: 

If you have a public/private key combination, then the client could encrypt with the public key, and you decrypt with the private key.

However, that's too much work for the client, and not a very "web method" way of doing it.

Since you are sending the user name and password as parameters then you should resort to transport security, HTTPS, basically, which requires you to have a public/private key combination issued to you from a trusted certificate authority.


It should be noted that your association of SSL encrypted channel with an external facing site is incorrect. The point of wanting to encrypt a channel is to prevent man-in-the-middle attacks, exactly like you are trying to do here.

You could use a self-issued certificate, but that would require installing the public key of the certificate on each machine that is going to call your web method. It's easier to just get one from a trusted authority.

casperOne
I've dealt with the self-issued certificate route before, and it's difficult to maintain as you suggest. I will likely need to work through company IT channels to get a "real" SSL certificate from a trusted authority, but it may be doable with the right justification.
Ogre Psalm33
Re the self issued certificate - AD can issue your machine one. It will be automatically trusted by any other machine in the domain.
Mark Brackett
I did not know that Mark--that's good info. I may still have to get approval, because the AD server is for the whole company (of course), and is outside the purveyance of our department. But it does sound like it'd be easier to do through company channels.
Ogre Psalm33
A: 

We put our AD service on its own web site and got an SSL cert. Problem solved.

theG
I'll have to do a little research on this--I'm not sure of the implications, both from the perspective that our web service box is on an internal network, and what our IT group says about the idea.
Ogre Psalm33
We reverse proxied a new web site in IIS and used the same home directory as the internal version of the service. Not a single problem to this day (knock on wood).
theG
A: 

I think SSL, or possibly IPSec, are probably your best solutions.

Ragoczy
+1  A: 

HTTPS (as mentioned) is the easy choice. Or, you could just let IIS handle authentication thru Digest or NTLM. Your app can still make authorization rules. NTLM is secure, but it'll hurt your interop. Otherwise, AD does offer some digest authentication methods, but I don't have tested code using them.

With Server 2000 domains, there's an option for "Store passwords in reversible format" - that will allow a domain controller to calculate MD5 hashes of the password to compare against your presented MD5 hash. MS realized this was a bit of a security problem, though, so Server 2003 implemented "Advanced" digest authentication - which precomputes the hash.

LDAP signon should select MD5 Digest as the authentication type, supply the username, and then supply the MD5 hash of the password. The normal LDAP clients will probably want to MD5 your password themselves though, so you'll have to override or craft them yourself.

Mark Brackett
I'm not sure if it was your intention, but the NTLM Authentication you mention in your answer led me down the rabbit-trail of looking at the WebClientProtocol.Credentials property, which may wind up being our best solution. Kudos!
Ogre Psalm33
A: 

For our particular situation, because both the client and the web service are running on our company Intranet, a solution that may work for us is to handle the Authentication on the client end using the Integrated Windows NTLM authentication, and then then just have the client supply the credentials to the Web Service. Here is the client code:

public void AddRole(string roleName)
{
    webSvc.Credentials = CredentialCache.DefaultCredentials;
    // Invoke the WebMethod
    webSvc.AddRole(roleName);
}

The web method will now look like this:

[WebMethod]
public ResultObj AddRole(string roleToAdd)
{
    IIdentity identity = Thread.CurrentPrincipal.Identity;
    if (!identity.IsAuthenticated)
    {
        throw new UnauthorizedAccessException(
                ConfigurationManager.AppSettings["NotAuthorizedErrorMsg"]);
    }
    // Remaining code to add role....
}

Again, I must stress this solution will probably only work if the server trusts the client, and both talk to the same Active Directory server. For public Web Services, one of the other answers given is going to be a better solution.

For further information, see:

Ogre Psalm33