如何在没有验证码的情况下保护联系表单?

5

我网站上有几个表单,任何人(无需注册)都可以向指定的已注册用户发送消息。这种表单很简单,我希望保持这种方式。如果我不想使用任何验证码,那么保护联系表单免受垃圾邮件和机器人的最佳方法是什么?

Your message:...
Your e-mail:...
[Send]

检查服务器和客户端的清晰验证,并使邮件功能正常,不要保存在数据库中。 - Sam Arul Raj T
保护什么?如果是垃圾邮件机器人,那么 CAPTCHA 的目的就是区分机器人和人类,因此所有机器人保护机制都属于这个范畴。 - Matt Gibson
相关:https://dev59.com/3nE85IYBdhLWcg3wzm1p - Maxime Pacary
可能是Good Form Security - no CAPTCHA的重复问题。 - Patrik
6个回答

11
您可以通过使用“表单密钥”技术来保护您的表单:
1. 在向用户显示表单时,为此单个表单提交生成一个随机ID(“表单密钥”)。将表单密钥存储在会话变量中。在隐藏输入字段中提交“表单密钥”。
2. 在服务器端,检查提交的表单密钥是否与存储在会话变量中的密钥相匹配。在使用后立即使其失效并从会话中删除。 没有验证码或类似的东西,无法真正保护表单免受滥用。 然而,使用这种技术,恶意的垃圾邮件发送者需要:
1. 通过HTTP为每个单独的表单提交请求表单以获取有效的唯一forkey 2. 解析DOM树/HTML代码以获取表单密钥 3. 以正确的格式提交它
此技术还可防止多次意外提交表单。
这是一个代码示例:
<?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 } ?>

谢谢。这是一个好的解决方案。但是,如果有人打开多个联系表单,它就不起作用了。用户不应该这样做,所以基本上这是一个好的解决方案。我会根据你的代码尝试做类似的事情,感谢你的建议。 - Lucas
@Lucas 我将代码改为在会话中存储有效表单键的列表,以响应您的多表单用例。此外,该方法还可以轻松地增强以检查超时。现在,表单密钥已经带有时间戳。您唯一需要做的是插入一个检查是否表单密钥已经超时的操作。 - Kaii
你如何实现上述解决方案? - Dz.slick

7
在我看来,许多网站(除了售票网站、像谷歌这样的主要网站和其他将被针对的网站)可以通过结合多种不同的技术来防止评论/表单垃圾邮件而无需使用验证码。我最近创建了一个开源的PHP项目,实现了以下测试以确定提交是否可能是合法的或垃圾邮件。
  • 隐藏表单字段 - 如果填写了隐藏表单字段,则表明可能是垃圾邮件
  • 时间表单提交 - 如果表单填写得太快或太慢,则表明可能是垃圾邮件
  • 过多的URL - 如果评论字段有太多的URL(数字可配置),则表明可能是垃圾邮件
  • 鼠标移动 - 如果用户不使用鼠标,则表明可能是垃圾邮件
  • 使用键盘 - 如果用户不使用键盘,则表明可能是垃圾邮件
  • 验证引荐者 - 如果HTTP引荐者与表单URL不匹配,则我们不应接受提交。
  • 验证电子邮件 - 如果表单中提供的电子邮件地址在语法上无效,则我们不应接受提交。

这是该项目的URL。我喜欢Kaii上面的测试,它会成为phpFormProtect的一个很好的附加测试。 https://github.com/mccarthy/phpFormProtect


隐藏表单字段和时间表单提交+1:当我管理一个每天有50个垃圾注册的Drupal网站时,我尝试了不同的验证码,但仅产生了暂时的效果;一旦我安装了HoneyPot(一个插件实现这两个措施-隐藏字段和时间提交)并将隐藏字段名称设置为“手机”,我在一年多的时间里没有收到任何垃圾注册信息。 - YakovL
当然,这对于小型网站来说是一个很好的措施;如果有人将您的网站作为垃圾邮件的目标,他们可以轻松处理此问题。 - YakovL
同意。正如我在答案中提到的那样,对于许多网站来说,这已经足够了。我的目标是平衡用户体验和垃圾邮件/机器人检测。 - Dan McCarthy

7

因此有以下选项:

  1. 最大化查询/IP地址
  2. 添加安全问题
  3. Captcha (即使您不喜欢它)
  4. 通过电子邮件发送验证
  5. 通过JavaScript提交数据

这些选项的详细信息以及我的意见。

  1. 它很好地防止发送许多消息,但仍然会有一些副本进入。如果您认为这是可以接受的,那么可能会起作用。注意:垃圾邮件发送者可以使用代理或动态IP,但这可能会很慢。也许考虑不阻止用户,而是在他们发送过多电子邮件时添加验证码。
  2. 这到底是什么?这些问题类似于“10 + 1”或“十加一”或“今天是星期几?”。如果您的网站只涉及很少的语言,它们可能效果很好。验证码仍然更好,但在某些情况下这也很有效。
  3. 尽管您不喜欢它,但我仍然认为这是最好的解决方案。添加一个reCAPTCHA并不难,但它将防止90%或更多的垃圾邮件发送者。但是这有两个问题:1.有时人类也无法很好地阅读它,2.垃圾邮件发送者可以使用人来解决它,并支付极小的金额(如$0.001/验证码),有时他们会这样做。但这同样适用于第2种情况。
  4. 可能是好的,但如果垃圾邮件发送者注意到此事,他们可以生成随机电子邮件地址并通过SMTP进行验证。但他们通常会去最容易攻击的目标而离开。
  5. 很好,垃圾邮件发送者无法使机器人像单击一样工作,但他们可以编写代码使单击变得不必要。但最容易攻击的目标规则仍然存在。

在我看来,最好的解决方案是3,然后是2,然后是1,接着是4和5。


谢谢,这非常有用。我想我会先专注于第4点,然后是第1点。 - Lucas

1

您可以从IP地址实现时间限制。因此,如果垃圾邮件机器人尝试在特定时间间隔(例如1小时)内一遍又一遍地发送表单,则应拒绝请求。

您需要在服务器端执行此操作,通过检查和存储用户的IP地址来完成。然后,当他们再次发送表单时,请检查用户是否已被阻止IP地址。

这也是StackOverflow 2年前实施的方法。


0

使用:iScramble

它基本上接受一个字符串(您在PHP中存储的表单HTML作为字符串)并生成一块混淆的Javascript代码。

它可以帮助隐藏用于保护表单的表单密钥,或者完全将您的表单从机器人中隐藏起来。

自90年代以来,我一直在使用它(以上链接中的页面显然至少有那么老),我从未收到过表单垃圾邮件。


0

您可以采取一些措施来证明用户是人类。如果您想保持简单,也许可以提供一个数学问题的字段,例如“2 + 5?”但这并不像验证码那样安全。但是,如果可能的垃圾邮件不会打扰您,那么这样做就可以了。


1
为什么不呢?如果问题以适当的格式提出,那么它比克林贡语验证码更清晰易懂。 - Alfabravo
@Alfabravo 没错,我经常读验证码时感到很困难(是的,我在说你,StackOverflow)。一个带有乱码字母的图像已经过时了。 - CodeCaster
有些脚本可以捕获文本并解决问题,这就是我指出它的原因。 - Tiago César Oliveira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接