• Aucun résultat trouvé

Safely including the user’s address in email headers

Dans le document DAVID POWERS Y (Page 158-161)

} }

Don’t forget the extra curly brace to close the elsestatement.

5.Just one final change is required to the section of code that builds and sends the email. If suspect content is detected, you don’t want that code to run, so amend the condition in the opening ifstatement like this:

// go ahead only if not suspect and all required fields OK if (!$suspect && empty($missing)) {

// build the message

6.Save contact.php, and test the form. It should send normal messages, but block any message that contains any of the suspect phrases. Because the ifstatement in step 4 sets $mailSentto falseand unsets $missing, the code in the main body of the page displays the same message that’s displayed if there’s a genuine problem with the server. A neutral, nonprovocative message reveals nothing that might assist an attacker. It also avoids offending anyone who may have innocently used a suspect phrase. You can check your code against contact08.phpin the download files.

Safely including the user’s address in email headers

Up to now, I’ve avoided using one of the most useful features of the PHP mail()function:

the ability to add extra email headers with the optional fourth argument. A popular use of extra headers is to incorporate the user’s email address into a Reply-To header, which enables you to reply directly to incoming messages by clicking the Replybutton in your email program. It’s convenient, but it provides a wide open door for an attacker to supply a spurious set of headers. With PHP Solution 5-5 in place, you can block attacks, but safely pass filtered email addresses to the mail()function.

You can find a full list of email headers at www.faqs.org/rfcs/rfc2076, but some of the most well-known and useful ones enable you to send copies of an email to other addresses (Cc and Bcc), or to change the encoding (often essential for languages other than Western European ones). Each new header, except the final one, must be on a sepa-rate line terminated by a carriage return and new line character. This means using the \r and \nescape sequences in double-quoted strings.

Let’s say you want to send copies of messages to other departments, plus a copy to another address that you don’t want the others to see. Email sent by mail()is often iden-tified as coming from nobody@yourdomain(or whatever username is assigned to the web server), so it’s also a good idea to add a more user-friendly “From” address. This is how you build those additional email headers and pass them to mail():

$additionalHeaders = "From: Japan Journey<feedback@example.com>\r\n";

$additionalHeaders .= "Cc: sales@example.com, finance@example.com\r\n";

$additionalHeaders .= 'Bcc: secretplanning@example.com';

$mailSent = mail($to, $subject, $message, $additionalHeaders);

5

If you want to send the email in an encoding other than iso-8859-1(English and Western European), you need to set the Content-Typeheader. For Unicode (UTF-8), set it like this:

$additionalHeaders = "Content-Type: text/plain; charset=utf-8\r\n";

The web page that the form is embedded in must use the same encoding (usually set in a

<meta>tag).

Hard-coded additional headers like this present no security risk, but anything that comes from user input must be filtered before it’s used. So, let’s take a look at incorporating the user’s email address into a Reply-Toheader. Although PHP Solution 5-5 should sanitize any user input, it’s worth subjecting the email field to a more rigorous check.

Continue working with the same page. Alternatively, use contact08.phpfrom the down-load files.

1.Although I suggested at the end of PHP Solution 5-3 that you add the emailfield to the $requiredarray, there may be occasions when you don’t want to make it required. So, it makes more sense to keep the code to validate the email address separate from the main loop that processes the $_POSTarray.

If emailis required, but has been left blank, the loop will have already added emailto the $missingarray, so the message won’t get sent anyway.

If it’s not a required field, you need to check $emailonly if it contains some-thing. So you need to wrap the validation code in an ifstatement that uses

!empty(). An exclamation mark is the negative operator, so you read this as

“not empty.”

Insert the code shown in bold immediately after the loop that processes the $_POST array. It contains a complex line, so you may prefer to copy it from contact09.php.

// otherwise, assign to a variable of the same name as $key elseif (in_array($key, $expected)) {

// regex to ensure no illegal characters in email address

$checkEmail = '/^[^@]+@[^\s\r\n\'";,@%]+$/';

// reject the email address if it doesn't match if (!preg_match($checkEmail, $email)) {

array_push($missing, 'email');

} }

PHP Solution 5-6: Automating the reply address

// go ahead only if not suspect and all required fields OK if (!$suspect && empty($missing)) {

Designing a regular expression to recognize a valid-looking email address is notori-ously difficult, and many that you find in books or on the Internet reject valid email addresses. Instead of striving for perfection, $checkEmail simply checks for an

@mark surrounded by at least one character on either side.

More important, it rejects any attempt to append spurious email headers. If the contents of $emaildon’t match the regex, emailis added to the $missingarray.

I decided not to create a special variable to indicate a suspected attack because the user may have innocently mistyped the email address. Moreover, it keeps the logic of the code simple. If the $missingarray contains any elements, the message isn’t sent, which is the whole point: you’ve stopped the attack.

2.You now need to add the additional headers to the section of the script that sends the email. Place them immediately above the call to the mail()function like this:

// limit line length to 70 characters

If you don’t want emailto be a required field, there’s no point in using a nonexist-ent value in the Reply-Toheader, so I have wrapped it in a conditional statement.

Since you have no way of telling whether the Reply-Toheader will be created, it makes sense to put the carriage return and new line characters at the beginning of the second header. It doesn’t matter whether you put them at the end of one header or the start of the next one, as long as a carriage return and new line sepa-rates each header. For instance, if you wanted to add a Ccheader, you could do it like this:

Finally, don’t forget to add $additionalHeadersas the fourth argument to mail().

5

3.Save contact.phpand test the form. When you receive the email, click the Reply button in your email program, and you should see the address that you entered in the form automatically entered in the recipient’s address field. You can check your code against contact09.phpin the download files.

Dans le document DAVID POWERS Y (Page 158-161)