Alessandro
Alessandro

Reputation: 908

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

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.

Upvotes: 2

Views: 470

Answers (2)

Alessandro
Alessandro

Reputation: 908

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.

Upvotes: 2

Ajjay Arora
Ajjay Arora

Reputation: 154

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. :)

Upvotes: 0

Related Questions