We have shared hosting servers which use PHP fastcgi (on IIS) for several clients (shared hosting). Regularly clients use old exploitable code which causes holes in their applications that eventually gets used by hackers to install malicious code. Most of the time this code is being used to send spam from our servers.
We have no control over our clients code, so patching the holes is quite impossible.
We would however like to block the clients sending spam once they send more then X email messages in Y amount of time.
The setup is fastcgi based, so there is little relation between php and the webserver.
PHP sends its mail through SMTP on localhost. The mailserver allows relay of all localhost connections (obviously).
One thing that goes through my mind is setting an environment variable containing an identifier in the fastcgi environment and using php's prepend file option to add a header to all mail send by php's mailer. After that we could use that mail header to identify the spamming culprit.
The option above still would not take care of spam scripts using regular telnet (telnet localhost, HELO, MAIL FROM etc... ) when sending email.
My question to you: is the idea that i've mentioned the best and perhaps only option to deal with our issue? Or are there better solutions for this situation? And if so, please explain how you would deal with the issue.
You can filter that on you MTA (message transfer agent). For example, allow no more than 50 emails in 1 hour for each user in Exim ( http://www.exim.org ) config file (/etc/exim/exim.conf):
begin acl
acl_check_not_smtp:
warn ratelimit = 0 / 1h / strict / $sender_address_local_part
log_message = Sender rate $sender_rate / $sender_rate_perio
acl_not_smtp = acl_not_smtp
begin acl
acl_not_smtp:
deny message = Sender rate overlimit - $sender_rate / $sender_rate_period
ratelimit = 50 / 1h / strict
accept
And no matter how they try to send, via php mail() or other method.
Most shared hosts block the use of PHP's mail() function, as that can be easily exploited. Instead they advice using sendmail or similar scripts which require SMTP authentication before sending. Assuming you're not already doing this, once implemented, you should be able to keep track of number of emails sent from a particular domain/email account and put restrictions on it.
Okay, stick with me on this one. I have not implemented it but it looks good.
The concept here is that you could
run a php file before EVERY page on your customers site
in that php file rename the mail function to mail_internal().
in that php create a new function called mail to do your check / verification that your customer is allowed to send mail, and if they are call the mail_internal() function with the same parameters.
You will have to install the runkit PECL extension
http://us.php.net/manual/en/runkit.installation.php
Changes
in php.ini
auto_prepend_file /var/www/allclients_forcedfile.php
in /var/www/allclients_forcedfile.php
runkit_function_rename ( "mail" , "mail_internal" );
function mail ( $to , $subject , $message, $additional_headers = "", $additional_parameters ="" )
{
$args = func_get_args();
error_log("mail_internal : $_SERVER[HTTP_HOST] : ".implode(" : ",$args));
//lookup whether you want to send more mail for this client maybe by keeping a counter in some file in the $SERVER[DOCUMENT_ROOT]
if($sendmoremail)
return mail_internal ( $args[0], $args[1] , $args[2], $args[3] , $args[4] );
return false;
}
As expected it seems Stack Overflow is not the right place for this question. The provided answers do not expose some clear method to identify FastCGI sessions' connections to the MTA (SMTP) server.
I will go with my initial concept of adding an identifier to php's environment. This identifier can be read in PHP's prepend file by using the getenv() function. This identifier can then be added to mail headers for outgoing mail.
Furthermore I have enabled the mail.add_x_header ini setting which will help identify which script caused the spam run.
I am leaving the question open for the bounty duration, hoping other options will magically appear :)
Related
I am using PHP with Apache on Linux, with Sendmail. I use the PHP mail function. The email is sent, but the envelope has the Apache_user#localhostname in MAIL FROM (example nobody#conniptin.internal) and some remote mail servers reject this because the domain doesn't exist (obviously). Using mail, can I force it to change the envelope MAIL FROM?
EDIT: If I add a header in the fourth field of the mail() function, that changes the From field in the headers of the body of the message, and DOES NOT change the envelope MAIL FROM.
I can force it by spawning sendmail with sendmail -t -odb -oi -frealname#realhost and piping the email contents to it. Is this a better approach?
Is there a better, simpler, more PHP appropriate way of doing this?
EDIT: The bottom line is I should have RTM. Thanks for the answers folks, the fifth parameter works and all is well.
mail() has a 4th and 5th parameter (optional). The 5th argument is what should be passed as options directly to sendmail. I use the following:
mail('to#blah.com','subject!','body!','From: from#blah.com','-f from#blah.com');
PHP Official documentation for mail()
bool mail ( string $to , string $subject , string $message [, string
$additional_headers [, string $additional_parameters ]] )
...
additional_parameters (optional)
The additional_parameters parameter can be used to pass additional
flags as command line options to the program configured to be used
when sending mail, as defined by the sendmail_path configuration
setting. For example, this can be used to set the envelope sender
address when using sendmail with the -f sendmail option.
This parameter is escaped by escapeshellcmd() internally to prevent
command execution. escapeshellcmd() prevents command execution, but
allows to add additional parameters. For security reasons, it is
recommended for the user to sanitize this parameter to avoid adding
unwanted parameters to the shell command.
Since escapeshellcmd() is applied automatically, some characters that
are allowed as email addresses by internet RFCs cannot be used. mail()
can not allow such characters, so in programs where the use of such
characters is required, alternative means of sending emails (such as
using a framework or a library) is recommended.
The user that the webserver runs as should be added as a trusted user
to the sendmail configuration to prevent a 'X-Warning' header from
being added to the message when the envelope sender (-f) is set using
this method. For sendmail users, this file is /etc/mail/trusted-users.
You can try this (im not sure tho):
ini_set("sendmail_from", yourmail#example.com);
mail(...);
ini_restore("sendmail_from");
I would also recommend checking into PHPMailer. It's great for creating and sending email, making the process a lot easier, along with support for SMTP.
following to php manual additinal -f parameter need to be passed to mail function
Not as many write here "-f from#email.com" but without white space "-ffrom#email.com"
https://www.php.net/manual/en/function.mail.php
What you actually need to do is change the hostname of the machine Apache is running on, plus the user Apache is running as.
In your current case it is:
Apache user:nobody
Server hostname: conniptin.internal
Changing those two values is pretty simple and will solve the root of your problem.
Although if you need to do it from PHP then perhaps use the system/exec functions. I do not think it will work in practice though, as you need to restart Apache and probably also the entire host for the new names to be used.
I am using PHP's mail() function, but I am unable to send mails to the majority of servers.
It send mails to the GMAIL without problems...Probably the rest of servers simply deny my mails...I set the MX records of the mail servers that I need...And I changed my PTR record to mail.example.com......however stil cant send mails..How can I solve this problem?
In addition, messages' sender is displayed as "www-data#localhost"
(I am using Digital Ocean, Ubuntu 12.04)
Errors that I am getting in log file:
My unqualified host name (localhost) unknown; sleeping for retry localhost sendmail[11620]: unable to qualify my own domain name (localhost) -- using short name(errors that I get in mail.err); and in log file it shows some errors regarding verification, Do I need to install smt regarding STARTTLS=client
when using the mail() function ypou neet to set the SENDER as well as teh from/to etc.
i.e.
mail($to, $subject, $body, $headers, "-f ".$webmaster);
The important thing is that "-f ".$webmaster that allows you to override the default of 'www-data#localhost' as the BOUNCE address (Envelope sender) for failed mail..
Most mailers will reject e-mail with an invalid bounce address.
Many hosted servers will not let you set the -f value tho...it sets the 'real sender' and of course its a classic way to send spam..
Try with the -f and if it fails change your hosting company.
Or get yourself a virtual private server.
I had the same problem and one thing you can do is: open a ticket with digital ocean support. By default, new accounts have a block to send e-mails (to avoid spammers). They resolved quickly and greatly improved.
Another thing is: check your mail reputation. You can find some websites that can help with this, like mail-tester.com - This site provides a report with potencial issues to fix the problem.
Currently I have a mail server configured (a real one from my ISP) and mail internal and external works on the command line. In PHP only external users work.
For testing I would like to send to internal users only.
( Ideally I would like to set up lots of aliases that point to one user so mail to:
tom#localhost.com, dick#localhost.com, harry#localhost.com end up in /var/mail/johnsmith )
I'd be greatful if someone could help here. I'm hesitant to edit the postfix config files...
On the command line johnsmith#localhost works but not in PHP. It's using cakePHP and I checked the value of $email-addr just before the send ($this->Email->send();) and the value is johnsmith#localhost. I'm not that familiar with cakePHP yet. The var/log/mail shows nothing, only external email addresses.
(server is Suse linux)
You can use the basic mail php function
http://php.net/manual/en/function.mail.php
Under linux, mail php function relies on sendmail, just check that sendmail is properly installed.
In /etc/postfix/main.cf add localhost.com:
mydestination = $myhostname, localhost.$mydomain,localhost,localhost.com
This allows sending on "localuser#localhost.com" via command line. I loaded up a test php mail form script in a browser and it works, sending mail through to /var/mail/localuser.
At the moment it means I have to check each local users /var/mail file. I'm working on the alias. My first attempt at that failed.
I am using php5.
Are there some settings or a simple php.ini directive that would redirect all the emails to a folder?
I want on the development machine to have all the emails generated by the system not sent to the actual receiver but put in a folder.
Thanks.
I used to have some code like this (kinda pseudocode):
define ('DEBUG', true);
function send_email($to, $subject, $body) {
if (DEBUG) {
file_put_contents('some_folder/' . $to . date('dmY-His') . '.html', $body);
}else{
// Actual code to send email
}
}
But i agree with others, it's easier/better to setup an development email account to receive those emails.
I don't think you will be able to do something like this. Mails are sent by a mail server so it must be your mail server that writes them to a file instead of sending them.
Why not simply send it to a special development email?
Sample:
define('DEBUG', true);
if(DEBUG)
{
// Override recipient
$recipient = 'development#domain.tld';
}
// Send mail...
No settings that I'm aware of in PHP itself. However, if you're using Postfix on your development server, here's a recipe I cooked up to redirect all outbound email to a single (local) address:
/etc/postfix/main.cf: (add this to the existing file, don't replace everything)
virtual_alias_maps = regexp:/etc/postfix/virtual
/etc/postfix/virtual:
/.*/ duskwuff#localhost
You can configure your mail server to accept SMTP messages as normal, but make it unable to forward them onto another mail server. If your mail server supports it, make it redirect all messages to a postmaster account, or any other address of your choice.
This means that PHP will behave as normal, everything will appear to work straight away with the message, but it just won't go to the 'intended' recipient.
It also means that you can inspect headers (pretty much as they would normally appear), to support debugging.
There are many ways to do this. Basically, you need to define the sendmail command in your php.ini to point to a program or script which will save the mail locally.
One solution is this:
Catch emails with php script
Another is this:
Mail catcher
I am trying to send out password reset links for when users forget their password to login to a system I am creating. The problem is, the smtp server is supposedly not configured on the server my system is hosted on. So whenever I try to use the php mail() function to send an email to an internal email address, the emails fail to send, but outside email address (tested with a gmail account), the emails go through. I believe this is because my server is sending directly out to the internet instead of passing through an internal smtp server to resolve where our domain emails should be sent. I was wondering if anyone knew how to configure this on an Xserve or if they knew how to specifically tell the php mail() function where to initially send the email. Any help or pointing in the right direction would be extremely helpful.
Thank you!
mail() doesn't send mail, it just hands things over to the local SMTP server. It does NOT reach out to the recipient's server to deliver the mail. In real world terms, mail() walks your letter down the street and drops it into the neighborhood mailbox. After that, it has absolutely nothing more to do with mail delivery.
Check your local SMTP server's logs to see why the local mails aren't being delivered. There should be a line or two saying why it's registered. Perhaps the local MTA (mail-transfer agent, aka the local "mail man") isn't configured properly.
You can control mail() with it's settings.
This might not solve your overall problem, but hopefully it's useful. This related answer has more information.
We just addressed this problem internally here. Hopefully this will help you as well.
Our environment:
Ubuntu 12.04 LTS
PHP 5.3.10
We could telnet into our SMTP server and send mail from our web server, so it wasn't a permissions issue. When attempting to mail externally, all works perfectly. When attempting to mail internally, silent failure.
Our PHP is using sendmail, which by default, attempts to relay mail to 127.0.0.1. Point this at your SMTP server by editing /etc/mail/sendmail.cf. Update the line from:
# "Smart" relay host (may be null)
DS
to
# "Smart" relay host (may be null)
DSyour.smtp.server.com
Restart sendmail and try sending yourself an email via PHP.
This is something that occurs on Parellels’ PLESK server administration software.
Find your ‘main.cf’ configuration file for PostFix, which for CentOS 6, is located at
/etc/postfix/main.cf
If you can’t find it, do a
which postfix
SSH command to at least see where Postfix is on your server.
Then, open the file up through a text editor, or in the Linux shell, and make these lines (should be at the end of the file, around line 677) :
virtual_mailbox_domains = $virtual_mailbox_maps, hash:/var/spool/postfix/plesk/virtual_domains
virtual_alias_maps = $virtual_maps, hash:/var/spool/postfix/plesk/virtual
virtual_mailbox_maps = hash:/var/spool/postfix/plesk/vmailbox
commented out like this :
#virtual_mailbox_domains = $virtual_mailbox_maps, hash:/var/spool/postfix/plesk/virtual_domains
#virtual_alias_maps = $virtual_maps, hash:/var/spool/postfix/plesk/virtual
#virtual_mailbox_maps = hash:/var/spool/postfix/plesk/vmailbox
Then, restart the Postfix service
sudo service postfix restart
Apache while your at it (can’t hurt), and voila! Your email address should be receiving those emails now. This also doesn’t affect any of your regular emails or anything else, either.