I would like some insight on this function for sanitising user submitted data into a contact form. No HTML content or anything, just plain text.
Are there any vulnerabilities here that I'm missing? Am I ok to use the flag FILTER_FLAG_NO_ENCODE_QUOTES
on the subject line? I'm not sure if quotes are safe to submit.
I'm looking to create a all-round function that will be safe to use site-wide.
Note: I'm using this with PHPMailer not mail()
.
// Sanitise headers
function Sanitise_Mail($h) {
$h = filter_var($h,FILTER_SANITIZE_STRING,FILTER_FLAG_NO_ENCODE_QUOTES);
// Taken from PEAR Mail
$h = preg_replace('=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i','',$h);
return $h;
}
Site-wide functions (sample)... Feel free to point out any other problems here.
// Use instead of error_log to add some useful information to each log
function error_report($err) {
global $user;
error_log('log: ' . $err . ' | User: ' . $user->username . ' | User IP: ' . IP_ADDRESS);
}
// Find & test site / global email settings
function Email_Settings($default = false) {
global $global,$setting;
// Run once & save to $setting StdClass()
if($default == false && !empty($setting->email_settings)) return $setting->email_settings;
else {
// Check for site email settings
if($default == false && !empty($setting->smtp_host) && !empty($setting->smtp_username) && !empty($setting->smtp_password) && !empty($setting->smtp_key)) {
if($smtp_pass = smtp_pass($setting->smtp_password,$setting->smtp_key)) {
if(!empty($smtp_pass)) {
$e = new StdClass();
$e->host = $setting->smtp_host;
$e->username = $setting->smtp_username;
$e->password = $smtp_pass;
$e->receiver = !empty($setting->smtp_receiver) ? $setting->smtp_receiver : $setting->smtp_username;
$e->replyto = $setting->smtp_replyto;
$e->port = $setting->smtp_port;
$e->encryption = $setting->smtp_encryption;
$setting->email_settings = $e;
return $e;
}
else error_report('Email_Settings(): SMTP password was decrypted but is empty for site: ' . SITE_ID);
}
else error_report('Email_Settings(): SMTP password failed to decrypt for site: ' . SITE_ID);
}
// Fallback: Use default email settings
else if(!empty($global->smtp_host) && !empty($global->smtp_username) && !empty($global->smtp_password) && !empty($global->smtp_key)) {
if($smtp_pass = smtp_pass($global->smtp_password,$global->smtp_key)) {
if(!empty($smtp_pass)) {
$e = new StdClass();
$e->host = $global->smtp_host;
$e->username = $global->smtp_username;
$e->password = $smtp_pass;
$e->receiver = OWNER_EMAIL !== NULL ? OWNER_EMAIL : FALLBACK_EMAIL;
$e->replyto = $global->smtp_replyto;
$e->port = $global->smtp_port;
$e->encryption = $global->smtp_encryption;
$setting->email_settings = $e;
return $e;
}
else error_report('Email_Settings(): Stored default password was decrypted but is empty.');
}
else error_report('Email_Settings(): Stored default SMTP password failed to decrypt.');
}
else error_report('Email_Settings(): Required email settings are missing.');
}
return false;
}
// Pass parameters to send emails
function Send_Email($subject,$body,$recipient,$sender = '',$sender_name = '',$html = true,$default = false) {
if(Email_Settings($default)) {
require_once ROOT . '/plugins/phpmailer/PHPMailerAutoload.php';
$mail = new PHPMailer;
//$mail->SMTPDebug = 3; // Enable verbose debug output
$mail->CharSet = 'UTF-8';
$mail->isSMTP();
$mail->Host = Email_Settings($default)->host;
$mail->SMTPAuth = true;
$mail->Username = Email_Settings($default)->username;
$mail->Password = Email_Settings($default)->password;
if(!empty(Email_Settings($default)->encryption)) $mail->SMTPSecure = Email_Settings($default)->encryption;
if(!empty(Email_Settings($default)->port)) $mail->Port = Email_Settings($default)->port;
$mail->From = !empty(Email_Settings($default)->sender) ? Email_Settings($default)->sender : Email_Settings($default)->username;
$mail->FromName = $sender_name;
$mail->addAddress($recipient);
if((bool)$html == true) $mail->isHTML(true);
// Add reply-to header if available
if(!empty($sender)) $replyto = $sender;
else if(!empty(Email_Settings($default)->replyto)) $replyto = Email_Settings($default)->replyto;
if(!empty($replyto)) $mail->AddReplyTo($replyto);
$mail->Subject = $subject;
$mail->Body = $body;
if($mail->send()) return true;
else {
error_report('Send_Email() error: ' . $mail->ErrorInfo);
return false;
}
}
else error_report('Send_Email(): Email_Settings() returned false.');
}
This is how I'm currently using this on the contact page...
if(isset($_POST['contactus'])) {
if(!empty($_POST['emailbody'])) {
if(!empty($_POST['subjectfrom'])) {
if(!empty($_POST['emailfrom'])) {
if(!filter_var($_POST['emailfrom'],FILTER_VALIDATE_EMAIL)) {
$result['error'][] = @$lang->module['invlaidemail'];
}
else if(function_exists('Sanitise_Mail')) {
$_POST['namefrom'] = Sanitise_Mail($_POST['namefrom']);
$_POST['subjectfrom'] = Sanitise_Mail($_POST['subjectfrom']);
$_POST['phonefrom'] = Sanitise_Mail($_POST['phonefrom']);
$_POST['emailbody'] = Sanitise_Mail($_POST['emailbody']);
if(!empty($_POST['phonefrom'])) $phonenum = @$lang->module['phone'] . ' ' . $_POST['phonefrom'];
else $phonenum = '';
$body = $_POST['emailbody'] . "\r\n\r\n" . $phonenum;
// Check IP ban list
if(function_exists('check_ip')) check_ip();
if(empty($result['error'])) {
// Send email
if(Send_Email($_POST['subjectfrom'],$body,Email_Settings()->receiver,$_POST['emailfrom'],$_POST['namefrom'],0)
|| Send_Email($_POST['subjectfrom'],$body,Email_Settings()->receiver,$_POST['emailfrom'],$_POST['namefrom'],0,1)) {
$result['success'][] = sprintf(@$lang->module['success'],$_POST['emailfrom']);
// Increment IP ban list
if(function_exists('ban_ip')) ban_ip(@$lang->module['ban_ip'],0,strtotime('+31 minutes'));
unset($_POST);
}
else $result['error'][] = @$lang->module['error'];
}
// Otherwise do nothing, echo errors in $result['error'] to browser later...
}
else {
$result['error'][] = @$lang->text['report_general_error'];
error_report('Function Sanitise_Mail() not found, cannot continue.');
}
}
else $result['error'][] = @$lang->module['noemail'];
}
else $result['error'][] = @$lang->module['nosubject'];
}
else $result['error'][] = @$lang->module['nomessage'];
}