我做了一个PHP邮件程序脚本,对字段进行了基本验证,返回错误,如果一切正常,则提交。但是它也有一个蜜罐字段,不需要填写(我假设通过使用CSS隐藏它,无论如何垃圾邮件机器人都将填写该字段)。如果该字段不为空,它将打开一个文本文件并在其上写入/追加尝试,并且还会发送有关该尝试的电子邮件警报。

<?php 
//print_r($_POST);
$error['name'] ="";
$error['company']="";
$error['email'] ="";
$error['subject'] ="";
$error['message'] ="";
$error['website'] ="";
$success = "";
$thistime = time();
$current_date = date('m/d/Y/T ==> H:i:s');

if(isset($_POST['_save'])) {
    $name = stripslashes($_POST['name']);
    $email = stripslashes($_POST['email']);
    $company = stripslashes($_POST['company']);
    $message = stripslashes($_POST['message']);
    $subject = stripslashes($_POST['subject']);
    $website = stripslashes($_POST['website']);

    if (empty($name) || empty($email) || empty($subject) || empty($message) ||
            !empty($website)) {
    if (empty($name))
       $error['name'] = "Please enter your Full Name";
    if (empty($email))
       $error['email'] = "Please enter a valid Email Address";
    if (empty($company))
       $error['company'] = "Please enter Your Company Name";
    if (empty($subject))
       $error['subject'] = "Please Write a Subject";
    if (empty($message))
       $error['message'] = "Please write a message, inquiries or
               other concerns above";
    if (!empty($website)) // this is the honeypot
       $error['subject'] = "Opps looks like you're a spambot. You 
              just filled in a not required field.;
      $myFile = "botlog.txt";
      $fh = fopen($myFile, 'a') or die("can't open file");
      $stringData = "bot trapped" . " " . "-"  . " " . $website . " " . "-
                    " . " " . $current_date . "\r\n";
      fwrite($fh, $stringData);
      fclose($fh);
      $donot="donotreply@example.com";
      $headers="From: {$email}\r\nReply-To: {$donot}"; //create headers
      mail('opps@example.com',$headers,$stringData);
    }
    else { //if not empty
    stripslashes($headers);
    $headers="From: {$email}\r\nReply-To: {$email}"; //create headers
    $content="Name: ".$name."\r\n\r\nCompany: "
               .$company."\r\n\r\nSubject: ".$subject."\r\n\r\nMessage: ".$message;
    mail('opps@example.com',$subject,$content,$headers); //mails it
    $success = "Thank you! You're email has been sent.";
    #done;
    }
}
?>


Am我做对了吗?这是否会打开任何漏洞?我愿意接受任何建议和改进。

评论

您有一些不匹配的双引号。请重新检查您的代码?

您对@amon答案的评论说,您已经删除了一些“不必要的”代码,以使该片段相对较短。尤其是由于删除操作似乎引入了一些错误,您如何知道删除的代码不包含安全漏洞?如果要检查代码的安全性,则需要检查所有代码。当您因为“该部分可能不包含安全问题”而删除代码时,您怎么知道?如果您能很好地确定安全问题,那么您就不会在这里问了!

#1 楼

据我所知,本质上不存在安全漏洞(但是200_success确实找到了一个相当重要的漏洞)。但是,如果您是安全意识强的人,则可能需要练习防御性编程,这意味着要进行仔细的输入验证并避免可能存在错误的结构。

在许多语言中,可能存在错误的结构之一是使用if仅用一个语句:代替()
if (!empty($website)) // this is the honeypot
       $error['subject'] = "Opps looks like you're a spambot. You 
              just filled in a not required field.;
      $myFile = "botlog.txt";
      $fh = fopen($myFile, 'a') or die("can't open file");
      $stringData = "bot trapped" . " " . "-"  . " " . $website . " " . "-
                    " . " " . $current_date . "\r\n";
      fwrite($fh, $stringData);
      fclose($fh);
      $donot="donotreply@whatever.com";
      $headers="From: {$email}\r\nReply-To: {$donot}"; //create headers
      mail('opps@gmail.com',$headers,$stringData);


使用(主要)等效项:

// this is the honeypot
if (!empty($website)) {
    $error['website'] = "Opps looks like you're a spambot. You just filled in a not required field.";
}
$myFile = "botlog.txt";
$fh = fopen($myFile, 'a') or die("can't open file");
$stringData = "bot trapped - $website -        \n"
            . "$current_date\r\n";
fwrite($fh, $stringData);
fclose($fh);
$return_address = "donotreply@whatever.com";
$headers = "From: {$email}\r\n"
         . "Reply-To: {$return_address}";
mail('opps@gmail.com', $headers, $stringData);


哦!这意味着我们将任何错误记录为“陷阱机器人”。这是您想要的吗?肯定不会。

我改进了一些其他功能:


字符串中不包含文字换行符。每个字符串文字不应跨越多行。如果需要在字符串中使用换行符,请使用转义版本。如果字符串对于一行而言太长,则每行使用一个字符串文字并将其串联起来。
不要在没有任何使用的情况下使用过多的串联(即"bot trapped" . " " . "-" . " " . $website . " " . "- " . " " . $current_date . "\r\n")。连接这些文字可以使您的源代码更具可读性。
您将网站错误记录为$error['subject']而不是$error['website']。这表示您从未测试过代码以查看其如何处理各种错误。
您忘记了字符串文字"Opps looks like ... field.;的右引号。此代码曾经运行过吗?

现在关于您的反机器人措施:隐藏的蜜罐字段不是一个好的解决方案:


足够先进僵尸程序很可能能够通过CSS检测到它是隐藏的(例如style="display:none"很明显)。
普通用户可能正在使用对CSS支持有限的浏览器,随后可能会看到蜜罐字段和决定填写。例如,使用辅助技术的视障访客或出于某种原因而使用诸如lynx之类的命令行浏览器的访客可能会出现这种情况。

取而代之的是,使用来自外部提供商的适当的验证码(例如Recaptcha),或使用非常简单的任务来应对挑战(例如“检查值三的单选按钮:o 1 + 3 o 4-1 o 6”) 。

评论


\ $ \ begingroup \ $
#amon说得好。你有敏锐的眼睛。是的,我已经对其进行了测试并且可以运行。我删除了一些不必要的东西(例如图像)时也犯了一个错误,同时我也删除了语句的结尾。我不想把它们放在这里,因为它们会延长代码。对于那个很抱歉。 $ error ['subject']仍然显示错误。这样就行了。很好的指针。实际上,我喜欢像您这样的答复,因为他们考虑到了每个细节。这就是使它变得更加有趣的原因,因为我开始跟踪作为新手的php错误。还在学习,这很有趣。谢谢。
\ $ \ endgroup \ $
–狼ver烯
2014年2月28日在10:34

\ $ \ begingroup \ $
这实际上是一个尝试使用蜜罐的测试项目。但是,我将使用recaptcha。但是我的问题是这个。蜜罐陷阱真的有用吗?他们中的一些人说验证码不再起作用,因为还有更多高级脚本可以使用。此外,对于在中国的血汗工厂中以美分发送垃圾邮件的实际人类,我们该怎么办?我知道可能必须完全在不同的讨论或论坛中,但是..只是说说而已。
\ $ \ endgroup \ $
–狼ver烯
2014-02-28 10:38

\ $ \ begingroup \ $
@wolverene值得深思:您在为谁辩护?验证码旨在作为一种自动的图灵测试,用于防御机器人。如果您想防御垃圾邮件,则处理邮件内容的垃圾邮件过滤器会更有用。另外:哪些故障是可以接受的?当您拦截了50%的漫游器时是否成功? 90%?如果我专门为您的网站编写机器人,您的防御措施仍然有效吗? (不幸的是,有关安全性的此类问题不在此站点上讨论。您可能对信息安全性有更多的希望。)
\ $ \ endgroup \ $
–阿蒙
14年2月28日在10:50

\ $ \ begingroup \ $
点了。所有的想法都消化了。我感谢所有信息。谢谢。
\ $ \ endgroup \ $
–狼ver烯
2014年1月1日14:53

\ $ \ begingroup \ $
会从所有观点中整理出一些东西,看看。
\ $ \ endgroup \ $
–狼ver烯
2014年1月1日14:59

#2 楼

您的邮件容易受到邮件头拆分攻击! (也称为CRLF注入攻击。)

基本上,如果一个字段包含CRLF,则攻击者可以在生成的消息中插入任意标头。例如,如果email参数是

victim1@example.com%0d%0aCc:%20victim2@example.net


…,那么您将编写带有标题的消息

From: victim1@example.com
Cc: victim2@example.net
Reply-To: victim1@example.com
Cc: victim2@example.net


> PHP的mail()函数将“有帮助”将victim2@example.net添加为垃圾邮件接收者。垃圾邮件看起来像是来自victim1@example.com

,我认为最简单的缓解措施是先将所有标头字段都通过
mb_encode_mimeheader()



有关字符串连接和内插的一般警告

每当您编写将由另一台计算机系统解释的字符串时,无论是电子邮件,HTML页面还是SQL查询,假设您容易受到某种注入攻击。这些“人类友好”语言中的每一种(与JPEG图像等二进制格式相对)都将具有特殊意义的分隔符。标头拆分,HTML / JavaScript注入和SQL注入攻击都具有相同的根本原因:粗心的字符串连接或插值。

因此,在连接或插值字符串之前,请停下来思考:“什么是我应该使用的合适的转义机制?”更好的是,问:“是否有一个高级库可以让我完成任务而无需进行低级字符串操作?”





RFC时间!

我正在深入探讨mb_encode_mimeheader()为什么是我的转义选择。如果您对本技术讨论感到困惑,则可以放心地忽略此解释,而只是通过mb_encode_mimeheader()过滤所有输入。

实际上,电子邮件标头有两个相关的标准。邮件传输代理(MTA)关注RFC2822。邮件用户代理(MUA)关注RFC2047。

MTA完全不使用MIME编码。只要他们看到所需的标头(例如“发件人”,“至”,“返回路径”),并且标头包含安全的ASCII子集,它们就会很高兴。

MUA,另一方面,它们更为复杂,因为它们必须处理非ASCII文本才能使非英语用户受益。例如,“主题”标头可以包含任意文本,例如“¡Hóla!”。这样的主题行应使用
mb_encode_mimeheader("¡Hóla!", "UTF-8", "Q");

作为

=?UTF-8?Q?=C2=A1H=C3=B3la!?=


进行编码,也就是说,按照$subject文档中的说明,确保mail()参数符合RFC 2047的方式。

与MTA和MUA都相关的标头(例如“ From”标头)又如何呢?可能需要生成一个看起来像用户的标头,在网上看起来像这样

From: "François Müller" <francois.mueller@example.com>


在网上,这样的标头必须被编码,因为它包含非ASCII字符。一种可接受的编码是
From: "=?UTF-8?Q?Fran=C3=A7ois=20M=C3=BCller?=" <francois.mueller@example.com>


这种编码既满足RFC 2822(因为MTA在双引号之间看到了不透明的ASCII字符串)又满足RFC 2047(由于MUA应该知道如何解码MIME编码的字符串以进行显示)。要获得这样的结果,您将需要PHP代码,例如

$from_header = sprintf('From: "%s" <%s>',
                       mb_encode_mimeheader("François Müller", "UTF-8", "Q"),
                       "francois.mueller@example.com");


如果将代码编写为

$email = '"François Müller" <francois.mueller@example.com>';
$from_header = mb_encode_mimeheader("From: $email", "UTF-8", "Q");

< br……您将得到

From: =?UTF-8?Q?=22Fran=C3=A7ois=20M=C3=BCller=22=20=3Cfrancois=2Emueller?=
 =?UTF-8?Q?=40example=2Ecom=3E?=


…这是错误的,因为MTA看到一个不透明的ASCII字符串,看起来像一个有效的电子邮件地址。但是,要点是,此类代码虽然简单,但不容易受到标头注入的影响。只要$email变量包含简单的ASCII文本,它将起作用。例如,

$email = '<francois.mueller@example.com>';
$from_header = mb_encode_mimeheader("From: $email", "UTF-8", "Q");


…将文本原封不动地传递,产生

From: <francois.mueller@example.com>





PHP扑朔迷离的时间!

与PHP经常发生的情况一样,mail()函数的设计不良,因此很容易陷入此陷阱。期望程序员知道RFC并花时间对所有标头进行正确编码纯属疯狂!如果设计mail()使其接受(标题名称→标题值)对的关联数组,则它可以自动对缓解标题拆分攻击采取缓解措施。


评论


\ $ \ begingroup \ $
吉普车,它比以往更加有趣。但是非常热衷。我什至都不了解其中的大多数内容,但查找有关它们的更多信息将证明是卓有成效的。非常感激。
\ $ \ endgroup \ $
–狼ver烯
2014年1月1日15:02

\ $ \ begingroup \ $
我知道,这不是一个明显的错误,但是修复它非常重要。考虑一下建议,并随时要求澄清。我鼓励您使用修订后的代码发布一个后续问题,以确保您做对了。
\ $ \ endgroup \ $
– 200_success
2014年1月1日21:01



#3 楼

邮件功能具有代码可以处理的返回值:


如果成功接受邮件发送,则返回TRUE,否则返回FALSE


所以,当它返回FALSE时,以下成功消息肯定是不正确的:

    mail('opps@gmail.com',$subject,$content,$headers); //mails it
    $success = "Thank you! You're email has been sent.";


评论


\ $ \ begingroup \ $
是的,我弄得太多糊涂了,因为这根本没有意义。我正在尝试清理代码。谢谢。
\ $ \ endgroup \ $
–狼ver烯
2014年1月1日15:03

#4 楼

对此stripslashes()的调用没有任何意义:

stripslashes($headers);                            # ← Calling stripslashes() on an
                                                   # undefined variable
$headers="From: {$email}\r\nReply-To: {$email}";   # ← Why bother, if you're going to
                                                   # assign to $headers immediately
                                                   # afterwards anyway?


您可能误解了stripslashes()的用途,或者您很粗心。

我'我也对stripslashes()的所有这些调用都不满意:

$name = stripslashes($_POST['name']);
$email = stripslashes($_POST['email']);
$company = stripslashes($_POST['company']);
$message = stripslashes($_POST['message']);
$subject = stripslashes($_POST['subject']);
$website = stripslashes($_POST['website']);


magic_quotes_gpc崩溃是PHP的一个基本问题,因此应该系统地处理而不是在特设的基础。如果您认为可以在启用了魔引号的服务器上运行此代码,请采纳此建议,以包括一个有条件地调用stripslashes()的前导代码块:

if (get_magic_quotes_gpc()) {
    function stripslashes_gpc(&$value)
    {
        $value = stripslashes($value);
    }
    array_walk_recursive($_GET, 'stripslashes_gpc');
    array_walk_recursive($_POST, 'stripslashes_gpc');
    array_walk_recursive($_COOKIE, 'stripslashes_gpc');
    array_walk_recursive($_REQUEST, 'stripslashes_gpc');
}


调用stripslashes()正如您所做的那样,无条件地是不好的做法。如果您的服务器已禁用魔术引号(如目前所建议的那样),那么您将处理输入内容,并删除应该存在的反斜杠。如果您的服务器启用了魔引号(这被认为是过时的做法),那么重新配置服务器以符合现代建议将破坏您的代码。无论哪种方式,您都输了!请记住,自PHP 5.3起已弃用魔术引号,而自PHP 5.4起已将其完全删除。

评论


\ $ \ begingroup \ $
是的,作为新手,我很粗心。很高兴在这里分享我的观点,以征询专家的所有意见。将尝试所有适合的建议。谢谢。
\ $ \ endgroup \ $
–狼ver烯
2014年1月1日14:58

#5 楼

您将“答复:”设置为“ donot@whatever.com”。 what.com在加利福尼亚州蒙特贝罗的一家名为Dreamissary的公司中注册。那是你吗?如果没有,则您没有业务将电子邮件重定向到他们。答复邮件但未意识到自己的答复将被发送给第三方的用户很可能会破坏消息中发送的任何信息的机密性,因此这是一个安全问题。将垃圾邮件发送给第三方也是令人讨厌的。为此,您应该在自己的域中使用无效地址。

评论


\ $ \ begingroup \ $
对不起,这纯属巧合。我没有意识到有这样一家公司。我将相应地编辑预览代码。
\ $ \ endgroup \ $
–狼ver烯
2014年1月1日14:49

\ $ \ begingroup \ $
@wolverene也注册了。 :-)此处使用的正确域是“ example.com”(根据RFC2606),然后在生产代码中使用实际域中的地址。
\ $ \ endgroup \ $
– David Richerby
2014年1月1日15:16

#6 楼

您还需要担心的一件事是CSRF攻击

要防止的最基本的实现是每次加载表单时生成一个随机令牌,将其保存到$_SESSION中,然后将该令牌放入表单中的隐藏字段,然后检查表单提交是否具有该令牌并与$_SESSION中保存的令牌匹配

评论


\ $ \ begingroup \ $
嗯。这个蜜罐的东西正在打开一罐蠕虫。谢谢。
\ $ \ endgroup \ $
–狼ver烯
2014年1月1日14:56