When debugging email-sending functionality in a standard Apache/PHP environment (without mod_suphp or suexec), identifying the exact PHP script responsible for sending emails can be challenging. The mail logs typically only show the UID (usually 'apache' user) without revealing the originating script path.
Here are several approaches to pinpoint the problematic script:
1. Mail Header Injection
Modify your mail() function calls to include identifiable headers:
// Add this to your mail sending code temporarily
$headers = "X-Script-Location: " . __FILE__ . "\r\n";
mail($to, $subject, $message, $headers);
This will appear in the email headers when received.
2. Custom Logging Wrapper
Create a wrapper function for mail() that logs script information:
function logged_mail($to, $subject, $message, $additional_headers = '', $additional_parameters = '') {
$log_entry = date('Y-m-d H:i:s') . " - Script: " . __FILE__ . "\n";
$log_entry .= "From IP: " . $_SERVER['REMOTE_ADDR'] . "\n";
file_put_contents('/path/to/mail.log', $log_entry, FILE_APPEND);
return mail($to, $subject, $message, $additional_headers, $additional_parameters);
}
3. Process Monitoring
Use strace to monitor PHP processes in real-time:
# Run this command as root
strace -f -p $(pgrep -u apache php) -e trace=execve,write 2>&1 | grep -i mail
PHP Function Overriding
Override the mail() function temporarily using runkit:
runkit_function_redefine('mail', '$to, $subject, $message, $additional_headers, $additional_parameters',
'error_log("Mail sent from: ".__FILE__." to $to with subject: $subject");
return mail($to, $subject, $message, $additional_headers, $additional_parameters);');
SMTP Log Analysis
If using SMTP (like PHPMailer), configure logging:
$mail = new PHPMailer(true);
$mail->SMTPDebug = SMTP::DEBUG_CONNECTION;
$mail->Debugoutput = function($str, $level) {
file_put_contents('/path/to/smtp.log', "$level: $str\n", FILE_APPEND);
};
- Implement centralized mail sending through a single class/function
- Use proper authentication for mail sending scripts
- Regularly audit scripts with mail functionality
- Consider using queue systems for better tracking
When working with a standard Apache+PHP setup (without suEXEC or mod_suphp), identifying which PHP script is sending emails can be frustrating. The mail logs typically only show the Apache user (usually 'www-data' or 'apache') as the sender, making it impossible to trace back to the specific script.
Here are several approaches to track email-sending scripts:
1. Custom Mail Wrapper Function
Create a centralized mail function that logs script details:
function logged_mail($to, $subject, $message, $headers = '') {
$backtrace = debug_backtrace();
$caller = $backtrace[0];
$log = sprintf(
"[%s] Mail sent by %s (line %d)\n",
date('Y-m-d H:i:s'),
$caller['file'],
$caller['line']
);
file_put_contents('/var/log/php_mail.log', $log, FILE_APPEND);
return mail($to, $subject, $message, $headers);
}
2. PHP open_basedir and Error Logging
Configure PHP to log script execution:
// In php.ini
open_basedir = /var/www/example.com
log_errors = On
error_log = /var/log/php_errors.log
3. Apache Custom Log Format
Modify your Apache configuration to include PHP script information:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{SCRIPT_FILENAME}e" combined_with_script
CustomLog /var/log/apache2/access_with_script.log combined_with_script
Using PHP's register_shutdown_function
Track emails by examining the call stack at shutdown:
register_shutdown_function(function() {
$emails_sent = count(get_defined_vars()['__mail_queue'] ?? []);
if ($emails_sent > 0) {
error_log("Script ".__FILE__." sent $emails_sent emails");
}
});
X-PHP-Script Header Injection
Add script identification to email headers:
$headers .= "X-PHP-Script: ".__FILE__."\r\n";
mail($to, $subject, $message, $headers);
Consider these architectural improvements:
- Implement a centralized mail service class
- Use dependency injection for mail functionality
- Implement rate limiting for emails
- Create middleware for mail monitoring