< /posts/ />
Writing fundamentally secure authentication
2024-10-26

#Overview

Throughout my time reverse engineering software, I have seen a variety of terrible authentication systems. From directly connecting to and querying their database to verify their user's license key, to requesting Google Docs and comparing a list of approved users against the current user, it is clear that writing fundamentally secure authentication may not be as intuitive as we thought! In this post, I will go over the number one fundamental method of securing software.

To clarify, what I mean by authentication in this context is the method in which one secures their desktop application from unauthorized users.

#Rely on the server.

This is probably the most important of them all- if logic can be executed on your server, execute it on your server. Unless you really messed up, nobody can read the code that is on your server.

But Caius! I am using ultra-vm-super-obfuscator! They can't read the code anyways!

Nomatter how secure the client-side is, it can at some point always be cracked. Why waste the energy and performance overhead of an obfuscator when you can just put it on the server, which is significantly more difficult to tamper with?

Compare these two examples, written in C#:

Example #1 (bad):

void Login(string username, string password) {

    switch(Server.Authenticate(username, password)) {
        case 0:
            Console.WriteLine("Your license has expired!");
            Quit();
        case 1:
            Console.WriteLine("You have been banned!");
            Quit();
        case 2:
            Console.WriteLine("You have been authenticated!");
            return;
    }
}

This authentication system seems to have an error code system:

  • If 0, the server determined that user's license has passed the expiration date.
  • If 1, the server determined the user is banned.
  • If 2, the server determined that the user has a valid license.

While this is cool, this system has a fatal flaw...

void Login(string username, string password) {
    return;
}

Oops.

So how can we fix this?

Yes, we could probably make this system more complex. However, relying on security through obscurity is essentially just obfuscating your source code. Bad.

Instead, we will make the client rely on the server. In this example, we will be creating secure authentication for an application that tells you the ingredients you need to make eggs. Here's our app:

    class EggBaker {
        void Main(string[] args) {
            Console.WriteLine("Ingredients required to bake an egg:");
            Console.WriteLine("Egg.");
        }
    }

Ok, so now we want to secure this using the server. To make the client's functionality rely on the server, we will store the ingredients needed to make eggs on the server. Only once we have authenticated the user will we send over the ingredients. Therefore, if a user is not authenticated, the application will not function as intended.

    class EggBaker {
        void Main(string[] args) {
            string response = Login(args[0], args[1]) // username/password
            Console.WriteLine("Ingredients required to bake an egg:");
            Console.WriteLine(response);
        }

        string Login(string username, string password) {
            string response = Server.Authenticate(username, password);

            switch(response) {
                case "0":
                    Console.WriteLine("Your license has expired!");
                    Quit();
                case "1":
                    Console.WriteLine("You have been banned!");
                    Quit();
                default:
                    Console.WriteLine("You have been authenticated!");
                    return response; // "Egg." if server allowed
            }
        }
    }

In this example, the error message codes are still retained. However this time, a default case is used to denote that if it was not a number the user should be authenticated. If we were to do a case "Egg.": instead, it would defeat the entire purpose as now the client knows the value.

If a bad guy bypasses this version, it will not matter. Sure, they will be able to see "You have been authenticated!", but they will not know the ingredients to make eggs.

#Conclusion

In this article, we have talked about a simple way to make your client application rely on your server as a means of fundamental security. In the future, I plan to dive into deeper examples, such as heartbeat, loaders, automatic bans, and something I have been theorizing: undumpable vm-based loaders.

If you have any questions, comments, or iniquiries, do not hesitate to contact me! You can mail me @ [email protected].