The return values of the function in .NET applications are transferred to the "evaluation stack", which is in protected memory in the process. However, you are talking about a string and that it is a reference type, so there is a pointer to that string location on the heap in the evaluation stack. Heap memory is relatively unsafe because it can be shared, and because it lives until the GC thinks it needs to be collected, unlike grades or call tables, which are highly volatile. But in order to gain access to heap memory, this memory must be shared, and your attacker must have an application with OS and CLR permissions to access that memory, and that knows where to look.
There are much simpler ways to get the plaintext password from the computer, if the attacker has such access. A keylogger can watch the password entered, or another snooper can watch the actual handle to the unmanaged side of the GDI and see that the text field that is actually displayed in the Windows GUI receives the plaintext value (it only gets confused on the display). All this, without even trying to crack the protection of .NET code or protected memory.
If your attacker has that control, you lose. Therefore, it must be the first line of defense; make sure that there is no such malicious program on the client computer, and that the instance of your client application that the user is trying to log into has not been replaced by a cracked appearance.
As for obfuscating passwords between instances, if you are worried about mem-snooping, a symmetric algorithm like Rijndael is not protection. If your attacker can see the memory of the client computer, he knows the key that was used for encryption, because your application will need to know it in order to decrypt it; it will either be hardcoded to the client application, or it will be stored next to the secure string. Again, if your attacker has this kind of control, you are lost if you are doing client-side authentication.
Instead, I would use the service level on a physically and electronically protected machine to provide any functionality of your application that would be harmful to you if the attacker uses it incorrectly (first of all, data search / modification). This service level can be used both for authentication, and for authorization of the user to perform what the client application will allow.
Consider the following:
- The user enters their credentials into your client application. These credentials may be the same as the AD credentials, but they will not be used as such. The only way to prevent a keylogger or other malware from appearing is to make sure that such malware does not exist on your computer by providing good AV software.
- The client application connects to your service endpoint through WCF. The endpoint can be signed with an X.509 certificate; not NSA level security, but at least you can be sure that you are talking to a server under your control.
- The client application then hashes your user password with something that creates a large digest, such as SHA-512. This in itself is unsafe; it's too fast, and the entropy of your user password is too low for the attacker to crack the hash. However, again, they must have control of the computer in order to see the hash, and we are going to confuse it even more.
- The client application transmits the username, password, and hardware identifier of the client computer through the WCF channel.
- The server receives these credentials. Note that the server does not receive the plaintext password; this is for some reason.
- The server disables the hashed password in 256-bit halves. The first half is what was encrypted in BCrypted (using an implementation configured to be accordingly slow, 10 or 11 rounds usually do this) and is compared with a hashed value in the user database. If they match, the DB returns AD user credentials that were symmetrically encrypted with the other half of the password hash. This is why a plaintext password is never sent; the server should not know about this, but the attacker should get something meaningful from the stolen copy of the user database.
- The server decrypts the AD credentials, sends them to AD, and receives an IPrincipal representing this user ID and security context. The IPrincipal implementation will contain null information that can be used to crack a user account.
- The server generates a cryptographically random 128-bit value, combines a 128-bit hardware GUID and hashes it using SHA512. He used half of this hash to symmetrically encrypt the key value that was used to decrypt AD credentials. He then decrypts the other half and stores this hash next to the encrypted key.
- Then the server transmits three pieces of information over the secure WCF channel; IPrincipal created by AD, an undefined 128-bit random value ("transfer token") and another cryptographic random value of arbitrary length ("session token").
- The client application is now authenticated on the client side, which means that you can control user access to the code by polling IPrincipal membership for the AD role, and now the server is sure that the user who has the session token is a real user. (data retrieval / storage) the client must use the coordinated WCF channel and transmit its session token. The combination of the WCF channel and the session token is one-time and unique; using the old token on the new channel or transmitting the wrong token on the same channel indicates that the session has been compromised. First of all, none of the persistent data stored anywhere on any client or server can be used to obtain AD credentials and authentication.
Now, when your client application closes, all "session state" is lost between the client and server; Session token is invalid for any other negotiated channel. So, you have lost authentication; the next client that connects can be anyone, regardless of who they are. There is a "transfer token":
- A “transfer token” is a free pass back to the system. It immediately expires if it is not used 18 hours after its release.
- When closing the client application, it transfers two pieces of information to the new instance (however, it decides to do this); registered user username and "transfer token".
- A new instance of the client application accepts these two pieces of information, and also receives the hardware identifier of the client machine. It negotiates a secure connection with the WCF service and conveys these three pieces of information.
- If the user last registered more than 18 hours ago (not 24 hours, so they cannot appear a minute before they did it yesterday and restarted the application), or if you want to be really paranoid, more than 8 hours ago the application immediately returns an error in which the transfer token for this account is out of date.
- The service accepts the transfer token, combines the hardware identifier, SHA-512s, half of the BCrypts, and compares the result with the stored second verification value. Only the correct combination of the transfer token and the machine that last entered the system will produce the correct hash. If it matches, the other half of the hash is used to decrypt the key, which then decrypts the AD information.
- The service then works as if the user provided the application password hash, decrypted the AD information, retrieved the IPrincipal, creating a new transfer token, session token, and re-encrypting the key for the AD data.
- If any part of this process fails (trying to use the wrong token, including using the same token twice, or using the token from another computer or another user), the service reports that the credentials are invalid. Then the client application will return to the standard user password check.
Here rub; this system, relying on a secret password that is not stored anywhere except the user's mind, does not have back doors; administrators cannot get the lost password for the client application. In addition, AD credentials, when they need to change, can only be changed from the client application; the user cannot be forced to change his password by AD himself when entering Windows, since this will destroy the authentication scheme they need to enter the client application (the encrypted credentials will no longer work, and the credentials of the client application needed to re-encrypt new ones) . If you somehow managed to intercept this check inside AD, and the client application credentials are AD credentials, you can automatically change the credentials in the user application, but now you use one set of credentials to obfuscate the same set of credentials, and if this mystery were known, you would be thrilled.
Finally, this version of this security system functions solely on one principle; that the server is not currently hacked by an attacker. Someone might log in, download offline data, and they’re stuck; but if they can set something to monitor memory or traffic, you will be closed, because when credentials (hash username / password or transfer token / hardware identifier) are entered and verified, the attacker now has a decryption key AD user. Usually it happens that the client never sends the decryption key, but only the control half of the hashed password, and then the server sends back the encrypted credentials; but you think that the client poses a greater security risk than the server, therefore, while this is true, it is best to store as little as possible plain text on the client for any period of time.