Skip to main content
🔐 Using Environment Variables a Little More Securely
  1. Posts/

🔐 Using Environment Variables a Little More Securely

·606 words·3 mins·
Security Guide Cryptography
May Meow
Author
May Meow
MayMeow is a developer and cybersecurity enthusiast with a passion for cryptography, DevSecOps, and open-source contributions. They enjoy creating tools that strengthen digital security, blending creativity and technology to innovate in fields like PHP and .NET. Always exploring new frontiers in tech, MayMeow is dedicated to safeguarding the digital landscape through their work.
Table of Contents

Storing credentials in text files is not such a smart idea. A few days ago I found a post somewhere on reddit where people were discussing a more secure way to use environmental variables for restic. (By default they are stored as plain text).

When I tried to look for a solution for this, I found another post where people talked that you can customise restic code and build it yourself. But who wants to do that?

I wanted something more general. And came up with this. If you are on Windows you can use the Data Protection API more info here.

The data is then encrypted with a machine key. You just have to store that encrypted data somewhere. (for example in the registry)

Encrypting and decrypting data
#

In this example I am using DataProtectionScope.LocalMachine, which means the data can be decrypted by any process running on the same machine where the data was encrypted.

Another options is to use DataProtectionScope.CurrentUser, which means only the process running under the user who encrypted the data can decrypt it.

Your data can be encrypted like this

 byte[] encryptedData = ProtectedData.Protect(
        System.Text.Encoding.UTF8.GetBytes(value),
        optionalEntropy: null,
        scope: DataProtectionScope.LocalMachine
        );

    string encryptedString = Convert.ToBase64String( encryptedData );

and decrypted as easy as this

byte[] encryptedData = Convert.FromBase64String(encryptedString);
	byte[] decyptedData = ProtectedData.Unprotect(
			encryptedData,
			optionalEntropy: null,
			scope:  DataProtectionScope.LocalMachine
			);

Using data as environment variable
#

Now that you know how to encrypt, store and read data, you can use it as an environment variable when invoking external binary.

Environment.SetEnvironmentVariable("VAR1", var1);

and because you want to use environment variables, change UseShellExecute to false, which means that the process you are starting will not use the operating system shell to start the process. Instead, it will start the process directly. This allows you to redirect the input, output and error streams of the process.

 if UseShellExecute is set to true, the new process will start in a new shell, and it will not have access to the environment variables set in the parent process.

ProcessStartInfo psi = new ProcessStartInfo
{
    FileName = batFilePath,
    RedirectStandardError = true,
    RedirectStandardOutput = true,
-    UseShellExecute = false,
+    UseShellExecute = false,
    CreateNoWindow = true,
};

start new process for your application

using (Process process = new Process { StartInfo = psi})
{
    process.Start();
    string output = process.StandardOutput.ReadToEnd();

    process.WaitForExit();

    Console.WriteLine("Microsoft Data protection API example");
    Console.WriteLine("---> " +  output);
}

For testing purposes you can create bat file like that:

@echo off
echo The value of MY_VARIABLE is: %VAR1%

If you run it as a standalone, you will not seed the variable value as this is only set under the process that your bat file is running from.

The above solution displays output after the process has finished. This is not ideal for time-consuming processes from which you want to see output. A possible solution could be as follows

using (Process process = new Process { StartInfo = psi})
{
+	process.OutputDataReceived += (sender, args) => Console.WriteLine("---> " + args.Data);

    process.Start();
+	process.BeginOutputReadLine();
+	process.BeginErrorReadLine();
-    string output = process.StandardOutput.ReadToEnd();

+	Task processTask = Task.Run(() => process.WaitForExit());
+	await processTask;
-    process.WaitForExit();

-    Console.WriteLine("Microsoft Data protection API example");
-    Console.WriteLine("---> " +  output);
}

This is a bit more secure solution at least better than storing them just like that into text file.

However remember that the data encrypted with DataProtectionScope.LocalMachine scope can be accessed aby anyone who can use your computer.

And if you decide to use DataProtectionScope.CurrentUser, it is probably a good idea to create another password protected user which will only be used to run your binary and/or provide an admin account to the necessary minimum of people.

Reply by Email

Related

🤔 Privacy Matter but What About Security?
·282 words·2 mins
Opinions Security
🔐 Use 2FA Everywhere
·810 words·4 mins
Cybersecurity Security
How to Install and Configure Samba
·432 words·3 mins
Administration Notes Guide Wiki File Sharing Linux Windows