I'm new to stackoverflow and couldn't find an answer to my question which is; How do I secure my mail() code in php to prevent people from adding bcc which would ultimatly result in mass mailing? My website uses the PHP mail() service to email me when a new comment has been entered in my site. What is the best way to prevent people tampering with it, such as removing the bcc? What I have so far is:
function mres($input){
if (get_magic_quotes_gpc()){
$input = stripslashes($input);
}
return mysql_real_escape_string($input);
}
$name = strip_tags(mres($_POST['name']));
$comment = strip_tags(mres($_POST['comment']));
$to = 'myself#gmail.com';
$subject = 'Website - comment';
$body = 'A new comment has been entered on the website.'."\n\n"."$name".' said: '."\n\n"."$comment";
mail($to,$subject,$body);
Assuming that this code is followed by:
mail($to, $subject, $body);
Then it's safe, if overkill -- the only arguments to mail() which are vulnerable to injection are the ones that control header fields ($to, $subject, and $additional_headers). strip_tags and mysql_real_escape_string are both unnecessary, and the latter will make apostrophes show up as \' in your email.
If there's no following call to mail(), then it's trivially safe, because it doesn't do anything. :)
I would look at something like http://mailgun.com/
As it is, your mail will often end up in people's spam if you just use mail()
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last month.
Improve this question
I have data coming from html <form>. That did basic HTML5 validation for users convenience, now I need to do server side check. I have this so far that came from W3.
Now that I run the vars through the validator, how do I apply that validated data? Or is it already validated and I am over thinking it? But I would still like to use htmlspecialchars()...
<?php
$fname = $lname = $email = $message = "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$fname = test_input($_POST["fname"]);
$lname = test_input($_POST["lname"]);
$email = test_input($_POST["email"]);
$message = test_input($_POST["message"]);
}
function test_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
// email body stuff here
// send email
mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
?>
Or is this better? But not sure how to include htmlspecialchars()
$EmailFrom = Trim(stripslashes($_POST['email']));
$EmailTo = "xxx#xxx.com";
$Subject = "Contact Form";
$fname = Trim(stripslashes($_POST['fname']));
$lname = Trim(stripslashes($_POST['lname']));
$email = Trim(stripslashes($_POST['email']));
$message = Trim(stripslashes($_POST['message']));
// email body stuff here
// send email
mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
?>
I have this so far that came from W3.
I assume you mean W3Schools (not to be confused with the W3C who have the domain w3.org): They are extremely low quality. They are often wrong, and even where they are right they frequently leave important things out of their explanations.
Now that I run the vars through the validator, how do I apply that validated data?
test_input returns the data. You then assign it to variables. Just use those variables instead of the original data stored in $_POST.
That said … the test_input function is entirely unsuitable for the context and you should not use it.
Any escaping and sanitization must be tailored based on what you do with the data.
In this case…
mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
… you are sending a plain text email.
$data = trim($data);
You might want to remove extra spaces from the start and end of strings that the user has typed by accident.
This shouldn't be needed and doesn't help with security, but it does no harm and can make the results tidier.
$data = stripslashes($data);
This is used to remove escaping. The data you are getting should not be escaped in the first place.
This used to be needed when PHP had a misguided and unreliable auto-escaping feature but that was removed from PHP years ago and, even when it was there, code should test to see if the feature is turned on or not.
Since there isn't any escaping to remove, this can remove real data.
Don't do that.
$data = htmlspecialchars($data);
You are sending a plain text email. Not an HTML email. The input isn't going anywhere near HTML.
Escaping it for HTML will just risk making the reader of the email see HTML entities instead of the characters you want them to see.
Don't do that.
Four related issues here: trimming data, escaping characters, data validation, and data sanitization.
Trimming the input is good, because the input may have some unnecessary characters such as space or figures. For example, if the input is $str = " 1.8 Some input" and you only want to store "Some input" then you could use $str = ltrim($str, ' .0123456789'); (with a space at the beginning of the second parameter).
It is common to pass posted data through mysqli_real_escape_string() which helps create a legal sql statement. This would, among other things, escape quotes and allow them to be entered smoothly into the dataset. For details see: http://php.net/manual/en/mysqli.real-escape-string.php.
Using stripslashes could remove splashes that were used to escape some characters. For example, if $name = "O\'reilly"; using stripslashes($name) gives the $name as O'reilly which can disrupt the logic of your sql statement because of the unescaped quote. So then you would not use stripslashes after using mysqli_real_escape_string.
It is always important to validate data on the server side. But use of htmlspecialchars() will not remove anything. Whatever is encoded with it will be as it was when the data is decoded with htmlspecialchars_decode(), after reading it from the database where it was stored.
PHP Filters should be used for both validation and sanitation of data sent to the server side. For example, you could sanitize and check a posted email as follows:
// Sample email address, possibly got from $email = $_POST['email']
$email = "someone##example.com";
// Remove all illegal characters from email
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
// Validate e-mail address
if(filter_var($email, FILTER_VALIDATE_EMAIL)){
echo "The <b>$email</b> is a valid email address";
} else{
echo "The <b>$email</b> is not a valid email address";
}
// gives: The **someone##example.com** is not a valid email address
For a gentle introduction to PHP filters, see for example, https://www.tutorialrepublic.com/php-tutorial/php-filters.php from where I got the above example. For more details see http://php.net/manual/en/book.filter.php.
I have read the following comment in the official documentation of php about mail() function:
Although it is not documented, for the parameters $to and $subject the mail() function changes at least \r and \n to space. So these parameters are safe against injection of additional headers. But you might want to check $to for commas as these separate multiple addresses and you might not want to send to more than one recipient.
Is it correct? Also, I have always considered the $message parameter safe against injection, is it also correct?
I know how to protect myself against injection, I just want to know if I can avoid to filter those parameters.
Yes, that's true, but it's also incomplete. In the engine source code, the function php_mail_build_headers ensures headers comply with RFC 2822 § 3.6 requirements for maximum number of values. Particularly, the following headers are checked for single value:
orig-date
from
sender
reply-to
to
bcc
message-id
in-reply-to
subject
Yes, the message parameter is safe from header injection by definition: the message part is inserted after the separating new line between headers and body, so any header-like text inserted as part of the message will appear as literal text within the message body.
For your comment that you don't want to apply those filters.
I think you can get it done automatically by using Zend_Mail as I commented.
$ composer require zendframework/zend-mail
I'm pasting this from their documentation:
use Zend\Mail\Message;
use Zend\Mail\Transport\Sendmail as SendmailTransport;
$message = new Message();
$message->addTo('matthew#example.org');
$message->addFrom('ralph#example.org');
$message->setSubject('Greetings and Salutations!');
$message->setBody("Sorry, I'm going to be late today!");
$transport = new SendmailTransport();
$transport->send($message);
I am sending users an activation link via php, I only have a regex in place to check if the email consists of *#*.* which is fine, but I would like to check if the email actually sends also.
I tried to do this with
if (mail(....)) {
// success
} else {
// error
}
But when I enter an email of a#a.a it still goes through to the success step.
How can I actually check if it is a correct email in php, thanks.
validate the email address preoperly with
if (filter_var($email_a, FILTER_VALIDATE_EMAIL)) {
echo "VALID";
}
the email server will return true on accepting the email it does not know if it will get to the user the only way to do this , and i'm sure you have seen it, is to get the recipient to click on a link in the email that sends them back to your site, then you know its valid\used address.
From the PHP docs
bool mail( string $to , string $subject , string $message [, string $additional_headers [, string $additional_parameters ]] )
Returns TRUE if the mail was successfully accepted for delivery, FALSE
otherwise.
It is important to note that just because the mail was accepted for
delivery, it does NOT mean the mail will actually reach the intended
destination.
Just because an email address is formatted properly does not mean that there is an actual mailbox at that location. There is no way in PHP to "check" if a email address exists/is active. That is the entire purpose of activation links, to verify that someone is checking that email address.
+1 for filter_var(). Use it instead of a simple regexp. Even the most complex regexp can't get every possible way of putting together an RFC 5322 compliant address. Your attempt to come up with something is pretty much guaranteed to fail.
But you didn't ask about address format verification, you asked about checking to see that the mail was sent. The whole purpose of an activation link is to validate the email address. If you clean up unvalidated accounts on a regular basis, it doesn't matter whether the email address is structured correctly. By sending a link, you're doing a full-circuit test that bypasses any possible format misinterpretations that filter_var() might have.
Suppose we're sending trivial feedback and going to make these fields dynamic:
sender name
sender e-mail
subject
message body
would be this PHP code enough to protect us from all kinds of mail-injections?
//sanitizing email address
if ($email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)){
//encoding subj according to RFC and thus protecting it from all kinds of injections
$subject = "=?UTF-8?B?".base64_encode($_POST['subject'])."?=";
//encoding name for same reasons, and using sanitized email
$from = "From: =?UTF-8?B?".base64_encode($_POST['name'])."?= <$email>\r\n";
//protecting body as it mentioned in http://php.net/mail
$message = str_replace("\n.", "\n .", $_POST['text']);
mail('me#example.com',$subject,$message,$from);
}
at the moment I am playing with names like "some#email.com, other#email.com," but it seems that all available mail clients handling it correctly
would be this PHP code enough to protect us from all kinds of mail-injections?
It looks pretty comprehensive, just as long as your email client supports the RFC 2047 encoding method you're using in the headers. (Some webmail clients don't recognize the encoding.)
My only recommendation, other than not using mail() to begin with, would be considering is_email rather than the built-in filter. The built-in fails a number of edge cases.
It depends, if the filter complies with rfc that specify that the local part cant contain anything if it is surrounded by " or whatever character some address like "foo\r\nTo: poor-guy#dom.tld\r\nTo: dummy"#foo.tld will give you headers like :
Subject: foo
To: poor-guy#dom.tld
To: dummy"#foo.tld
quite bad ...
I have a feedback form where the user can enter his/her feedbacks in a textarea. When the form is submitted I am using the php mail function to get all the user details into my mail.
mail( "aaa#ddd.com", "Subject: Comments posted by $postedBy", $message, "From: $emailID" );
Here $message is the user comments. But I get something like this in the email body.
Hi.test line break\r\nnew line\r\nnewline 2\r\ntest again\r\nagain.
The line breaks in text area are showing up in the mail. How can I fix this?
Edit:
$message = mysql_real_escape_string($_POST['comments']);
Are the \r\n directly displayed or is all in one line without seeing \r\n?
For last I think you have to set the correct content-type.
In example 4 on http://php.net/manual/en/function.mail.php you can see how to set the content-type. But you have to use plain/text for that.
EDIT:
After your edit: mysql_real_escape maskes all linebreaks. use $_POST['comment'] on your mail()-call to have it working!
mail($to, $subject, $_POST['comment'], $from);
There is some function in your code that replaces newline characters with \r\n.
just trace your code and see, where this replacement takes place, and remove it.
Not a big deal
I'm not sure if you've tried this but can you try replacing the "\r\n" with "<br>".
Alternatively see if you can change the email mime type
Check this
http://php.bigresource.com/Email-MIME-Types-KesYPexl.html