Preventing mail injection in php intercepting some characters \r, \n, %0A, %0D - php

I'd like to stop people spamming using special characters used for mail injection in php. For example \n and \r are used to concatenate several headers and so that also with %0A and %0D. So I wrote a regex to match them. But I suspect that my regex is not so efficient... something brings me to think that I'm writing something of unusable... Any help is appreciated, thanks.
Here is a basic example of that I'd like to do...
if (!preg_match('/^[^\s]./', $_POST['name']) || (preg_match('/(%0A|%0D|\\n+|\\r+|;|mime-version:|content-type:|content-transfer-encoding:|subject:|to:|cc:|bcc:)/i', $_POST['name'])) || !(strpos($_POST['name'],'\r') === false) || !(strpos($_POST['name'],'\n') === false)) {
exit("Warning: your Name contains illegal characters! This mail will not be sent!");
} elseif (!preg_match('/^[^\s]./', $_POST['subject']) || (preg_match('/(%0A|%0D|\\n+|\\r+|;|mime-version:|content-type:|content-transfer-encoding:|subject:|to:|cc:|bcc:)/i', $_POST['subject'])) || !(strpos($_POST['subject'],'\r') === false) || !(strpos($_POST['subject'],'\n') === false)) {
exit("Warning: your Subject contains illegal characters! This mail will not be sent!");
} elseif (!preg_match('/^[a-zA-Z0-9]+([_\\.-][a-zA-Z0-9]+)*'.'#([a-zA-Z0-9]+([\.-][a-zA-Z0-9]+))+$/', $_POST['mail'])) {
exit("Warning: your Mail contains no valid email address! This mail will not be sent!");
} elseif (!preg_match('/^[^\s]./', $_POST['message'])) {
exit("Warning: your Message connot be empty! This mail will not be sent!");
} else {
$name = $_POST['name'];
$mail = $_POST['mail'];
$subject = $_POST['subject'];
$message = $_POST['message'];
mail($to, $subject, $message, $headers, "-f$mail") {
exit("Your Mail is sent! Thanks!");
}
In order to match \n, \r, %0A and %0D how should I write my regex?
And
!(strpos($_POST['subject'],'\r') === false) !(strpos($_POST['subject'],'\n') === false)
are quite good?
Thanks.

I did a test. The test result is a success! I tested the regex trying directly in localhost with different methods:
<?php
$test = "the dog \n was \r sleeping on the floor";
if (preg_match_all('/(%0A|%0D|\\n+|\\r+|;|mime-version:|content-type:|content-transfer-encoding:|subject:|to:|cc:|bcc:)/i', $test, $tmp)) {
echo "I found this character: '";
print_r($tmp[1]);
echo "'";
} else {
echo "I cannot find any string searched";
}
?>
Result:
I found this character: 'Array ( [0] => [1] => ) '
Looking at source I can see the \n and the \r
I found this character: 'Array
(
[0] =>
[1] =>
)
'
So I think that the regex is well build.
Also other test I did with strpos():
if !(strpos($_POST['subject'],'\n') === false)) {
fails with single quotes while finds the \n with double quotes...
if !(strpos($_POST['subject'],"\n") === false)) {
Conclusions: regex is well formed and strpos() needs "" to match \n or \r.

I am not very thorough with regex, but I can help u by giving an alternate trick solution to your problem.
You ca use following code to solve your purpose.
<?php
function sanitize_strings($raw_string) {
return preg_replace("/[^0-9a-zA-Z_\-\\\%]+/", "", $raw_string);
}
if( $raw_string == sanitize_strings($raw_string) )
{
echo "No illegal characters found.";
}
else
{
echo "Illegal characters found.";
}
?>
Hope this works for you. :)

Related

php expression for only lowercase and underscore for email validation

I am trying to have users sign up with a desired email. But I dont want them to have to input the '#domain.com', I just want them to input the username, (everything to the left of '#' ). Also, how would I go about making sure that the only characters to the left are lowercase letters, numbers and an underscore? Basically if they enter a capital letter, it should lower it. I do know about strtolower, but just don't quite know how to implement it in my script. I only want my script to be able to validate one domain, in this example 'domain.com'
My script needs these fields populated
$email = ($_POST['email']);
and this checks if it is valid
if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
echo "You have entered an invalid email. Press back and try again.";
$db = null;
exit();
}
So I know I need to do something with the $email variable. Sorry am a bit new to php
You're doing it wrong, as there is a range of characters to the left of the '#' which are valid in email addresses, but this is what you want:
$stub = strtolower($_POST['email']);
$email = $stub . "#domain.com";
if ((preg_match("/[^a-z0-9_]/", $stub)) ||
(filter_var($email, FILTER_VALIDATE_EMAIL) === false) ) {
echo "Invalid email....";
die;
}
See the following:
strtolower: http://php.net/manual/en/function.strtolower.php
strpos: http://php.net/manual/en/function.strpos.php
$email=strtolower($email);
if(strpos($email,'#')){
//Fail
}
Then just validate with filter_var as you have specified.
$email = strtolower('dsfds_09azZdsf');
if (strlen($email) == strlen(preg_replace('#[^a-z_0-9]#i', '', $email)))
{
echo 'valid';
}
else
{
echo 'not valid';
}

Finding an exact match in a text file using PHP AJAX jQuery form

I have a .txt file where I would like to find an EXACT match of a single email entered in a form.
The present directives (see below) I used, work for a standard form. But when I use it in conjunction with an AJAX call and jQuery, it confirms it exists by just finding the first occurrence.
For example:
If that person enters "bobby#" it says not found, good.
If someone enters their full Email address and it exists in the file, it says "found", very good.
Now, if someone enters just "bobby", it says "found", not good.
I used the following three examples below with the same results.
if ( !preg_match("/\b{$email}\b/i", $emails )) {
echo "Sorry, not found";
}
and...
if ( !preg_match( "/(?:^|\W){$email}(?:\W|$)/", $emails )) {
echo "Sorry, not found";
}
and...
if ( !preg_match('/^'.$email.'$/', $emails )) {
echo "Sorry, not found";
}
my AJAX
$.ajax({
type: "POST",
url: "email_if_exist.php",
data: "email="+ usr,
success: function(msg){
my text file
Bobby Brown bobby#somewhere.com
Guy Slim guy#somewhere.com
Slim Jim slim#somewhere.com
I thought of using a jQuery function to only accept a full email address, but with no success partly because I didn't know where to put it in the script.
I've spent a lot of time in searching for a solution to this and I am now asking for some help.
Cheers.
Because your text file contains "bobby" in it, any regex such as you are suggesting will always find "bobby". I would suggest checking for the presence of the # symbol BEFORE you run the regex, as any valid email will always have # in it. Try something like this:
if (strpos($email,'#')) {
if ( !preg_match("/\b{$email}\b/i", $emails )) {
echo "Sorry, not found";
}
}
EDIT: Looking at this 4 years later... I would make the regex match to the end of the line, using the m modifier to specify multiline so the $ matches newline or EOF. The PHP line would be:
if ( !preg_match("/\b{$email}$/im", $emails )) {
If you're just checking to see if the user exists, this should work:
$users = trim(preg_replace('/\s\s+/', ' ', $users));
$userArray = explode(' ', $users);
$exists = in_array($email, $userArray);
Where $users is referencing to the example file and $email is referencing to the queried e-mail.
This replaces all newlines (and double spaces) with spaces and then splits by spaces into an array, then, if the e-mail exists in the array, the user exists.
Hope I helped!
'/^'.$email.'$/' is quite close. Since you want the check being "true" only if the full email address is on the file you should include in the pattern the "limits" of the email: Whitespace before and end_of_the_line after if:
'/ '.$email.'$/'
(Yes, I've just changed ^ -start of line- for a whitespace)
If your text file filled with lines that every line ending with the email,
so you can regex with testing and match by your "email + end od line"
like that:
if( preg_match("/.+{$email}[\n|\r\n|\r]/", $textFileEmails) )
{
/// code
}
The code would validate first using php core functions whether the email is correct or not and then check for the occurrence.
$email = 'bobby#somewhere.com';
$found = false;
//PHP has a built-in function to validate an email
if(filter_var($email, FILTER_VALIDATE_EMAIL)){
//Grab lines from the file
$lines = file('myfile.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
//Grab words from the line
$words = explode(" ", $line);
//If email found within the words set the flag as true.
if(in_array($email, $words)) {
$found = true;
//If the aim is only to find the email, we can break out here.
break;
}
}
}
if(false === $found) {
echo 'Not found!';
} else {
echo 'Found you!';
}
If you file is formatted as your example first_name, last_name, email#address.tdl
it's really easy to break it up on load to search.
I don't know why you would use preg_match for this bit your if you were advised to use preg use it to verify the email address. You're better off using indexOf method in php (strpos) to search the file but the below method works for your fixed file format.
Object Orientated File Reader and searcher
class Search{
private $users = array();
public function __construct($password_file){
$file = file_get_contents($password_file);
$lines = explode("\n", $file);
$users = array();
foreach($lines as $line){
$users = expode(" ", $line);
}
foreach($users as $user){
$this->users[] = array("first_name" => $user[0], "last_name" => $user[1], "email" => $user[2])
}
}
public function searchByEmail($email){
foreach($this->users as $key => $user){
if($user['email'] == $email){
// return user array
return $user;
// or you could return user id
//return $key;
}
}
return false;
}
}
Then to use
$search = new Search($passwdFile);
$user = $search->searchByEmail($_POST['email']);
echo ($user)? "found":"Sorry, not found";
Using preg_match to validate email then check
If you want to use preg and your own file search system.
function validateEmail($email) {
$v = "/[a-zA-Z0-9_-.+]+#[a-zA-Z0-9-]+.[a-zA-Z]+/";
return (bool)preg_match($v, $email);
}
then use like
if(validateEmail($_POST['email'])){
echo (strpos($_POST['email'], $emails) !== false)? "found":"Sorry, not found";
}

Why won't preg_match work?

Ever since "ereg" became depreciated, I began to use "preg_match". Unfortunately in my code, it doesn't accept my valid e-mail address. I am certain that this Regular Expression i'm using is working, but what I'm looking for is an alternative in doing this function or to point out what I'm doing wrong.
Here's my function:
function validate($email){
$regex = "^((([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|/|=|\?|\^|_|`|\{|\||\}|~)+(\.([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|/|=|\?|\^|_|`|\{|\||\}|~)+)*)#((((([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.))*([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.(af|ax|al|dz|as|ad|ao|ai|aq|ag|ar|am|aw|au|at|az|bs|bh|bd|bb|by|be|bz|bj|bm|bt|bo|ba|bw|bv|br|io|bn|bg|bf|bi|kh|cm|ca|cv|ky|cf|td|cl|cn|cx|cc|co|km|cg|cd|ck|cr|ci|hr|cu|cy|cz|dk|dj|dm|do|ec|eg|sv|gq|er|ee|et|fk|fo|fj|fi|fr|gf|pf|tf|ga|gm|ge|de|gh|gi|gr|gl|gd|gp|gu|gt| gg|gn|gw|gy|ht|hm|va|hn|hk|hu|is|in|id|ir|iq|ie|im|il|it|jm|jp|je|jo|kz|ke|ki|kp|kr|kw|kg|la|lv|lb|ls|lr|ly|li|lt|lu|mo|mk|mg|mw|my|mv|ml|mt|mh|mq|mr|mu|yt|mx|fm|md|mc|mn|ms|ma|mz|mm|na|nr|np|nl|an|nc|nz|ni|ne|ng|nu|nf|mp|no|om|pk|pw|ps|pa|pg|py|pe|ph|pn|pl|pt|pr|qa|re|ro|ru|rw|sh|kn|lc|pm|vc|ws|sm|st|sa|sn|cs|sc|sl|sg|sk|si|sb|so|za|gs|es|lk|sd|sr|sj|sz|se|ch|sy|tw|tj|tz|th|tl|tg|tk|to|tt|tn|tr|tm|tc|tv|ug|ua|ae|gb|us|um|uy|uz|vu|ve|vn|vg|vi|wf|eh|ye|zm|zw|com|edu|gov|int|mil|net|org|biz|info|name|pro|aero|coop|museum|arpa))|(((([0-9]){1,3}\.){3}([0-9]){1,3}))|(\[((([0-9]){1,3}\.){3}([0-9]){1,3})\])))$";
if (preg_match($regex, $email)) {
return true;
} else {
return false;
}
}
and here's my code to react with the function.
if (validate($email) == false){
$_SESSION['error'] = "You have an invalid email!<br /><br />";
header("Location: contact.php");
die;
}
As I run this code, it shows this error message:
Warning: preg_match() [function.preg-match]: No ending delimiter '^' found in C:\xampp\htdocs\2012\Next\inc\functions.php on line 11
Your regex pattern need delimiters:
function validate($email){
$regex = "/...your pattern.../";
if (preg_match($regex, $email)) {
return true;
} else {
return false;
}
}
EDIT: AND then you need to escape the delimiters characters in your regex, if present.
Using / as delimiter, you got two in your pattern:
$regex = "/^((([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|\/|=|\?|\^|_|`|\{|\||\}|~)+(\.([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|\/|=|\?|\^|_|`|\{|\||\}|~)+)*)#((((([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.))*([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.(af|ax|al|dz|as|ad|ao|ai|aq|ag|ar|am|aw|au|at|az|bs|bh|bd|bb|by|be|bz|bj|bm|bt|bo|ba|bw|bv|br|io|bn|bg|bf|bi|kh|cm|ca|cv|ky|cf|td|cl|cn|cx|cc|co|km|cg|cd|ck|cr|ci|hr|cu|cy|cz|dk|dj|dm|do|ec|eg|sv|gq|er|ee|et|fk|fo|fj|fi|fr|gf|pf|tf|ga|gm|ge|de|gh|gi|gr|gl|gd|gp|gu|gt| gg|gn|gw|gy|ht|hm|va|hn|hk|hu|is|in|id|ir|iq|ie|im|il|it|jm|jp|je|jo|kz|ke|ki|kp|kr|kw|kg|la|lv|lb|ls|lr|ly|li|lt|lu|mo|mk|mg|mw|my|mv|ml|mt|mh|mq|mr|mu|yt|mx|fm|md|mc|mn|ms|ma|mz|mm|na|nr|np|nl|an|nc|nz|ni|ne|ng|nu|nf|mp|no|om|pk|pw|ps|pa|pg|py|pe|ph|pn|pl|pt|pr|qa|re|ro|ru|rw|sh|kn|lc|pm|vc|ws|sm|st|sa|sn|cs|sc|sl|sg|sk|si|sb|so|za|gs|es|lk|sd|sr|sj|sz|se|ch|sy|tw|tj|tz|th|tl|tg|tk|to|tt|tn|tr|tm|tc|tv|ug|ua|ae|gb|us|um|uy|uz|vu|ve|vn|vg|vi|wf|eh|ye|zm|zw|com|edu|gov|int|mil|net|org|biz|info|name|pro|aero|coop|museum|arpa))|(((([0-9]){1,3}\.){3}([0-9]){1,3}))|(\[((([0-9]){1,3}\.){3}([0-9]){1,3})\])))$/";
EDIT 2:
about the error you were getting
Warning: preg_match() [function.preg-match]: No ending delimiter '^' found ...
it found the ^ character as first character in your pattern, taking it as a delimiter, and complained because it did not find a matching ^ delimiter ending the pattern.
You can do this without preg_xx altogether:
function validate($email)
{
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
if (false === validate($email)) {
// invalid email given
}
It uses filter_var() together with FILTER_VALIDATE_EMAIL

Issue filtering words in contact form PHP based

I having a issue filtering bad words inside a contact form message.
It strips all the message except the first letter of the word.
Any help?
below is just getting the info
<?php
$error = '';
if(!empty($_POST['username'])) $username = $_POST['username'];
if(!empty($_POST['email'])) $email = $_POST['email'];
if(!empty($_POST['subject'])) $subject = check($_POST['subject']);
if(!empty($_POST['message'])) $msg = filter($_POST['message']);
this is the function I am trying to use to strip the bad words and replace them
$bad_words = array(
'word1' => 'gosh',
'word2' => 'darn',);
function filter($matches) {
global $bad_words;
$replace = $bad_words[$matches[0]];
return !empty($replace) ? $replace : $matches[0];
}
checks the drop down options and doesn't allow certain subjects to be emailed.
function check($str){
global $error;
if ($str == 'Mean Spirited Comment'){
$error = 'You sent a Mean-Spirited Comment';
} else if ($str =='Political Comment'){
$error = 'You sent a Political Comment';
}
return $str;
}
places the info and sends
$to = 'email#email.com';
if (!empty($subject) && !empty($msg) && !empty($email) && !empty($username)){
if ($error == ''){
mail($to, $subject, $msg, 'From:' . $email);
} else {
print $error;
}
}
?>
You could use str_replace since it can take array.
For instance:
$message = "Hello there my good friends! I am very happy to see you all today even though I feel like crap.";
$badWords = array("Crap", "Damnit", "Frack");
echo str_replace($badWords, "*", $message);
Results would be: Hello there my good friends! I am very happy to see you all today even though I feel like *.
Why re-invent new methods when PHP already offers plenty of useful ones? This should be enough to remove "bad words" from messages being sent.

PHPMailer Form Help

Is there any better way to stop spam coming through on my phpmailer script?
Also how would I go about adding formatting to this so its more readable when it gets sent through to email e.g. break lines
I hope my php syntax is correct - as i do not understand PHP.
<?php
# bool has_injection (String $var, [String $var, ...])
function has_injection () {
$values = func_get_args();
for ($i=0, $count=func_num_args(); $i<$count; $i++) {
if ( stristr($values[$i], "%0A") || stristr($values[$i], "%0D") || stristr($values[$i], "\\r") || stristr($values[$i], "\\n")
|| stristr($values[$i], "Bcc") || stristr($values[$i], "Content-Type") ) {
return true;
}
}
return false;
}
$error = '';
if (isset($_POST) && count($_POST)>0) {
# The form has been submitted
$course_title = $_POST['course_title'];
$course_date = $_POST['course_date'];
$course_code = $_POST['course_code'];
$course_fee = $_POST['course_fee'];
$break .= "\n";
$qual_subject_level = $_POST['qual_subject_level'];
$break .= "\n";
$email = $_POST['email'];
if ($name && $email && $subject) {
if (has_injection($name, $email, $subject)) {
# You've got another spammer at work here
$error = 'No spamming';
exit(0);
}
else {
# It's safe to send the message
mail('my#gmail.com',
$subject,
$course_title,
$course_code,
$course_fee,
$break,
$qual_subject_level,
$break,
$subject,
"From: $name <$email>");
}
}
else {
$error = 'Please fill in all the forms';
}
}
?>
One i use is have a text area and use your .css file to display:none it most bots dont read the css and thus think that the text box is shown and if it has content in it it's a bot if it does not then send your email.
E.G CSS
.antiBot{display:none};
HTML
<input type="text" class="antiBot" name="antibot" value="" />
PHP
<?php
if($_REQUEST['antibot'] == ""){
// send your email
}else{
// bot using your system
}
?>
How ever change the name or bot will get be able to notice its a trap and will get around it with little work insted of having to parse the CSS file for your site
So in your case just rap the if above around your code as for formatting of an email if its plain text use dubble quotes and \n (newline) but it wont work in single quotes.

Categories