1

For a project I'm working on, I need to securely send commands between servers. These commands should not be able to be altered, and there should be some security about not being able to just keep resending the request.

This is what I've come up with:

public static function encryptCommand($command) {
    $time = microtime(true);
    $command .= ':' . $time;
    $encryptedCommand = Security::encrypt($command, Configure::read('key'));
    $hash = Security::hash($time);

    return array(
        'command' => $encryptedCommand,
        'hash' => $hash
    );
}

public static function decryptCommand($data) {
    $encryptedCommand = $data['command'];
    $decryptedCommand = Security::decrypt($encryptedCommand, Configure::read('key'));

    $time = substr($decryptedCommand, strrpos($decryptedCommand, ':') + 1);

    if (Security::hash($time) != $data['hash']) {
        throw new SecurityException('The data has been tampered with.');
    }

    if (microtime(true) - $time > 5) {
        throw new SecurityException('Message arrived too late.');
    }

    return substr($decryptedCommand, 0, strrpos($decryptedCommand, ':'));
}

The idea is to append the current time to the command, encrypt it, and send it together with a hash of the time. At the receiving side, I can decrypt the command, compare the sent hash with a new calculation of the time hash in the decrypted command, and check if not too much time has passed.

Is this a good approach, am I missing something? Should I do this differently?

2 Answers 2

3

If someone manages to catch a request like this it may be possible to resend the request numerous times in your five second frame.

Now the question is how to prevent this. There are more options, the most simple one seems to be, that you add a unique ID to each command, and the recieving server stores them in a way (like a table) and executes your commands only if it is the first time this unique ID arrives. This way each command will be executed one time only. You can additionally use the time and store the unique IDs only for 24 hours, so your table (or whatever) doesn't get too large. In this case, you only have to check if the timestamp with the message is from the last 24h and the unique ID is unique in the last 24 hours. This way you also won't have problems if the clocks of your servers are not in perfect sync.

Edit

I don't know how many servers you have but with this concept it looks like it is also possible to catch a request to one server and send it to all your other servers. If so, you should add the name of the server you want to give a command.

0

The simplest way to prevent replay attacks is to assign a unique and increasing serial number to each command (always increasing) and have the server remember what was the last serial it received. It will then refuse any new serial that isn't strictly bigger than the previously recorded one.

You might need to maintain a list of last seen serial per source, though, unless you have a way to synchronize these numbers globally

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.