0

I'm sending an email with an attachment from one server to another server and I would like to find a good way to verify that this email really comes from that server.

The applications are in PHP. I can't verify the origin with for example the IP (server in an Intranet). Could I use a hash in subject or in the body of the email.

flaggalagga
  • 451
  • 5
  • 14
  • 2
    The term to search for here is "digital signature". There are email-specific implementations of this, or generic ones which you could use to sign the attachment directly. – IMSoP Apr 15 '22 at 10:08
  • 1
    you can of course use hashing as long as the receiving server knows whether it is valid or not. – Ersin Apr 15 '22 at 13:00

1 Answers1

1

Generating a hash alone is not enough. What you need is asymmetric cryptography. The first step to implementing this would be to generate a public/private key pair. This is easily accomplished using openssl.

openssl genrsa -out private.key 1024
openssl rsa -in private.key -pubout > public.key

In the message sending script :

  • Generate a hash from the attachment.
  • Use the private key to generate a signature from the hash.
  • Add the signature to the email as a custom header.

For example:

<?php
  $hash = hash_file("md5", "path/to/your.file");
  $key = openssl_pkey_get_private("file://path/to/your/private.key");

  openssl_sign($hash, $signature, $key);
  openssl_free_key($key);

  // build your message and attach the file

  $headers['X-Signature'] = base64_encode($signature);  
  mail($to, $subject, $message, $headers);
?>

In the message reading script :

  • Parse the email file (which is beyond the scope of this question, there are other answers explaining how)
  • Use the public key to check the validity of the signature.

Something like this:

<?php
  require_once('email-parser.php');
  $msg = parse_email_file("path/to/your/message.eml");
  
  $signature = base64_decode($msg['headers']['X-Signature']);
  $hash = md5($msg['attachment']);
  
  $key = openssl_pkey_get_public("file://path/to/your/public.key");
  $verified = openssl_verify($hash, $signature, $key);
  openssl_free_key($key);
  
  if ($verified) {
    // DO STUFF
  } else {
    // PANIC!!!
  }
?>
Besworks
  • 4,123
  • 1
  • 18
  • 34
  • Thank you very much this works very well. However I didn't managed to read the custom header 'X-Signature' in imap so I put the signature in the subject. There is one error in your example : $key = openssl_pkey_get_private("file://path/to/your/private.key"); should be : $key = openssl_pkey_get_private(file_get_contents("file://path/to/your/private.key")); – flaggalagga Apr 26 '22 at 15:58
  • The manual for [openssl_pkey_get_private](https://www.php.net/manual/en/function.openssl-pkey-get-public.php) says that the `public_key` parameter can be either a path to a file or a string containing the key. It worked for me as demonstrated using PHP v8.0.12. – Besworks Apr 26 '22 at 18:59