简体   繁体   中英

How to encrypt and decrypt sensitive information in C# without a user-entered password

Forgive me if this is a stupid and obvious question, but I'm having trouble googling for the correct resources. I'm not a security expert and I'm struggling to understand how to properly go about this.

Here's the scenario. I have an internal application on an internal server: not something that will ever go out to a client site. This application has a database of username and password pairs that are used to talk to secure web services. I have no need to keep these passwords secret from colleagues, but I want to protect them in case the server is attacked and the data stolen.

Traditionally one would salt and hash them. This is a process I understand in principle but it depends on the user entering a password which can then be validated against the stored hash. That's not the case for me.

So: searching around there are various solutions that use a fixed "pass phrase" to secure a string. Here's a one example, https://stackoverflow.com/a/10177020/271907 and here is another https://stackoverflow.com/a/10366194/188474 .

However, as I understand it neither of these offers a useful solution in my case. That "pass phrase" is going to have to be stored somewhere for my application to do its work. If I hard-code it into the application it can be reverse engineered. If I encrypt it and put it in a separate file it can be stolen and worked out using a rainbow table.

I looked into using reg_iis to encrypt a key as per Encrypting Web Config using ASPNET_REGIIS but, to be honest, that just left me even more confused. I'm not even sure whether or not these encrypted config files can be ported between machines or whether I'd have to re-encrypt between dev and test and live. I don't know how secure they are either: AFAIK there has to be a key somewhere and if there's a key it can be broken.

To further muddy the waters I found this answer which doesn't use a key: https://stackoverflow.com/a/10176980/271907 . However the author admits it's out of date and I have no idea how secure the result is.

Is there any kind of sensible approach to solving this problem that doesn't leave a hole in the security somewhere?

Any solution where you decrypt the password to check it is going to fundamentally be insecure because your application must always know how to do that decryption.

You can make it harder by not storing the decryption key in the code, but wherever you put it a hacker that can compromise your code can probably access anything it can access too.

Even if your application security is rock solid; your passwords are still plain text in your DB and if that gets compromised then lots of your users are exposed.

That said - this is an internal-only, low risk system. Your best course of action may be to let your bosses know the relative risk vs the cost of proper security and let them make an explicit business call (and carry any future blame).

The only way of doing this without leaving a hole in the security is by hashing and salting the passwords with a one-way algorithm. The fact that current passwords are plain text shouldn't be a problem - there are lots of ways to push users to encrypt them, but easiest is just to do it for them: next time they log in, if they have a plain text password encrypt it. Then after a suitable wait (depending how often your users log in) remove the old password and check against the new hash.

The golden rule is: if you store passwords you must hash them in a way you can't reverse

The only other option is for you to not authenticate at all - use NTLM or AD or OAuth to get some other service to authenticate the user and just trust that source instead.


If instead you're looking to secure the credentials the application uses itself then you have a similar problem, but the focus shifts. You still can't do much to avoid exposure if the host machine is compromised, but most attacks will only target files.

This can be a problem if all your connection details are held in a web.config or appsettings.json as compromising those files can expose your SQL server or other service passwords.

This is where you can use ASPNET_REGIIS - it lets you add secret configuration that IIS can access, but that isn't held in plain text with the web files.

In .NET core there's new Microsoft.Extensions.SecretManager.Tools that do the same thing.

Both of these add a layer of protection for any application credentials that would otherwise be stored in plain text on disk. Attackers must compromise the machine to get at them, rather than just the files.

In both cases these configuration details are no longer portable - you'll have to set them up again (and re-encrypt) on each server.

However, you only really need the additional protection in live - in development or testing sandboxes you can just use the plain text config, and then override the details with the encrypted settings on the live server.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM