5

On my website I have a couple of forms where someone (without registration) can send message to specified, registered user. The form is simple, and I want to keep that way. What is the best way to protect a contact form againts spam and bots if I don't want to use any captcha?

Your message:...
Your e-mail:...
[Send]
Lucas
  • 2,924
  • 8
  • 27
  • 32
  • Check for clear validation in server as well as client side, and make mail functionality,,don't save in DB – Sam Arul Raj T Mar 01 '12 at 12:55
  • Protect from what? If it's spam bots then the purpose of CAPTCHA is to tell bots from humans, so all bot protection mechanisms come under that umbrella term. – Matt Gibson Mar 01 '12 at 12:56
  • Related: http://stackoverflow.com/questions/2603363/good-form-security-no-captcha – Maxime Pacary Mar 01 '12 at 13:04
  • Possible duplicate of [Good Form Security - no CAPTCHA](https://stackoverflow.com/questions/2603363/good-form-security-no-captcha) – Patrik Mar 22 '18 at 13:10

6 Answers6

11

You can protect your form with the form keys technique:

  1. When displaying the form to the user, generate a random ID ("form key") just for this single form submission. Store the form key in a session variable. Submit the "form key" in a hidden input field.
  2. On the server side, check the submitted form key against the one stored in the session variable. Immediately invalidate it after use. (delete it from the session)

Without a captcha or something comparable, it is impossible to really protect a form from abuse.

However, using this technique a malicious spammer would have to

  1. request for form via HTTP for each single form submission to obtain a valid unique forkey
  2. parse the DOM tree / HTML code to obtain the form key
  3. submit it in the correct format

This technique also protects your form from multiple accidential submissions.

Here is a code example:

<?php
session_start();
if (isset($_POST['form_submit'])) {
  // do stuff
  if (isset($_POST['formkey']) && isset($_SESSION['formkeys'][$_POST['formkey']])) {
    echo "valid, doing stuff now ... "; flush();
    // delete formkey from session
    unset($_SESSION['formkeys'][$_POST['formkey']]);
    // release session early - after committing the session data is read-only
    session_write_close();
    // do whatever you like with the form data here
    send_contact_mail($_POST);
  }
  else {
    echo "request invalid or timed out.";
  }
}
else {
  // show form with formkey
  $formkey = md5("foo".microtime().rand(1,999999));
  // put current formkey into list of valid form keys for the user session
  if (!is_array($_SESSION['formkeys']) {
    $_SESSION['formkeys'] = array();
  ]
  $_SESSION['formkeys'][$formkey] = now();
?>
<html>
<head><title>form key</title></head>
<body>
  <form method="POST">
    <input type="hidden" name="PHPSESSID" value="<?=session_id()?>">
    <input type="text" name="formkey" 
      value="<?= $formkey ?>">
    <input type="submit" name="form_submit" value="Contact us!">
  </form>
</body>
</html>
<?php } ?>
Kaii
  • 20,122
  • 3
  • 38
  • 60
  • Thanks. This is good solution. But it won't work if someone open more than one contact form. A user should not do this, so basically it is a good solution. I will try to do something similar based on your code, thanks for advice. – Lucas Mar 01 '12 at 13:27
  • @Lucas i adapted my code to store a *list of valid formkeys* in the session to react on your use case with multiple forms. Also, this approach can be easily enhanced to check for a timeout as well. the formkey is now stored with a timestamp.. the only thing you must do is insert a check if the formkeys has already timed out.. – Kaii Mar 01 '12 at 13:42
  • How do you implement the solutions above? – Dz.slick Jun 07 '12 at 14:48
7

In my opinion, many sites (excepting ticket sellers, major sites like google, and other sites that will be targeted) can prevent comment/form spam without a captcha by using a number of different techniques in combination. I recently created an open source PHP project that implements the following tests to determine if the submission is likely legitimate, or spam.

  • Hidden Form Field - If hidden form field is filled in, this is an indicator of spam
  • Time Form Submission - If form is filled out too fast or too slow, this is an indicator of spam
  • Too many URLs - If the comment field has too many URLs (Number is configurable) this is an indicator of spam
  • Mouse Movement - If the user does not use their mouse, this is an indicator of spam
  • Used Keyboard - If the user does not use their keyboard, this is an indicator of spam
  • Validate Referer - If the HTTP referer does not match the form URL, we shouldn't accept the submission.
  • Validate Email - If the email address provided in the form is not valid from a syntax perspective, we shouldn't accept the submission.

Here is the URL of the project. I like Kaii's test above, would make a great additional test for phpFormProtect. https://github.com/mccarthy/phpFormProtect

Dan McCarthy
  • 71
  • 1
  • 3
  • +1 for hidden form field and time form submission: when I was administrating a Drupal site with like 50 spam registrations a day I've tried different captchas and each gave only a temporal effect; once I've installed HoneyPot (a plugin which implements both this measures – hidden field and time submission) and set the hidden field name to "phone", I got 0 spam registrations for over a year – YakovL Mar 22 '18 at 12:18
  • PS of'course this is a good measures for a small site; if somebody *targets* your site as a place to spam, they can easily handle this – YakovL Mar 22 '18 at 12:22
  • Agreed. As I mentioned in my answer, for many sites this is more than enough. My goal is balancing user experience and spam/bot detection. – Dan McCarthy May 07 '18 at 15:50
7

So the options:

  1. Maximize query/IP
  2. Add security question
  3. Captcha (even if you don't like it)
  4. Sending e-mail to validate it
  5. Submitting data via JavaScript

Details on these and my opinion on them.

  1. It works well to prevent sending many messages, but a few copies of them will still get in. If you think that's affordable, this might work. Note: a spammer can use proxies or dinamic IP, but that might be slow. Perhaps consider not blocking the user but adding a captcha if they send too many e-mails.
  2. What is this exactly? These are questions like "10+1" or "ten plus 1" or "What day is it?". They might work well - if your website will be in only very few languages. Captchas are still better but this works well in case.
  3. You don't like it, but I still say it's the best. Adding one reCAPTCHA isn't that hard but it will prevent 90% of the spammers - or more. But there are 2 problems with this: 1. sometimes human can't read it as well, 2. spammers could use people to solve it for a minimal (like $0.001/captcha) amount and sometimes they do. But that stands for case 2 as well.
  4. Could be good, but if spammers note this, they can generate random email addresses and validate it via SMTP. But they usually go to the easiest target and leave.
  5. Good, spammers can't make bots act like a click, but they can make codes which makes click non-required. But the easiest target rule stays.

In my opinion, the best is solution 3, then solution 2, then solution 1, then solution 4 and 5.

axiomer
  • 2,088
  • 1
  • 17
  • 26
  • Thanks, that is very useful. I think I will focus at the first at point 4, and then point 1. – Lucas Mar 01 '12 at 13:23
1

You can implement a time limit from an IP. So if a spam bot tries to send the form again and again (in a specific time interval, say 1 hour), the request should be rejected.

You'll have to do this on the Server-Side by checking and storing the IPs of the users. Then when they again send the form, check if the user is already in the blocked IPs for the time being.

This is also what StackOverflow implemented 2years back.

Community
  • 1
  • 1
Arjun Bajaj
  • 1,932
  • 7
  • 24
  • 41
0

Use: iScramble

It basically takes a string (your form html stored as a string in PHP) and generates a block of obfuscated Javascript.

It can help hide those form keys you are using to secure your forms, or completely hide your form from bots all together.

I've been using this since the 90's (the page at the link above is clearly at least that old) and I don't get form spam ever.

Chris
  • 955
  • 15
  • 20
0

You can do something to prove the user is human. If you want to keep simple, maybe a field asking a math question, like "2 + 5?", for example. But that's not so secure as a captcha could be. But, if the possible spam doesn't botter you, it's ok to do that.