Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.
Some time ago I almost lost my hosting account for "sending spam". I had to remove my Contact Us page to ensure this doesn't happen again. But my site isn't so good without any support to people visiting my site. So a few days I've been reading some security articles and have coded a new version of my Contact Us page. Here it is:
<?php
require("config.php");
if ($_POST['action'] == "send") {
if (strtolower($_SERVER['HTTP_REFERER']) != strtolower("$url/support.php") && strtolower($_SERVER['HTTP_REFERER']) != strtolower("$url2/support.php")) {
echo "Error: An unsuspected error has occured.<br>";
$error = true;
}
if (strlen($_POST['name']) > 20) {
echo "Error: Name is too long. 20 characters max.<br>";
$error = true;
}
if (strlen($_POST['email']) < 40) {
echo "Error: E-mail address is too long. 40 characters max.<br>";
$error = true;
}
$email_pattern = '/^[^@\s<&>]+@([-a-z0-9]+\.)+[a-z]{2,}$/i';
if (!preg_match($email_pattern, $_POST['email'])) {
echo "Error: E-mail address is not valid.<br>";
$error = true;
}
if (!$error) {
$name = htmlspecialchars(strip_tags($_POST['name']), ENT_QUOTES);
$email = strip_tags($_POST['email']);
$message = htmlspecialchars(strip_tags($_POST['message']), ENT_QUOTES);
$mail = mail($e_mail, "$pname Support Request", $message, "FROM: $email");
if ($mail) {
echo "<font color='green'><b>Your message has been successfuly sent. Thank You.</b></font><br><br>";
}
else {
echo "<font color='red'><b>There has been an error. Please send your message to <a href='mailto:$e_mail'>$e_mail</a>. Thank You.</b></font><br><br>";
}
$sent = true;
}
echo "<br>";
}
if (!$sent) {
?>
<form action="contact.php" method="post">
<input type="hidden" name="action" value="send" />
Name:<br>
<input type="text" name="name" maxlength="20"><br><br>
Your E-mail address:<br>
<input type="text" name="email" maxlength="40"><br><br>
Message:<br>
<textarea name="message" rows="6" cols="30"></textarea><br><br>
<input type="submit" name="submit" value="Send">
</form>
Some information:
$url and $url2 are same url's but one with www and one without www, in case user would be browsing my site with www in it or without it.
The question is, is this script secure? Can I put it on my host without any risk to loose it? Also, does it change anything if hacker gets the code? I'll be sharing my site script with other people who also want to have site like mine, so I don't know if one of those people might hack into my site and make me loose my hosting.
- You're not verifying if the variables exist (eg: $POST['name'])
- What are you trying to achieve with checking a HTTP_REFERER header? It's not even a required one.. So any user-agent that doesn't provides it, would have difficulty using your site.
- Why would an e-mail address be limited to 40 characters?
- Your 'filtering' still doesn't remove newlines and thus allows for header injection.
The easiest way to create a 'safe' e-mail form is (imho) by hardcoding the recipient and all the headers... This way an attacker can try to abuse your via the mail body..
Thanks for the tips. Well, "hardcoding" doesn't sound so easy
I limit e-mail address to 40 characters to ensure attacker doesn't input a list of e-mails addresses.
I'm still new to security, maybe there's any good freeware contact script which I could use and also learn from it? It would be great. Or maybe some article about filtering data or anything else related to that?
The major weakness in your code is that you fail to filter $email.
Most spammers aren't going to try to exploit you by adding a large list of email addresses to the To header. (You still want to make sure that's not a possibility, of course.) That's too obvious, so they typically try to inject multiple Bcc headers in their email address, expecting that you'll use it exactly as you have. This lets them send spam to a very large list of recipients, and you're far less likely to notice, because you won't see the list in the email you receive. (You'd have to notice network traffic anomalies or something similar.)
There are two useful things you can do with $email:
1. Filter it using Cal Henderson's pattern: http://iamcal.com/publish/articles/php/parsing_email/
2. As defense in depth, use ctype_print() to ensure all of the characters are printable. (Newlines and carriage returns aren't.)