avoid re-reading pem file while creating openssl signatures - php

I am using a simple php code similar to the standard openssl_pkcs7_sign() example to sign many mails in a loop
http://php.net/manual/en/function.openssl-pkcs7-sign.php
The problem is this code is too I/O intensive. This reads the same certificates and calls the openssl functions in a loop.
Can I just read the certificate in memory and use for signing everytime
This is the relevant part of the script
<?php
$headers = array( "From: Ram <ram#netcore.co.in>", "Subject" => "Signed mail");
/* All the recipients who will get the mail */
$users = array ( 'ram#netcore.co.in' , 'aa#netcore.co.in','bb#netcore.co.in');
foreach ($users as $rcpt){
$headers['To'] = $rcpt;
openssl_pkcs7_sign($rcpt."infile.txt",
"outfile.txt","file://noPassPrivatePublicKey.pem",
array("file://noPassPrivatePublicKey.pem", ""),
$headers);
/* Now make an smtp connection and send the mail */
send_mail_to_smtp($from,$rcpt,"outfile.txt");
}
?>

Sign the mail once and send it to all recipients. S/MIME signs the mail body, only. So setting the To: header afterwards won't invalid the signature.

Related

Send email reliably and handle errors robustly

I'm making my first site for a learning exercise as I've been a java back-end developer. I am setting up a user registration form and since this is going to be my user's first glimpse at my site, I want to make sure I handle things as robustly as possible. Through some trials I've come across and implemented almost all these solutions:
php's mail() function - this worked the first day i tested it and stopped working later. First time I realized sending mail wasn't a "given" simple task
pear mail class - implemented this tonight and is currently still working
pear smtp mail - read about this here. Makes me feel like I should be using smtp?
At this point I have realized sending email reliably is not quite as trivial as I originally thought. My question is what is the most reliable way to send mail, and what is the most robust way to handle exceptions? For instance if SMTP is the most reliable way, please explain why and provide a simple example with error handling.
For any of the errors that occur, are they errors where doing some automated retry would benefit? I understand that just because I send mail doesn't mean the person will get it, but I'm asking what the most robust solution is because I'm sure other people have done this 100 times over.
To prove I'm not just a lazy coder, this is what I've got so far which has been working - but I have no idea how robust this actually is (pear mail):
<?php } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
include('Mail.php');
include('Mail/mime.php');
$to = urldecode($_POST['email']);
if (preg_match('(\r|\n)', $to)) {
die('No email injection for you!');
}
$headers = array(
'From'=>'tag <me#mysite.com>',
'Subject'=>'Registration for mysite.com'
);
$text_body = 'boring text message';
$html_body = '<html>
<head><title>Welcome</title></head>
<body>
<p>slightly less boring message</p>
</body>
</html>';
//Utilize the mime class to generate mime body and add mime headers
$mime = new Mail_mime();
$mime->setTXTBody($text_body);
$mime->setHTMLBody($html_body);
$body = $mime->get();
$headers = $mime->headers($headers);
//Utilize the mail class to send the mime mail
$mail = Mail::factory('mail');
$mail->send($to, $headers, $body);
echo 'mail sent maybe?';
?>
EDIT:
Code sample using SMTP with error handling
<?php } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
include('Mail.php');
include('Mail/mime.php');
$to = $_POST['email'];
$to = urldecode($to);
if (preg_match('(\r|\n)', $to)) {
die('No email injection for you!');
}
$headers = array(
'From'=>'tag <me#mysite.com>',
'Subject'=>'Registration for mysite.com'
);
$text_body = 'boring text message';
$html_body = '<html>
<head><title>Welcome</title></head>
<body>
<p>slightly less boring message</p>
</body>
</html>';
//Utilize the mime class to generate mime body and add mime headers
$mime = new Mail_mime();
$mime->setTXTBody($text_body);
$mime->setHTMLBody($html_body);
$body = $mime->get();
$headers = $mime->headers($headers);
//Utilize the mail class to send the mime mail
$host = 'mail.mysite.com';
$port = '26';
$username = 'me#mysite.com';
$password ='myPassword';
$smtp = Mail::factory('smtp', array (
'host' => $host,
'port' => $port,
'auth' => true,
'username' => $username,
'password' => $password
));
$mail = $smtp->send($to, $headers, $body);
if (PEAR::isError($mail)) {
echo("<p>" . $mail->getMessage() . "</p>");
} else {
echo("<p>Message successfully sent!</p>");
}
?>
There are typically several steps involved in sending mail:
Application: Put mail into queue of local delivery agent
Delivery agent: Send mail to configured SMTP server
SMTP server: Send mail to destination mail server
User mail application: Fetch mail from mail server
User: Click on mail, read it
PHP's mail() function puts the mail into the queue of the local mail delivery agent on unix. You'll only get an error (return value false) if that does not work. You do not get notified when the agent cannot deliver the mail further or any of the steps 2-5.
Using a direct SMTP connection to your SMTP server at least gives you an if the mail cannot be delivered to your SMTP server, which is more information than you get with mail(). What you don't get is information if the mail does not get read or is simply filtered out into a spam folder, or if the remote mail account does not exist (3-5).
To get to know that the remote account exists, you need to either parse the error response mails ("Undelivered mail returned to sender), or implement the full remote server SMTP connection and sending yourself (step 3), which I would not recommend.
To find out if the mail has been read, you could embed a "web bug", a tiny (potentially clear) image that is displayed in the HTML mail and notifies you that the mail has been displayed. You can use this to put sent mails into a database and mark them as read when your web bug image URL gets called. Mails that did not get read in X days can be seen as "not read" or "failed" - but the user can also simply be on vacation :)
Reliability & robustness
Your own mail server (step 3) automatically tries to re-send mails when the remote user's mail server is down. If that does not work, you'll get mails like "Mail delivery delayed for 24 hours", and another mail when it stopped doing that.
So once the mail is on your mail server, you can be sure that this server will do everything it can to deliver it.

what's the issue with Message-Id in email sent by php

I have suspicious message-id header of email sent by php to gmail account:
Message-Id: <5100054f.a489440a.5d93.6a70SMTPIN_ADDED_MISSING#mx.google.com>
Could you please tell does it have this strange format and what SMTPIN_ADDED_MISSING means here? Examples I saw in the internet had format something like this containing sending domain but my message id doesn't contain it for some reason:
38D1C1FD-3C35-4568-925C-FC46CAC0DE8A#sendinghost.com
I don't think I set this header in Zend_Mail. What generates this headers? Do you see any issues with this header?
A proper outbound email client should be generating the Message-ID header when the email is sent. Google is being 'nice' and generating it for you when the message passes through its email system, but most won't, and most spam filters will take this missing header as an indication that the message is more likely to be spam. Any malformed or missing headers will add to the "spam score".
It is not difficult to generate, all that is required is that it is unique per-message:
$message-id = time() .'-' . md5($sender . $recipient) . '#' $_SERVER['SERVER_NAME'];
Or
$message-id = time() .'-' . md5($sender . $recipient) . '#yourdomain.com';
Gives:
1358961017-677533f745f613447d06de25e7fa4d32#yourdomain.com
Google SMTP generates it if missing. This header must be set by the first SMTP server. So you do not generate it - google does. It is used to prevent multiple delivery and to link related messages together.
It is not required to set message id header, but it's like a good practice for most (but not all, only configured) smtp to add (may be fix) this header. So to avoid generation of this header by others you can generate it by yourself.
This one works for me (I also added a 'Date' line to the header because it was a spam issue to me as well). Based on this peace of code.
Here's my PHP array approach (using Pear's Mail and Mime libraries):
$headers = array(
'From' => $from,
'Subject' => $subject,
'To' => $to,
'Cc' => '',
'Date' => date('r'),
'Message-ID' => sprintf("<%s.%s#%s>",
base_convert(microtime(), 10, 36),
base_convert(bin2hex(openssl_random_pseudo_bytes(8)), 16, 36),
'yourdomain.com')
);
Note that using $_SERVER['SERVER_NAME'] instead of literally 'yourdomain.com' won't work in php cli as commented out by Oleg on another answer.
I am using same MessageId to track the exchanged messages.
I fix the MessageId with:
$mail->MessageID =sprintf('<%s#%s>', $myMessageID, 'myserver');
tl;dr; Do not use port 25 when sending email instead use port 587
When I was sending outbound email from my custom created golang email client using port 25 to my local postfix server with destination email address either gmail or google gsuite adddress I was seeing
Message ID <5be55db9.1c69fb81.d0444.d894SMTPIN_ADDED_MISSING#mx.google.com>
as viewed from destination email adddress in gmail Show Original ... However since I am using full TLS certs in both my golang email client and local postfix server, when I replace using port 25 with the secure port 587 in my outbound email client (postfix was already using TLS certs) then I get the proper
Message ID <20181109163255.F164D8E9588#mail.myexample.com>
NOTE - at no time am I defining an email header message-id infact the golang repo I'm using does not have an api call to define that header
You missed the '<' and '>' brackets.

imap headers of a quoted mail

I'm sending an email with some xheader.
When the recipient of the email replays to that email, i want to parse it, and get the content of that xheader from the mail i get by replay.
unfortunately, when i'm parsing the email i get back, i don't see my xheader.
(I printed the whole email, and the xheader is not there)
How can i do that in PHP with Zend Framework (i'm using Zend_Mail_Storage_Imap)?
Code:
$mail = new Zend_Mail_Storage_Imap(array(
'host' => 'pop.gmail.com',
'user' => 'a#gmail.com',
'password' => 'a',
'ssl' => 'SSL'
));
$count = $mail->countMessages();
$message = $mail->getMessage($count);
print_r($message);
//go through the message
foreach(new RecursiveIteratorIterator($message) as $part){
echo '*****************<br/>';
print_r($part);
echo '<br/>*****************<br/>';
//match parts content type to text/html - the one that maches is the message HTML body
if (preg_match('/.*text\/html.*/', $part->contentType)){
$body = $part->getContent();
}
$headers = $part->getHeaders();
if (isset($headers['X-aHeader'])){
echo $headers['X-aHeader'];
}
Thanks,
Boris.
Pekka gets points for the correct response here - X-headers in an original message will not necessarily be retained for any replies to that message. But, you're using Gmail, so you have another potential option.
Gmail allows plus addressing, so username#gmail.com will also receive mail for username+extra#gmail.com. If your X-aHeader content is alphanumeric, you can append it to your email address (e.g. a+headerdata#gmail.com). If your header content isn't alphanumeric, I'd recommend caching what you would put in the header locally on your system, and provide a uniqid as the email plus-address. When you receive a reply, you can use that uniqid to look up the data you cached locally.
This is a common technique for uniquely identifying recipients who reply to emails, or who perhaps bounce messages.

Creating S/MIME from MIME?

I don't completely understand and some documentation or help would be appreciated greatly :)
Using PHP I create a MIME by using ezcomponents Mail object. But what I do not understand is:
Do you create an S/MIME message from a original MIME by signing it with openssl_pkcs7_sign ? or do you create an S/MIME from scratch and sign it when its done?
Please bear with me as I try to understand the correct way of doing things.
EDIT: Found this piece of code to illustrate my question better
<?
// Setup mail headers.
$headers = array("To" => "someone#nowhere.net",
"From" => "noone#somewhere.net",
"Subject" => "A signed and encrypted message.");
// Sign the message first
openssl_pkcs7_sign("msg.txt","signed.txt",
"signing_cert.pem",array("private_key.pem",
"password"),array());
// Get the public key certificate.
$pubkey = file_get_contents("cert.pem");
//encrypt the message, now put in the headers.
openssl_pkcs7_encrypt("signed.txt", "enc.txt",
$pubkey,$headers,0,1);
$data = file_get_contents("enc.txt");
// separate header and body, to use with mail function
// unfortunate but required, else we have two sets of headers
// and the email client doesn't decode the attachment
$parts = explode("\n\n", $data, 2);
// send mail (headers in the Headers parameter will override those
// generated for the To & Subject parameters)
mail($mail, $subject, $parts[1], $parts[0]);
?>
Save yourself a lot of pain and route the messages you need signed through a MTA filter that is designed for the job, e.g. Gnu Anubis (SMTP proxy) or implement a milter

which of this method is recomended - mail function

hay
now i want to send mail to 200 users
first way
$users = 'user1#,user2#,user3,etc';
foreach(explod(',',$users as $mail){
mail($mail,'','','');
}
or
mail(mail one,mail 2,mail3,mail4,etc)
i know the code is fully error
but i want the meaning
which is the best
mail with multi by spreate by ,
or looping the mail function with the one mail every time
Ideally, you should use an external piece of mailing software so you can send an e-mail to a list and it will handle individual recipients; this way, you can avoid looping mail calls (and queued sendmail requests) while not disclosing your mailing list.
Of the options you presented, it is best to send your email using a loop with individual calls to the mail function so as not to disclose your recipients.
Finally, maybe try something like this:
$recipients = array('user#example.com','admin#example.org',); // mail list
$bcc = join(',', $recipients);
mail(
'"Undisclosed Recipients" <no-reply#example.com>',
$subject,
$message,
"BCC: {$bcc}"
);
However, if you are using this, make sure whatever sendmail client you are using strips the BCC header before sending.

Categories