Problem
I have an PHP endpoint used to mail me feedback or error logs from my sites here: https://github.com/kaihendry/vanilla-php-feedback-form/blob/master/feedback/feedback.php
<?php
switch ($_SERVER['HTTP_ORIGIN']) {
case "https://example.com":
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
break;
default:
die("Bad origin: " . $_SERVER['HTTP_ORIGIN']);
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit;
}
$subject = 'Feedback from ' . $_SERVER['REMOTE_ADDR'] . ' ' . $_SERVER['HTTP_REFERER'];
$input = json_decode(file_get_contents('php://input'), true);
if(empty($input["msg"])) { http_response_code(400); die(); }
$message = json_encode($input, JSON_PRETTY_PRINT) . "nn--n" . json_encode($_SERVER, JSON_PRETTY_PRINT);
if(empty($input["from"]) || !filter_var($input["from"], FILTER_VALIDATE_EMAIL)) {
mail('me@example.com', $subject, $message);
} else {
$headers = 'From: ' . $input["from"];
mail('me@example.com', $subject, $message, $headers);
}
?>
There are a couple of problems:
- It’s slow. I suspect it’s mail(), which is used with ssmtp & AWS SES. It’s fast when I comment the mail line out. Can it be backgrounded somehow?
- Should I be returning JSON like status OK or not OK if mail() fails? What is best practice?
- Is there an easy way of compiling them into a digest or maybe better alternatives to email I may have missed? I’m a bit worried someone can abuse this script using cURL. How can I prevent that sanely?
Solution
1
From looking on the script, seems there is nothing that can be slow except of the mail()
. The problem is, it needs to connect with the target mail server and thats often very slow.
mail()
got many problems so it is best avoid using it. You can read about it here and here
The best alternative is probably Swiftmailer http://swiftmailer.org/. With that you can solve also your “in background sending” using spool
. Your mails will be send after the script finished running.
For the next two questions I would advise to handle your script like an HTTP API
.
2
With Spool
you cannot know in real time if sending of your email was an success, as this will happen after your script will terminate.
Without Spool
it could still be faster (or not), but you will know if it was ok right away
What to return depend now on the above
You can use standard response codes, almost for all your scenarios. http_status_codes
You don’t need to return anything in the body most of the time.
Fail:
- 400 – Bad Request – when the request is missing
$msg
, or body json is malformed - 401 – Unauthorized – when missing http_origin or not in the list of allowed
- 405 – Method not allowed – if it is not POST or OPTIONS
- 409 – Conflict – email sending failed (if not usign spool)
Success:
- 200 – OK – email send successfully (if not using spool)
- 202 – Accepted – when everything is ok, but using spool so you don’t know if the sending of email was successfull
In an API approach you would be able to call some url to check if your Accepted request was done processing or not, but this is another topic.
3
As I said before, this is basically an API, so all methods how to secure an API apply here. You can study those.
If you want to go simple and quick the easier ones are:
- SSL + Http Basic Auth – simple basic auth when going over https is quite secure
- SSL + ApiKey in header + Token Auth – basic thing for all API
I would not recommend going without SSL as you can always catch the request and read the keys.
Never call PHP mail() directly.
At least digest the sorry saga web2mail scripts first.
There are PHP Mail modules that escape line endings correctly.