Tag Archives: Security

Enabling and Enforcing HTTPS on a Subdomain with cPanel

Nowadays, there’s really no excuse not to enable HTTPS on a website, even a small personal one. It’s free and simple. In fact, chances are that whatever host you’re using offers a simple option you can just turn on. In this article, we’ll see how to set this up in cPanel, which is commonly used in Linux/PHP/MySQL web hosting services.

Set up the Subdomain

Subdomains service in cPanel

If you haven’t already, create a subdomain. To do this:

  1. Locate the Subdomains service in cPanel.
  2. Enter a name for the subdomain.
  3. Enter a path to a folder to be used as the document root for the subdomain.
  4. Click the Create button.

Enable HTTPS on the Subdomain

Let’s Encrypt™ SSL service in cPanel

New subdomains will by default run on HTTP, which is insecure. Enabling HTTPS requires an SSL or TLS certificate. To set this up:

  1. Locate the Let’s Encrypt™ SSL service in cPanel.
  2. Scroll towards the bottom of the page, and page through your subdomains until you locate the new one you want to apply HTTPS to.
  3. Click on the Issue action link next to it.
  4. Leave the settings as they are and click on the Issue button.

Enforce HTTPS on the Subdomain

Domains service in cPanel

Enabling HTTPS is only half good if people can still access the site insecurely over HTTP. It’s very easy to automatically redirect people from the HTTP endpoint to HTTPS. To do this:

  1. Locate the Domains service in cPanel.
  2. Locate the new subdomain, which may be on a different page.
  3. Turn on the switch in the Force HTTPS Redirect column.
  4. A success message should confirm that it’s been enabled.

Test the Subdomain

The subdomain is secure and running on HTTPS

To make sure everything is set up correctly, use a browser to ensure that the website at your subdomain is secure.

  1. Wait a few seconds. The redirect you just enabled might not kick in right away.
  2. Use an incognito session in your browser. Otherwise, if you visited the subdomain before enabling the redirect, it’s possible that the browser might still show it as insecure.
  3. Access your domain with the URL starting with https://. Ensure that your browser displays the padlock icon and reports the connection as secure.
  4. Access your domain with the URL starting with http://. Once the page loads, ensure that you are now on https:// and that the browser displays the padlock icon and reports the connection as secure. Optionally, you can also open your browser’s dev tools, switch to the Network tab, and observe a 301 redirect request.

Summary

As you can see, it’s super easy to get HTTPS working on a subdomain in cPanel. Just enable HTTPS for the subdomain, force the HTTPS redirect, and you’re done.

Securing PowerShellGet on a Windows EC2 Instance

I’ve been doing some work with security on AWS recently, and part of that involved running security assessments using Amazon Inspector to identify vulnerabilities at network and host level.

If I launch a fresh EC2 instance right now using the Microsoft Windows Server 2019 Base AMI and run a host-level assessment, the report lists a vulnerability related to the PowerShellGet module:

Microsoft Security Response Center’s entry about this vulnerability explains a little more about it:

“A security feature bypass vulnerability exists in the PowerShellGet V2 module. An attacker who successfully exploited this vulnerability could bypass WDAC (Windows Defender Application Control) policy and execute arbitrary code on a policy locked-down machine.

“An attacker must have administrator privileges to create a configuration that includes installing PowerShellGet V2 module onto a machine from the PowerShell Gallery. The WDAC policy must be configured to allow the module to run. After this is done, PowerShell script can be injected and run fully trusted, allowing the attacker arbitrary code execution on the machine.”

— CVE-2020-16886 at MSRC

The same page says that this vulnerability was fixed in PowerShellGet v. 2.2.5. So why do we have this problem? Here’s why:

PS C:\Users\Administrator> Get-Module PowerShellGet -ListAvailable


    Directory: C:\Program Files\WindowsPowerShell\Modules


ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     1.0.0.1    PowerShellGet                       {Install-Module, Find-Module, Save-Module, Upda...


PS C:\Users\Administrator>

That AMI came with PowerShellGet 1.0.0.1, but we need version 2.2.5. We can install it by running a Powershell session in Administrator mode, and running the following commands (from the Installing PowershellGet documentation) and agreeing to install the NuGet provider:

Install-Module -Name PowerShellGet -Force
Update-Module -Name PowerShellGet

This results in the new 2.2.5 version being installed alongside the older 1.0.0.1 one:

A Powershell session showing how we started with PowerShellGet 1.0.0.1, installed a more recent version, and now have the new 2.2.5 version alongside the old one.

I don’t know enough to be able to say whether having that version 1.0.0.1 around still poses any kind of risk, but it seems to be enough for Amazon Inspector which no longer reports any vulnerability after installing version 2.2.5:

If you’re really paranoid, check out this Stack Overflow question for ways to get rid of the old version manually. I haven’t actually tried this, so be careful.

Removing the Server Header in ASP .NET Core

There are many aspects to web security, but in this article we’ll focus on one in particular. Attackers can use any available information about a target web application to their advantage. Therefore, if your web application is sending out headers revealing the underlying infrastructure of your web application, attackers can use those details to narrow down their attack and attempt to exploit vulnerabilities in that particular software.

Let’s create a new ASP .NET Core web application to see what is returned in the headers by default:

mkdir dotnet-server-header
dotnet new web
dotnet run

This creates a “Hello world” ASP .NET Core application using the “ASP .NET Core Empty” template, and runs it. By default it runs on Kestrel on port 5000. If we access it in a browser and check the response headers (e.g. using the Network tab of the Chrome Developer Tools), we see that there’s this Server header with a value of Kestrel. If it were running under IIS, this value might have been MicrosoftIIS/10.0 instead.

Honestly, this could be worse. Older versions of ASP .NET running on the old .NET Framework used to add X-Powered-By, X-AspNet-Version and X-AspNet-MvcVersion headers with very specific information about the underlying software. While this information can be really useful for statistical purposes (e.g. to identify the most popular web servers, or to identify how prevalent different versions of ASP .NET are), they are also very useful for malicious purposes (e.g. to look for known vulnerabilities in a specific ASP .NET version).

ASP .NET Core, on the other hand, only adds the Server header, which is quite broad. However, the less information we give a potential attacker, the better for us.

There is no harm in removing the Server header, and to do this in ASP .NET Core, we can take a tip from this Stack Overflow answer:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>()
                              .UseKestrel(options => options.AddServerHeader = false);
                });

The highlighted line above, added to Program.cs, has the effect of getting rid of that Server header. In fact, if we dotnet run again now, we find that it is gone:

Update 27th July 2020: Just to clarify, this removes the Server header coming from Kestrel. However, if you use other software (e.g. IIS) to host your web application, you will need to take additional steps to remove it from there as well.

It is always a good idea to do a vulnerability assessment of your web application, and in doing so, remove any excess information that complete strangers do not need to know. What we have seen here is a very small change that can reduce the security risk at least by a little.

Encrypting Strings in C# using Authenticated Encryption

Encryption is fundamental and ubiquitous. Whether it’s to prevent sensitive settings (such as passwords and API tokens) from falling into the wrong hands, or making sure no one listens in on confidential communications, encryption is extremely important. Many people do not even realise that they use it every day.

Encrypting data using the .NET Framework or .NET Core libraries, however, is not trivial. There are different ways to encrypt and decrypt data, and sometimes this requires some knowledge about the underlying algorithm.

To keep things really simple, we’ll use a third party library that provides a simple interface for encryption and decryption. Because this library uses strings and byte arrays, it is not suitable for encryption of large amounts of data, such as huge files, which would bloat the application’s memory. However, it is perfectly fine for small strings.

Later in the article, I also share a simple tool that I built to help generate keys and test encryption and decryption. You can find this tool under the AuthenticatedEncryptionTester folder in the Gigi Labs BitBucket repository.

Using AuthenticatedEncryption

AuthenticatedEncryption is a library that provides simple methods for encryption and decryption:

“The library consists of a single static class. This makes it very easy to use. It uses Authenticated Encryption with Associated Data (AEAD), using the approach called “Encrypt then MAC” (EtM). It uses one key for the encryption part (cryptkey) and another key for the MAC part (authkey).”

All we need to start using this is to install the corresponding NuGet package, either using the Package Manager Console:

Install-Package AuthenticatedEncryption

…or using the .NET Core command line tools:

dotnet add package AuthenticatedEncryption

The project’s readme file (which is the first thing you see in the GitHub repo) explains how it’s used, and it is really simple. First, you generate two keys, called the cryptkey and authkey respectively:

var cryptKey = AuthenticatedEncryption.AuthenticatedEncryption.NewKey();
var authKey = AuthenticatedEncryption.AuthenticatedEncryption.NewKey();

This is something you will typically do once, since you have to encrypt and decrypt using the same pair of keys.

Next, we need something to encrypt. We can get this from user input:

Console.Write("Enter something to encrypt: ");
string plainText = Console.ReadLine();

We can now encrypt the plain text by using the keys we generated earlier:

string encrypted = AuthenticatedEncryption.AuthenticatedEncryption
    .Encrypt(plainText, cryptKey, authKey);
Console.WriteLine($"Encrypted: {encrypted}");

And we can also decrypt the cipher text using a similar mechanism:

string decrypted = AuthenticatedEncryption.AuthenticatedEncryption
    .Decrypt(encrypted, cryptKey, authKey);
Console.WriteLine($"Decrypted: {decrypted}");

You will by now have noted the double AuthenticatedEncryption that is constantly repeated throughout the code. This is a result of the unfortunate choice of the library author to use the same for the class and namespace. There is already an open issue for this.

Update 20th July 2020: this syntactical problem was recently fixed by renaming the class. As from version 2.0.0, once you have your using AuthenticatedEncryption;, you can call the relevant methods directly on the static Encryption class, such as Encryption.NewKey().

Let’s run this code and see what happens:

Simple encryption and decryption using the AuthenticatedEncryption library. Running on Kubuntu 19.10 using .NET Core.

As you can see, the input string was encrypted and the result was encoded in base64. This was later decrypted to produce the original input string once again.

Authenticated Encryption Tester

To facilitate key generation as well as experimentation, I wrote this small tool:

Authenticated Encryption Tester. A simple tool to quickly use the functions of the AuthenticatedEncryption library.

This lets you use the AuthenticatedEncryption library functionality that we have just seen in the previous section. It’s useful to initially generate your keys, and also to test that you are actually able to encrypt and decrypt your secrets successfully.

It is a WPF application running on .NET Core 3, so unlike the AuthenticatedEncryption library, unfortunately it only works on Windows. However, for those of you who, like me, have the misfortune of already using Windows, it can turn out to be a handy utility.

You can get the code from the AuthenticatedEncryptionTester folder in the Gigi Labs BitBucket repository. While I won’t go through all the code in the interest of brevity, I’d like to go through some parts and show that it’s doing pretty much what we’ve seen in the previous section.

        private void GenerateCryptKeyButton_Click(object sender, RoutedEventArgs e)
            => GenerateKeyInTextBox(this.CryptKeyField);

        private void GenerateAuthKeyButton_Click(object sender, RoutedEventArgs e)
            => GenerateKeyInTextBox(this.AuthKeyField);

// ...

        private void GenerateKeyInTextBox(TextBox textBox)
        {
            string key = AuthenticatedEncryption
                .AuthenticatedEncryption.NewKeyBase64Encoded();
            textBox.Text = key;
        }

The first two fields in the window expect to have the two keys in base64 format. You can either use keys you had generated earlier and stored, or you can hit the Generate buttons to create new ones. These buttons create new keys using the NewKeyBase64Encoded() method, which is just like NewKey() except that it returns a base64-encoded string instead of a byte array. This is handy in situations where you want a string representation, such as in a GUI like this.

Encryption and decryption also work just like in the previous section, and the implementation merely adds some extra code for validation and I/O. This is the method that runs when you click the Encrypt button:

        private void EncryptButton_Click(object sender, RoutedEventArgs e)
        {
            const string operation = "Encrypt";

            string cryptKeyBase64 = this.CryptKeyField.Text;
            string authKeyBase64 = this.AuthKeyField.Text;
            string plainText = this.PlainTextField.Text;

            try
            {
                if (string.IsNullOrWhiteSpace(cryptKeyBase64)
                    || string.IsNullOrWhiteSpace(authKeyBase64)
                    || string.IsNullOrWhiteSpace(plainText))
                {
                    ShowWarning("Both keys and the plain text must have a value.",
                        operation);
                }
                else
                {
                    byte[] cryptKey = Convert.FromBase64String(cryptKeyBase64);
                    byte[] authKey = Convert.FromBase64String(authKeyBase64);

                    string cipherText = AuthenticatedEncryption
                        .AuthenticatedEncryption.Encrypt(plainText, cryptKey, authKey);
                    this.CipherTextField.Text = cipherText;
                }
            }
            catch (Exception ex)
            {
                ShowError(ex, operation);
            }
        }

The Encrypt button takes what’s in the Plain Text field and puts an encrypted version in the Cipher Text field. The Decrypt button does the opposite, taking the Cipher Text and putting the decrypted version in the Pain Text field. The code for the Decrypt button is very similar to that of the Encrypt button so I won’t include it here.

One thing you’ll note as you experiment with this is that the encrypted output string changes every time. This is an expected behaviour that provides better security. By clearing the value in the Plain Text field before hitting Decrypt, you can verify that it is always decrypted correctly to the original input string, even with different encrypted values.

Summary

The AuthenticatedEncryption library is great for encryption and decryption of simple strings. For large amounts of data, you should instead use streams together with the cryptographic APIs available in the .NET Framework or .NET Core.

You can use my Authenticated Encryption Tester to generate keys or experiment with encryption and decryption using the AuthenticatedEncryption library. It is built on WPF so it only works on Windows.

Using Time-Based One-Time Passwords for Two-Factor Authentication

Introduction

Two-factor authentication (2FA) is becoming more and more important, as its adoption is driven by a need for major software companies to secure their systems against threats, as well as due to legal requirements of strong customer authentication, such as the PSD2 directive that came in force in Europe last month.

2FA can be implemented in a number of ways. Typically, it is a combination of the usual username/password login as well as something else, often being a one-time password (OTP) that is sent via SMS or email, or generated by an algorithm.

In this article, we’ll focus entirely on generating and verifying Time-Based One-Time Passwords (TOTP) using Google Authenticator and the Otp.NET library.

Update 20th October 2019: This also works if you use Microsoft Authenticator instead of Google Authenticator. Microsoft Authenticator requires more permissions on your device, sends usage data to Microsoft by default, and is slightly more confusing because you have to choose the type of account.

Update 22nd October 2019: I discovered another mobile app called Authy, and it works just as well to acquire the TOTP secret and generate codes. It is interesting because it has a mechanism to take encrypted backups in the cloud and synchronise across devices, addressing the problem of when you lose or change your phone.

About TOTP

TOTP is an algorithm used to generate one-time passwords based on a shared secret and the current time. It is defined in RFC6238, and is a variant of the HOTP algorithm (RFC4226) which uses a counter instead of time.

The client and server use the same algorithm, the same shared secret and (roughly) the same time to generate the same code.

TOTP can be thought of as a function that takes the shared secret and current time as inputs, and generates a one-time password as output. Given that the client and server both know the same shared secret, and that their software clocks are more or less in sync without major clock skew, then they would generate the same code. This allows a code generated on a mobile device to be verified on the server side.

Generating a Shared Secret

We will use Otp.NET to perform most operations related to TOTP generation and verification. This can easily be intalled in a .NET (Core) console application via NuGet:

Install-Package Otp.NET

It is then really easy to generate and output a shared secret using the following code:

var secret = KeyGeneration.GenerateRandomKey(20);
var base32Secret = Base32Encoding.ToString(secret);
Console.WriteLine(base32Secret);

The secret that we generated on the first line is an array of bytes. However, we output it in base32 encoding. This is important for the next step when we will pass the secret to the mobile device. As I learned the hard way, it does not work if the secret is an arbitrary string and not base32-encoded.

Running the above, I just got the following in the output:

6L4OH6DDC4PLNQBA5422GM67KXRDIQQP

Generating a QR Code for the Secret

Stefan Sundin made this great 2FA QR code generator. The two required fields are the Secret (where we paste the value generated above) and a Label (which is arbitrary and identifies the application — we’ll simply put “MFA Test 1” in there).

The QR code helps to synchronise the secret between the server and the mobile device.

Setting up Google Authenticator

Find Google Authenticator in your phone’s app store and install it. It requires access to your camera as we’ll see in a second.

Get Google Authenticator from your phone’s app store.

After installation and its brief in-built tutorial, you get to the point where you can set up your first TOTP code generator (they call it an “account”):

To synchronise a shared secret onto your mobile device, you can scan a barcode or type in the secret directly.

This step is where you enter the shared secret into Google Authenticator. You can do that by scanning a QR code (first option), or by typing it in (second option). The latter is slow and painful, especially on a mobile device, and should be kept as a fallback in case there is some kind of problem scanning the QR code. Scanning the QR code is really just a convenience mechanism and is an encoded version of the same secret.

Scan the barcode to get the shared secret into Google Authenticator.

Once you’ve scanned the QR code, Google Authenticator has acquired the shared secret and starts generating TOTP codes every 30 seconds:

Google Authenticator is generating TOTP codes.

Since you can have more than one of these code generators in here (for different applications), they come with a label. In this case, you’ll notice that we have “MFA Test 1”, which is exactly what we entered in the Label field when generating the QR code.

Generating TOTP codes from Otp.NET

If you need to generate TOTP codes from .NET code (essentially to do what Google Authenticator is doing), then Otp.NET makes it very easy to do that:

            string base32Secret = "6L4OH6DDC4PLNQBA5422GM67KXRDIQQP";
            var secret = Base32Encoding.ToBytes(base32Secret);

            var totp = new Totp(secret);
            var code = totp.ComputeTotp();

            Console.WriteLine(code);

The ComputeTotp() method takes an optional DateTime parameter as the current time to use for the code generation algorithm. If not provided, it uses DateTime.UtcNow, which is typically what you want to use.

The TOTP code generated from the C# program (top right) is identical to the one generated from Google Authenticator on my phone (bottom centre).

Since we are using Google Authenticator, we don’t actually need this at all, so this is just something to keep in mind if you ever actually need it. It also gives some assurance that we’re on the right track, because what we’re doing in C# and on the mobile device are evidently well in sync.

Verifying TOTP Codes

Like every other operation we’ve seen, verifying TOTP codes with Otp.NET is also very easy. The following code shows how to do this, although most of the code is actually handling input and output.

            string base32Secret = "6L4OH6DDC4PLNQBA5422GM67KXRDIQQP";
            var secret = Base32Encoding.ToBytes(base32Secret);

            var totp = new Totp(secret);

            while (true)
            {
                Console.Write("Enter code: ");
                string inputCode = Console.ReadLine();
                bool valid = totp.VerifyTotp(inputCode, out long timeStepMatched,
                    VerificationWindow.RfcSpecifiedNetworkDelay);

                string validStr = valid ? "Valid" : "Invalid";
                var colour = valid ? ConsoleColor.Green : ConsoleColor.Red;
                Console.ForegroundColor = colour;
                Console.WriteLine(validStr);
                Console.ResetColor();
            }

Here’s what it might look like while you test it out repeatedly:

A number of tests show interesting results.

As you can see above, I did a number of things:

  1. I entered two invalid codes, and got invalid responses.
  2. I entered a valid code, and got a valid response as expected.
  3. I waited for a new code to be generated, then entered the same code as before, and it was accepted.
  4. I entered the new code that was generated, and it was validated.
  5. I entered another invalid code, and it was marked as such.

The most interesting part of the above is the third step, and it requires further explanation. Codes are generated in time windows, by default every 30 seconds. That doesn’t necessarily mean that the previous code should be rejected. The time window might have shifted just as the user was typing the code, or there could be network delays, etc. Typically, some leeway is allowed when validating these codes. The RFC recommends allowing codes from one time window in the past or future, and that’s what the value of VerificationWindow.RfcSpecifiedNetworkDelay that we passed in as the third parameter to VerifyTotp() does. If you want, you can pass in something different that is more lenient or more restrictive.

On the other hand, accepting the same code twice is wrong, considering we are supposed to be generating one time passwords. In order to make sure that a code isn’t used twice, we need to store something that we can later check to know whether a code has been used. That’s the reason for the second parameter to VerifyTotp(). It gives us back a number indicating the time step used, so we can save this whenever a code is used, and later check whether the same time step has already been used before.

Assuming a single shared secret, a very quick-and-dirty dummy implementation using a HashSet instead of real persistence could look something like this:

            string base32Secret = "6L4OH6DDC4PLNQBA5422GM67KXRDIQQP";
            var secret = Base32Encoding.ToBytes(base32Secret);

            var totp = new Totp(secret);

            var usedTimeSteps = new HashSet<long>();

            while (true)
            {
                Console.Write("Enter code: ");
                string inputCode = Console.ReadLine();
                bool valid = totp.VerifyTotp(inputCode, out long timeStepMatched,
                    VerificationWindow.RfcSpecifiedNetworkDelay);

                valid &= !usedTimeSteps.Contains(timeStepMatched);
                usedTimeSteps.Add(timeStepMatched);

                string validStr = valid ? "Valid" : "Invalid";
                var colour = valid ? ConsoleColor.Green : ConsoleColor.Red;
                Console.ForegroundColor = colour;
                Console.WriteLine(validStr);
                Console.ResetColor();
            }

Sorry about that &amp; in there – the operator is supposed to be &=. There’s a glitch in the editor I’m using and I hope it’ll be sorted out at some point.

Like this, there’s no way you can ever have the same code be valid twice:

The same code, even within the same time window, is invalid the second time.

Conclusion

In this article we’ve seen how Time-Based One-Time Passwords can be generated and verified. We’ve focused mainly on:

  1. Generating a shared secret using Otp.NET
  2. Bringing it to a mobile device with Google Authenticator
  3. Using Google Authenticator to generate TOTP codes
  4. Using Otp.NET to validate these codes

In a two-factor authentication implementation, this is of course only one of the factors, and usually takes place after a regular username/password login.