2

The bottom line is to create a custom FTP Authentication Provider. In order to do this one must inherit from IFtpAuthenticationProvider interface and then register that assembly in GAC. After that the assembly needs to be registered in IIS's FTP settings.

In order for this to be accomplished I have to use the database which holds my user information in the database from the custom Membership and Role providers we have developed previously. So, that means I have to use a local class library app.config file and read it with ConfigurationManager.OpenMappedExeConfiguration(). I read the connection strings from there and I'm even able to provide it to Linq-To-Sql classes dynamically. No problem there.

Next, I try to create a class that inherits from SqlMembershipProvider so that I can use it's own system to decrypt the password for a user. But, the problem is that it has to read machineKey values from configuration. The only way you can provide custom configuration to a SqlMembershipProvider is trough it's Initialize method (which is not intended for us to use it in our code anyway). But anyway I tried and failed. I've been able to provide it a custom membership settings but not machineKey settings.

So, I decided to go radical. I said: I have the decryptionKey from machineKey so I'll try to decrypt the password manually.

So far I've tried using RijndaelManaged:

    private string UnEncodePassword(string encodedPassword, string secretKey)
    {
        string password = encodedPassword;

        var keyBytes = new byte[16];
        var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
        Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));

        RijndaelManaged rm = new RijndaelManaged
        {
            Mode = CipherMode.CBC,
            Padding = PaddingMode.PKCS7,
            KeySize = 128,
            BlockSize = 128,
            Key = keyBytes,
            IV = keyBytes
        };

        var encryptedBytes = Convert.FromBase64String(encodedPassword);
        password = Encoding.UTF8.GetString(Decrypt(encryptedBytes, rm));

        return password;
    }

I'm kind of aware that I'm shooting blanks here since I'm not eve sure what kind of padding is used and most importantly even if I could I'm not hitting the right IV. The best I'v done so far is to get some junkish response like: �21l�<��(�\t��].

So I need some pointers. Even suggestions on how to tackle this whole issue from another perspective are welcome.

EDIT:

Just to make things a little bit more clear. I'm not trying to crack the password or anything. I have the password and I have the decryptionKey etc. I just want to find a way to Encrypt/Decrypt it so that I can make a user validation.
I am fully aware that encrypting/decrypting passwords are not the best approach in the first place, but there are reasons why that is being done that way.

Community
  • 1
  • 1
TheBoyan
  • 6,802
  • 3
  • 45
  • 61
  • I has two thoughts: 1. IV never will be the same is keyBytes. It can be constant or stored somewhere. Try all 0xFF or all 0x00. 2. Why you need decrypted user password? I think, you need method to authenticate password provided by user against password in database. Right? – werewindle Dec 29 '11 at 14:47
  • And other thought. It is weird to store encrypted password in database. Almost all software stores only password hashes. – werewindle Dec 29 '11 at 14:49
  • @werewindle - 2. yes, but the password is encrypted. I need to decrypt it first so I can compare it. – TheBoyan Dec 29 '11 at 14:49
  • @BojanSkrchevski: You should encrypt the user input and compare it to the encrypted password hash in the database. You'll be running around in circles for a long time if you attempt to decrypt the password and compare the two plain-text values. – Cᴏʀʏ Dec 29 '11 at 14:56
  • 2
    @BojanSkrchevski - Please do not store or use a system where you are able to decrypt a user's passwords. A system that allows you to do this is NOT secure. Encrypt the user's input and compare that. The memembership role functionality built into ASP.NET by default does user/password the correct way, which means, you CANNOT decrypt the password without knowing plaintext password, even then your onl comparing the two values not decrypting the stored data. – Security Hound Dec 29 '11 at 15:09
  • Can anyone support these claims with a few links as to why encrypting the password is not secure? – TheBoyan Dec 29 '11 at 15:17
  • 1
    I've got one: http://stackoverflow.com/questions/4941826/hashing-vs-encrypting-passwords – TheBoyan Dec 29 '11 at 15:38
  • Have you tried a reflection tool like [ILSpy](http://wiki.sharpdevelop.net/ILSpy.ashx)? Then take a look at `System.Web.Security.MembershipProvider` - `DecryptPassword` and `EncryptPassword` methods. – kuujinbo Dec 29 '11 at 16:23
  • @kuujinbo - I'll definetly give it a try, thanks – TheBoyan Dec 29 '11 at 16:24
  • "there are reasons why that is being done that way." and those reasons are fundamentally wrong. User's passwords are not your property. You should have no means to access them. Users as a whole are unaware of security implications and use the same password for their bank as they do with every website. If your database is ever compromised it leads to cascading vulnerabilities for the users. – Chris Marisic Dec 31 '11 at 00:59

3 Answers3

5

You should not be decrypting passwords. You should be comparing password hashes.

Passwords should not be stored with reversible encryption. Passwords are meant to be stored as hashes (not MD5 it's too weak).

Regarding your comment above, you do not need to decrypt passwords to compare them. You want to compare the hashes, not the original values. The system should not be able to get the original text of a password. Using the built in ASP.NET membership providers correctly stores passwords as hashes. This is likely why you can't decrypt them, they're meant to be impossible to decrypt.

Edit: In regards to the fact you use passwordFormat="Encrypted" for the SQL membership provider. With this fact you can either encrypt the password in the same format, with the same padding, cipherMode, and IV (you will need to query this from the SQL database) and then just compare the encrypted password values. This is likely unneeded work. You really just want to invoke the Membership's logon function in code with the username/password combination and verify whether it is successful or not. You will need to make sure the machine keys and relevant pieces match in both systems otherwise you won't be able to succeed.

You really want to plan to move to hashed passwords however, storing encrypted passwords as opposed to hashed passwords is nearly pointless. It is only just slightly better than storing them in plain text.

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • First of all at this point it's not my decision. Just treat that part as someone else did it and I have to take it from there. – TheBoyan Dec 29 '11 at 14:54
  • Do note, that impossible in regards to security is ALWAYS a relative term. With enough money and time, generally every single encryption or hashing scheme can eventually be decrypted. The goal is to just make it take years or 10,000s of years for it to happen. – Chris Marisic Dec 29 '11 at 14:55
  • Update on your edit. Do you have a suggestion how this can be solved? – TheBoyan Dec 29 '11 at 14:55
  • @BojanSkrchevski unless you're using custom hand built membership providers that are writing reversible encrypted passwords to the database the passwords are hashes in the database. You cannot decrypt them. – Chris Marisic Dec 29 '11 at 14:56
  • I don't think you got my point. I already have the decryptionKey. I just want to find a way to see if the user entered the correct password, regardless. – TheBoyan Dec 29 '11 at 14:57
  • @BojanSkrchevski I get your point, but I strongly believe your assumptions are inaccurate to what is really happening. Using the ASP.NET membership providers results in hashed passwords, not encrypted passwords. – Chris Marisic Dec 29 '11 at 14:58
  • Are you specifically using `passwordFormat="Encrypted"` in the configuration? Regardless even if you are using the low security encrypted format, you still do not need to compare decrypted password values. You want to encrypt the password text and compare the encrypted values. – Chris Marisic Dec 29 '11 at 15:00
  • @BojanSkrchevski: http://www.byteblocks.com/post/2011/05/03/Decrypt-password-in-SqlMembershipProvider.aspx – Cᴏʀʏ Dec 29 '11 at 15:00
  • @Chris - yes that's what is being used. – TheBoyan Dec 29 '11 at 15:01
  • @Cory - if you noticed I already have this link in my question and I also explain why this won't work – TheBoyan Dec 29 '11 at 15:02
  • @BojanSkrchevski: Oops! I didn't even look at the links in your post. That'll teach me. – Cᴏʀʏ Dec 29 '11 at 15:08
  • @Cory - NP, happens to me too :) – TheBoyan Dec 29 '11 at 15:09
  • @ChrisMarisic - I get what you're trying to say. That I should change my whole approach to storing password so that I can gain more control and thus solve my problem. For now that isn't an option for me. I have to find a solution to somehow mimic the behaviour of EncryptPassword and DecryptPassword of MembersipProvider. +1 for the suggestion though. – TheBoyan Dec 29 '11 at 15:13
  • @BojanSkrchevski /shrug I've explained the solutions very concisely both in regards to how security SHOULD BE done, and options for how you're currently do it. – Chris Marisic Dec 29 '11 at 16:07
  • @ChrisMarisic - and I thank you sincerely for that. But, the problem is more specific than "just" using the Membership provider's Encypt/Decrypt methods(I've already tried that as you can see in the question). The problem is that it cannot read the values from the config file. So I suppose I have to find another approach. That's why I tried RijndaelManaged. – TheBoyan Dec 29 '11 at 16:19
1

You don't typically need to store encrypted passwords, unless you want a form of password-recovery which lets the user see their current password. It's usually a bad idea and it does require a lot more work.

Conventionally, you should only store a salted hash of the password in your database. Hashing is a one-way operation (so nobody can ever decrypt the information to find out the passwords if your database is compromised).

To detect whether a user's password matches the one you have stored, you only need to hash their password with the same salt you used to hash the value in the database. If the two hashes match, the user has provided a correct password.

The authenticate algorithm should look something like this:

  1. Get salt for hashing (could be that you use a salt unique to each user, or a global one)
  2. Generate a hash of the user's password.
  3. Compare the hashed password to the one stored in your database for the username specified (if using LINQ-to-SQL, a simple Any statement can do this).
  4. Return a value to indicate success or failure.
Paul Turner
  • 38,949
  • 15
  • 102
  • 166
-1

I do agree with the others that hashed passwords are generally more secure that enrypted ones.

But because you can't choose why do you have to use the Encryption or Decription methods from MembershipProvider (btw, I don't even think it's possible).

Why don't you create your own encryption methods and use them in the web application and your class library.

  • I never gave it thought. It can definetly be one way to solve the problem. – TheBoyan Dec 29 '11 at 16:28
  • -1 this is a terrible solution, this has all the costs of replacing the encrypted passwords with a wheel reinvented AND technically flawed solution. If you're going to replace all the passwords, they should be replaced with hashes. – Chris Marisic Dec 29 '11 at 18:21
  • @ChrisMarisic - as I said previously that is a requirement and I cannot change it. Having that into consideration, this is the solution that is most applicable. – TheBoyan Dec 29 '11 at 18:26