I have tried both real escape string and other php methods but I am not sure I am using them correctly. This code shows my input and then the ajax post, where and how would I preform the sanitation?
Please note there is no data base connection so all the character stripping would have to be done in jQuery somehow.
Would this be more of the correct direction to go in?
<?php
$name = $_POST["name"];
$email = $_POST["email"];
$phone = $_POST["phone"];
$message = $_POST["message"];
$msg = "
Name:$name
Email:$email
Phone:$phone
Comment:
$message";
function checkInput($msg) {
$msg = #strip_tags($msg);
$msg = #stripslashes($msg);
$invalid_characters = array("$", "%", "#", "<", ">", "|");
$msg = str_replace($invalid_characters, "", $msg);
return $msg;
}
$to = "email address";
$subject = "name";
$message = $msg;
$headers = "Contact form enquiry";
mail($to,$subject,$message,$headers);
?>
You perform sanitation immediately before you put the text into some code or specific data format.
So in the code you have here:
var dataString = 'name=' + name + '&email=' + email + '&phone=' + phone + '&message=' + message;
You would escape each variable before you put it into the URL. You can do that with encodeURIComponent. However, you are using jQuery ajax so you shouldn't be doing that by hand in the first place.
data: { 'name': name, 'email': email, 'phone': phone, 'message': message},
And in the HTML you are generating:
.append("<h2 class='text-center form_submit_text'>Hi " + name + ", we will contact you soon </p>")
should be:
var heading = jQuery("<h2>").addClass('text-center').addClass('form_submit_text').text("Hi " + name + ", we will contact you soon);
$('#thanks').empty().append(heading);
You might also need to do some escaping in your PHP, such as before putting data into SQL.
You validate and sanitize in bin/mail.php. See filter_var for the built in ways to validate and sanitize incoming data. For example, for email you can do
if (filter_var($_POST['email']), FILTER_VALIDATE_EMAIL)) {
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL));
}
Phone numbers would required a regular expression to validate and sanitize (so it only contains numbers and/or re-formats to your preferred format). Free text like $message should use FILTER_SANITIZE_STRING.
Related
I am using a simple contact form: http://luiszuno.com/previews/formy/
and the issue is that whenever the character "&" is typed on the form the rest of the message after "&" is not received on the email. Edit: Added the code, form works correctly other than the missing data issue. What could be causing this issue?
jQuery(document).ready(function($) {
$("#formy").on( "submit", function( event ) {
$(this).serialize();
});
// Hide messages
$("#formy-success").hide();
$("#formy-error").hide();
$("input,textarea").blur(function(){
$(this).css("border-color","#596a87");
});
// on submit...
$("#formy #submit").click(function() {
$(this).serialize();
// Required fields:
//name
var name = $("#name").val();
if(name == "" || name == "Name *"){
$("#name").focus();
$("#formy-error").fadeIn().text("Name required");
$("#name").css("border-color","#a22528");
return false;
}
else {$("#name").css("border-color","#596a87");}
// email
var email = $("#email").val();
if(email == "" || email == "Email *"){
$("#email").focus();
$("#formy-error").fadeIn().text("Email required");
$("#email").css("border-color","#a22528");
return false;
}
else {$("#email").css("border-color","#596a87");}
// email validation
function validateEmail(email) {
var filter = /^([a-zA-Z0-9_.+-])+\#(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
return filter.test(email);
}
if (!validateEmail(email)) {
$("#formy-error").fadeIn().text("Invalid email address");
$("#email").css("border-color","#a22528");
return false;
}
//budget
var budget = $("#budget").val();
if(budget == "" || budget == "Budget"){
return false;
}
// comments
var comments = $("#comments").val();
if(comments == "" || comments == "Message *"){
$("#comments").focus();
$("#formy-error").fadeIn().text("Message required");
$("#comments").css("border-color","#a22528");
return false;
}
else {$("#comments").css("border-color","#596a87");}
// send mail php
var sendMailUrl = $("#sendMailUrl").val();
// Retrieve values for to, from & subject at the form
var to = $("#to").val();
var from = $("#from").val();
var subject = $("#subject").val();
// Create the data string
var dataString = 'name=' + name
+ '&email=' + email
+ '&comments=' + comments
+ '&to=' + to
+ '&from=' + from
+ '&budget=' + budget
+ '&subject=' + subject;
// ajax
$.ajax({
type:"POST",
url: sendMailUrl,
data: dataString,
success: success()
});
});
// On success...
function success(){
$("#formy-success").fadeIn(250).text("Thanks, I will contact you soon!");
$("#formy-error").hide();
$("#formy fieldset").slideUp(250);
}
return false;
});
send-mail.php
<?php header("Content-Type: text/html; charset=utf-8");
//vars
$subject = $_POST['subject'];
$to = explode(',', $_POST['to'] );
$from = $_POST['email'];
//data
$msg = "NAME: " .$_POST['name'] ."<br>\n";
$msg .= "EMAIL: " .$_POST['email'] ."<br>\n";
$msg .= "BUDGET: " .$_POST['budget'] ."<br>\n";
$msg .= "COMMENTS: " .$_POST['comments'] ."<br>\n";
//Headers
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html; charset=UTF-8\r\n";
$headers .= "From: <".$from. ">" ;
//send for each mail
foreach($to as $mail){
mail($mail, $subject, $msg, $headers);
}
?>
Your problem is that you are building your data string manually and not URI-encoding the data values you insert into the string. Therefore any raw & character in your text will be assumed to be part of the URI and not the data, meaning that it will be interpreted as indicating the start of the next parameter, rather than just a character in the text. URI-encoding this data will convert this and other special characters into their encoded equivalents, so they will not be mis-interpreted.
Now, you can certainly fix this by wrapping every field variable with encodeURIComponent(), but this is verbose and tedious:
var dataString = 'name=' + encodeURIComponent(name)
+ '&email=' + encodeURIComponent(email)
+ '&comments=' + encodeURIComponent(comments)
+ '&to=' + encodeURIComponent(to)
+ '&from=' + encodeURIComponent(from)
+ '&budget=' + encodeURIComponent(budget)
+ '&subject=' + encodeURIComponent(subject);
There are better ways:
Currently, your $(this).serialize(); will not work because this represents the clicked button, not the form. However if you point it at the form, you can use it to easily serialise the fields within it automatically, and jQuery will handle any encoding issues on your behalf. This saves on manual encoding, and on code to fetch each field value individually (although I note you currently need this for your validation, although there are other ways to implement validation which would remove that need, but that's another topic entirely).
$.ajax({
type:"POST",
url: sendMailUrl,
data: $("#formy").serialize(),
success: success //Unrelated: I also removed the brackets here, so it becomes a _reference_ to the "success" function - writing success() as you did means the function is immediately executed, and what gets passed to jQuery is the _result_ of the function, which isn't what you want in this case
});
As you can see this is much less hassle. And if you ever add more fields to your form in future, you won't have to change this bit of code at all.
Another side point - you're going to a lot of trouble to validate the form input using JavaScript. This is nice and user-friendly, but it provides no security whatsoever. On the server side you appear to be happily inserting whatever values the browser sends directly into your email. Any user with a small amount of knowledge can either modify your JavaScript with their developer tools, or turn off JS, or just use another tool entirely (e.g. PostMan, or a custom application) to fire HTTP requests at your server without ever touching your form. They could potentially send problematic values which might screw up your email - e.g. a different "from" value, or some nasty HTML, or if you're interacting with a database anywhere in your application, carry out SQL Injection attacks to mess that up. You should always validate all incoming data in your PHP code for security issues, and to ensure it meets your business rules, before using it for anything else.
Using the encodeURIComponent() as #ADyson recommended fixed the missing data/urls from received email when using the contact form.
var comments = $("#comments").val();
var e_comments = encodeURIComponent(comments);
I am trying to make a form for people to fill out that requests a service. They must for one of the fields enter a phone number. To ensure that the phone number is valid I have set up the PHP to send an SMS to the provided number using my providers API.
The sms sends okay and it sends with a PHP variable enclosed. (A Random int between 9999 and 99999.) This code is sent successfully but every submit the code changes as the function runs. The problem with this is that you receive the text but when you enter it in the code has already changed as you submitted the form to validate. I have tried using a button type in html but cant figure our how to run the PHP using it.
Below is the first form they fill out which sends the message.
<?php
$confirmcode = rand(9999, 99999);
//echo "<br/><br/><br/><br/><br/><br/>Confirmation Code:"."<br/><br/>$confirmcode<br/><br/><br/>";
if (isset($_POST['submit'])){
// Validation
//Check Name is non-blank
if( 0=== preg_match("/\S+/", $_POST['fname'])){
$errors['first_name'] = "Please enter your name.";
}
if (0=== preg_match("/^[\+0-9\-\(\)\s]{10,}+$/", $_POST['phone'])){
$errors['phone'] = "Please enter a phone number";
}
//Check Email has required symbols
if (0=== preg_match("/.+#.+\..+/", $_POST['email'])){
$errors ['email'] = "Please enter a valid email address.";
}
//End Validation
$errors = array();
$name = $_POST['fname'];
$address = $_POST['address'];
$phone = $_POST['phone'];
$email = $_POST['email'];
//Sending Confirmation SMS code to confirm phone number.
// Declare the security credentials to use
$username = "############";
$password = "############";
// Set the attributes of the message to send
$message = "Hello " ."$name" ."Your confirmation code is: " ."$confirmcode".". " ."Please enter it .". "on he appraisal request form.";
$type = "1-way";
$senderid = "SanctuaryRE";
$to = $_POST['phone'];
// Build the URL to send the message to. Make sure the
// message text and Sender ID are URL encoded. You can
// use HTTP or HTTPS
$url = "http://api.directsms.com.au/s3/http/send_message?" .
"username=" . $username . "&" .
"password=" . $password . "&" .
"message=" . urlencode($message) . "&" .
"type=" . $type . "&" .
"senderid=" . urlencode($senderid) . "&" .
"to=" . $to;
// Send the request
$output = file($url);
// The response from the gateway is going to look like
// this:
// id: a4c5ad77ad6faf5aa55f66a
//
// In the event of an error, it will look like this:
// err: invalid login credentials
$result = explode(":", $output[0]);
//END SMS
header("Location: process.php");
}
?>
And the form they are pushed onto to confirm the code they were sent.
<html>
<div class="wrapper2">
<form action="" method="POST">
<input type="text" class="textfieldlong" placeholder="Confirmation Code" name="giventoken">
<input type="button" class="submit" value="Verify Phone Number" name="submit2" id="submit2">
</form>
</html>
<style>
.wrapper{
display:none;
}
</style>
</div>
<?php
include "index.php";
//$token = $_POST['giventoken'];
//if (!strcmp($confirmcode,$token)){
// echo "Match";
//}
echo "$confirmcode";
?>
The random int is generated right at the beginning. Please help! Thnx. :D
The random number is being created every time.
You will have to database the number for each specific user and then check when they enter the code. You cant expect the user to get the text and confirm immediately. The only real way to do this is to database it.
Setup a table with the userid and the code, then check when they confirm that you sent that code to that user.
I have been on this for days and days, and am at the point that I have pulled out so many hairs that I now have just one hair left on my head. That hair is my last bit of pride. But seriously though, I have found dozens of answers but none seem to apply to my problem!
I have an e-mail form for a website I made. The site and form are made in Flash (AS3), the script for processing the e-mail is an external php file. The e-mail form works just fine, except for when I use certain characters:
% is not shown in the e-mail, including any text directly behind it
when a &, < or > is present, the form will say 'sending..' but not go beyond that point; I don't receive any e-mail.
All (or most at least) other characters like !##$^*_+-=~` are no problem.
I have already made sure both AS3 and php codes have
"MIME-Version: 1.0; Content-Type: text/html; charset=utf-8" is included in my sending if check in the php file;
the textfields in AS3 are set to htmlText instead of just text.
My scripts:
mail.php
if( $yourName == true ) {
$sender = $fromEmail;
$yourEmail = "myemail#example.com"; // Here i of course use my own email address
$ipAddress = $_SERVER['REMOTE_ADDR']; // This gets the user's ip Address
$emailMsg = "Van: $sender\r\n" .
"Name: $yourName\r" .
"Subject: $yourSubject\n\n" .
"$yourMsg\n\n\n\n" .
"------------------------------\r" .
"Sent from IP-address $ipAddress\r" .
"X-Mailer: PHP/" . phpversion();
# these are three (out of many) things I tried to work around the problem #
//$emailMsg = str_replace( '&', "&", $emailMsg );
//$emailMsg = htmlspecialchars($emailMsg, ENT_QUOTES);
//$emailMsg = mysql_real_escape_string($emailMsg);
$return = "From: $sender\r\n";
if( mail($yourEmail, "$yourSubject", $emailMsg, $return, "MIME-Version: 1.0; Content-Type: text/html; charset=utf-8")) {
echo "sentStatus=yes";
}
else {
echo "sentStatus=no";
}
}
?>
FormScript.as
package {
/*required imports*/
public class FormScript extends Sprite {
/*here are the variable declarations*/
public function FormScript() {
sendbtn.buttonMode = true;
sendbtn.addEventListener(MouseEvent.CLICK, submit);
resetbtn.buttonMode = true;
resetbtn.addEventListener(MouseEvent.CLICK, reset);
urlRequest.method = URLRequestMethod.POST;
/*here are are some positionings and addchilds*/
function init():void {
//Set all fields to empty
yourName.htmlText = "";
fromEmail.htmlText = "";
yourSubject.htmlText = "";
yourMsg.htmlText = "";
valid.text = "";
}
function submit(e:MouseEvent):void {
//Check to see if any of the fields are empty
if(yourName.htmlText == "" || fromEmail.htmlText == "" ||
yourSubject.htmlText == "" ||yourMsg.htmlText == "" ) {
valid.text = "All fields must be filled in";
}//Check if you're using a valid email address
else if(!checkEmail(fromEmail.htmlText)) {
valid.text = "Please enter a valid e-mail address";
}
else {
valid.text = "Sending..";
var emailData:String =
"name=" + yourName.htmlText +
"&from=" + fromEmail.htmlText +
"&subject=" + yourSubject.htmlText +
"&msg=" + yourMsg.htmlText;
var urlVars:URLVariables = new URLVariables(emailData);
urlVars.dataFormat = URLLoaderDataFormat.TEXT;
urlRequest.data = urlVars; varLoad.load( urlRequest );
varLoad.addEventListener(Event.COMPLETE, thankYou );
}
}
function reset(e:MouseEvent):void {
init(); //call the initial clear function
}
function checkEmail(s:String):Boolean {
//yourMsg.text = escape("&");
//This tests for correct email address
var p:RegExp = /(\w|[_.\-])+#((\w|-)+\.)+\w{2,4}+/;
var r:Object = p.exec(s);
if( r == null ) {
return false;
}
return true;
}
function thankYou(e:Event):void {
var loader:URLLoader = URLLoader(e.target);
var sent = new URLVariables(loader.data).sentStatus;
//valid.text = sent;
if( sent == "yes" ) {
valid.text = "Thank you for your e-mail!"; timer = new Timer(500);
timer.addEventListener(TimerEvent.TIMER, msgSent);
timer.start();
}
else {
valid.text = "Something went wrong, please try again";
}
}
function msgSent(te:TimerEvent):void {
if(timer.currentCount >= 10) {
init();
timer.removeEventListener(TimerEvent.TIMER, msgSent);
}
}
}
}
}
Keywords:ampersand special characters symbols less-than less than greater-than greater than please don't edit this, it's for others to find this question because you can't search for an '&' and such.
The most obvious culprit here is messy way you're creating the emailData string. As a first step I'd recommend reformatting it to the following:
var urlVars:URLVariables = new URLVariables();
urlVars.name = yourName.htmlText;
urlVars.from = fromEmail.htmlText;
urlVars.subject = yourSubject.htmlText;
urlVars.msg = yourMsg.htmlText;
I think this will automatically URI encode the values, but if not, use encodeURI() as suggested by Mark Knol.
Within Flash, the values need to be encoded, otherwise the querystring could be corrupted.
var emailData:String =
"name=" + encodeURI(yourName.htmlText) +
"&from=" + encodeURI(fromEmail.htmlText) +
"&subject=" + encodeURI(yourSubject.htmlText) +
"&msg=" + encodeURI(yourMsg.htmlText);
Try to use
$emailMsg = utf8_decode($emailMsg);
I decode all my strings I get from Flash.
If this doesn't help, use
$emailMsg = urldecode($emailMsg);
Or both :D
I'm using PHP for sending an e-mail. The values in the e-mail are depending on the inputs of a form. But for some reason, the mail is suddenly not sending. It did before. What's wrong with my code?
Orders are placed correctly in the database, so no error there.
if ($order->addOrder($_DB)) {
$user = "SafetyCam.be";
$verzonden = FALSE;
$firstname = $_POST['firstname'];
$lastname = $_POST['lastname'];
$address = $_POST['address'];
$zip = $_POST['zip'];
$place = $_POST['place'];
$country = $_POST['country'];
$phone = $_POST['phone'];
$email = $_POST['email'];
$twogig = $_POST['vn_qty'];
$fourgig = $_POST['ja_qty'];
$total = $_POST['total'];
$totaal = (($twogig * 50) + ($fourgig *80) + 2.5);
$headers = 'From: info#something.be';
$to = 'me#gmail.com';
$subject = 'Bevestiging bestelling';
$message = 'Hello $firstname,
You placed the following order on our website.
- $twogig x 2GB SafetyCams ordered
- $fourgig x 4GB SafetyCams ordered
+ shippingcosts (2,5 EUR)
The total cost for this order amounts to $totaal EUR.
Your products will be delivered as quickly as possible after receipt of your payment.
Please transfer the order amount of $totaal EUR into account X.
After payment, the products will be sent to the following address:
$firstname $lastname
$address
$zip $place
$country
We hope you will be very happy with your purchase.
Sincerely yours";
if (mail($to, $subject, $message, $headers)) {
$verzonden = TRUE;
$feedback = '<div class="feedbackheader">Thanks</div><br / >';
} else {
$verzonden = FALSE;
$feedback = '<div class="feedbackheader">Error!</div>'; }
}
else {
$feedback = '<div class="feedbackheader">Error!</div>';
}
}
Why do you open your mail $message with a single quote and end it in a double quote.
You should open and end with both double quotes, especially since you use PHP variables inside.
$message = 'Hello $firstname"; //Wrong
$message = "Hello $firstname"; // Works
You've opened the "message" string with an apostrophe ' but tried to close it with a quotation mark ". The SO syntax highlighter gives it away!
You have started your variable $message = 'Hello $firstname, with single quote and end it with double quote, what you need to do is just make
$message = "Hello $firstname
if you put it in single quote php wont scan your variable content for varible like $firstname
Your $message variable starts the string with a ' but ends it with a ", so all the code after it is included in the variable until another ' which happens when your defining $feedback.
Basically you are not closing the string, and therefore your entire code is being changed. If you are using color coding you should have seen this (I can see it from your question).
Also, if you are using single quotes, you cannot use inline variables.
$var = 1;
echo '$var'; // ouput: $var;
echo "$var"; // output: 1
You start your message-string with a single quote (') and try to end it with a double quote, thus your logic is parsed incorrectly.
I use SwiftMailer:
require_once('../lib/swiftMailer/lib/swift_required.php');
function sendEmail(){
//Sendmail
$transport = Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs');
//Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);
$body="Dear $fname,\n\nYour job application was successful. \n\nYours,\n\nEamorr\n\n\n\n\n\n\n";
//Create a message
$message = Swift_Message::newInstance('Subject goes here')
->setFrom(array($email => "no-reply#yourdomain.com"))
->setTo(array($email => "$fname $lname"))
->setBody($body);
//Send the message
$result = $mailer->send($message);
}
I did a webpage for a client that involved a series of text boxes asking for specific information such as a person's name, e-mail address, company, etc. Along with a button that would e-mail the information to my client. Whenever I tested the button it seemed to work perfectly, I uploaded the page and thought I was done. But, the other day my client got this email from the site:
Name: rfhopzdgmx rfhopzdgmx
Email: envlxw#lnlnsm.com
Company: zUDXatAfoDvQrdH
Mailing Address:
AaSsXklqpHIsoCNcei
gXsimMPRBYZqq
vGLvZraZNdpOAV, ChsmuibE PoKzaSCubXPRI
Home Phone: CIJbIfjMfjIaTqAlD
Work Phone: JFLZBOvru
Cell Phone: XlFJTTFGiTTiiFQfy
Fax: UEJMOVZodWPkKxew
Comments:
sPvSCE hgetwoguderu,*
[url=http://atyktjlxcznl.com/]atyktjlxcznl[/url],
[link=http://nudvfcehwpyg.com/]nudvfcehwpyg[/link], http://lvvwkbzbhnzp.com/
Note: The * line contained HTML link code, I just don't know how to get this site to show it.
Here is the PHP code in the site for the e-mail button.
<?php
//This Sends A Formatted Text Email Using The Text Boxes
if ($_POST['submit']){
//This Gets The Form Data
$fname = $_POST['fName'];
$lname = $_POST['lName'];
$email = $_POST['email'];
$company = $_POST['co'];
$address1 = $_POST['address1'];
$address2 = $_POST['address2'];
$city = $_POST['city'];
$state = $_POST['state'];
$zip = $_POST['zip'];
$homep = $_POST['homeP'];
$workp = $_POST['workP'];
$cellp = $_POST['cellP'];
$fax = $_POST['fax'];
$comments = $_POST['txaOutputField'];
//echo "<script language = 'javascript'>alert('YAY');</script>";
if ($fname && $lname && $email && $comments){ //Check If Required Fields Are Filled
//This Sets The SMTP Configuration In php.ini
ini_set("SMTP", "smtp.2ndsourcewire.com");
//This Replaces Any Blank Fields With 'None's
if ($company == ""){
$company = "None";
}
if ($address1 == ""){
$address1 = "None";
}
if ($city == ""){
$city = "None";
}
if ($state == ""){
$state = "None";
}
if ($zip == ""){
$zip = "None";
}
if ($homep == ""){
$homep = "None";
}
if ($workp == ""){
$workp = "None";
}
if ($cellp == ""){
$cellp = "None";
}
if ($fax == ""){
$fax = "None";
}
//This Creates The Variables Necessary For The Email
$to = "CLIENT EMAIL WHICH I'M CENSORING";
$subject = "Email from 2ndSourceWire.com";
$from = "From: noreply#2ndsourcewire.com";
$secondEmail = "MY EMAIL WHICH I'M ALSO CENSORING";
if ($address2 == ""){
$body = "Name: $fname $lname\n".
"Email: $email\n".
"Company: $company\n\n".
"Mailing Address:\n".
"$address1\n".
"$city, $state $zip\n\n".
"Home Phone: $homep\n".
"Work Phone: $workp\n".
"Cell Phone: $cellp\n".
"Fax: $fax\n\n".
"Comments:\n".
"$comments";
}
else {
$body = "Name: $fname $lname\n".
"Email: $email\n".
"Company: $company\n\n".
"Mailing Address:\n".
"$address1\n".
"$address2\n".
"$city, $state $zip\n\n".
"Home Phone: $homep\n".
"Work Phone: $workp\n".
"Cell Phone: $cellp\n".
"Fax: $fax\n\n".
"Comments:\n".
"$comments";
}
//This Sends The Email
mail($to, $subject, $body, $from);
mail($secondEmail, $subject, $body, $from);
echo "<script language = 'javascript'>alert('The email was sent successfully.');</script>";
}
else {
//The Required Fields Are Not Filled
echo "<script language = 'javascript'>alert('Please fill your first name, last name, email address, and your comment or question.');</script>";
}
}
?>
I'm a little dumbfounded on how this happened, the client mentioned a couple e-mails of this, so I don't think it is a random glitch. Also, the e-mail address was formatted like an e-mail address, so someone or some program was interpreting the labels next to each text box. I also noticed that the first and last names entered are the same word, even though they were in different text boxes, I'm thinking its some spam program, but wouldn't they try to advertise something and make money, rather than just spouting out random text? Also, the comments section makes no sense to me at all, the links goto nowhere and they're all perfectly formatted, a random person just screwing around wouldn't know those tags, and a programmer doing it wouldn't bother with it, but also neither would a program.
I have no idea what caused this or how to fix it, I'm drawing a blank here. Anyone have any ideas?
A spammer/bot entered duff data into your page and you dutifully sent it on in your application.
Why do you think this is a mystery?
add a CAPTCHA to stop it happening. If you dont what to write your own you can use reCAPTCHA
even a simple question like "are you a human Y/n?" or "2+2?" will stop the bot,
also using some js to set an hidden value on submit and check for that on the server.
some validation on $email and $phone would be nice to have.
Instead of making people try to read CAPTCHAs, I like to have four text boxes in a row and ask the user to check two random ones (e.g. "Please check the first and third boxes") and make sure those are the only two checked in the validation.