Client Server: How to make it more difficult for client to modify their source code

Viewed 108

I have an imaginary program that I've distributed to dozens of clients which involve their home thermostats. The script performs two tasks:

  • When a request from my server is received, the script will modify the temperature of the user's thermostat to the designated temperature.
  • When a request from my server is received, the script will reply back the current set temperature of the thermostat.

The program being ran on the clients computer is programmed in a scripting language and is not compiled. The source code can be modified at any time and the new modified script can be re-ran at will.

I have three problems:

  • What changes can I make to the script running on the client's computer and/or the program running on my server such that I feel more confident that the user did not tamper with the source code of the script?

  • How can I be relatively sure that the user is running the most up-to-date version of my program?

  • Without using IP addresses, how can the client know a request came from the server and not another client?

I understand that code running on a client's PC is impossible to police. However I want to make it less trivial for someone to modify the source code of my script.

2 Answers

As you correctly point out, it's impossible to guarantee nobody modifies your source - or replaces your client with an entirely different one they write from scratch. It simply cannot be done and it's not even because your client is implemented as a script; binaries can be replaced/spoofed as well.

What changes can I make to the script running on the client's computer and/or the program running on my server such that I feel more confident that the user did not tamper with the source code of the script?

Make your script compute a cryptographically secure hash of its own source code and send that hash to the server. You'll know what the hash of your client's source code is and can make sure this is the same. This does not guarantee the client is not pretending to be running your source (they could compute the hash of your client and send it while running entirely other code) but this will prevent casual and/or accidental modifications to the script from working (i.e. it guarantees any spoofing is intentional).

How can I be relatively sure that the user is running the most up-to-date version of my program?

Include a version number in the client source code you distribute so you can guarantee each new version has a unique hash with almost 100% probability. Then, you can have a history of versions with corresponding client hashes.

Without using IP addresses, how can the client know a request came from the server and not another client?

This one you can actually do correctly. Have your server use its private key to sign the messages it sends, and have your client verify the signature using your server's public key (which the client source code can contain). Since only your server could possibly have signed the messages with the correct private key, the client can be confident that success with the corresponding public key means your server sent the message.

If it is a home thermostat, the whole architecture is likely wrong. Your server will not be able to connect to devices on your customers' home networks (or at least they will have to perform configuration that you should not expect to be done).

So in a better architecture, you have your service on the internet, to which your devices connect. Note that these connections can be long ones as well, not necessarily just the typical short-lived http connections.

The client software running on a device can be modified by your users arbitrarily, or they can make other clients, you cannot do much about this. In reality, unless this is very valuable for some reason, nobody will bother, especially in a commercial way, because any change to your api will break 3rd party clients. One option to still make client code harder to modify is obfuscation, but be aware that it is not really a security feature, but it does increase the necessary effort.

Checking the version of the client is straightforward, it can just be sent with requests. However, the usual way to achieve what I think you wanted is to version your API. You probably don't care about the exact version of the client, but you do care about which version of the service (the API) it supports. If that is your goal, look into API versioning.

Authenticating the server in this architecture is straightforward if communication uses TLS. With TLS (eg. https), server authentication is implicit. You still want to authenticate the client though, which can be done via the standard means, a username-password, tokens or a client certificate. When talking about devices, you might want to consider a device with a TPM chip so that it can securely hold secrets, but whether you need that depends on the exact scenario and your threat model (who will have physical access, what happens if they can impersonate other devices and so on).

Related