spatie/laravel-honeypot

防止通过表单提交的垃圾邮件

4.5.3 2024-09-20 13:45 UTC

README

Latest Version on Packagist Test Status Code Style Status Total Downloads

当将表单添加到公共网站时,垃圾邮件机器人可能会尝试使用虚假值提交它。幸运的是,这些机器人中的大多数都很笨。你可以在表单中添加一个不可见的字段,该字段在提交时不应包含任何值。这样的字段被称为蜜罐。这些垃圾邮件机器人将填写所有字段,包括蜜罐。

当收到包含已填充蜜罐字段的提交时,此包将丢弃该请求。此外,此包还会检查表单提交所需的时间。这是通过另一个不可见的字段中的时间戳来完成的。如果表单提交时间异常短,反垃圾邮件也将被触发。

安装此包后,你只需将 x-honeypot Blade 组件添加到你的表单中。

<form method="POST">
    <x-honeypot />
    <input name="myField" type="text">
</form>

此包还支持手动将必要的值传递到视图层,因此你可以轻松地将蜜罐字段添加到由 Inertia 驱动的应用程序中。

支持我们

我们投入了大量资源来创建 一流的开源包。你可以通过 购买我们的付费产品之一 来支持我们。

我们非常感谢你从家乡寄来明信片,说明你正在使用我们哪个包。你可以在 我们的联系页面 上找到我们的地址。我们将把所有收到的明信片发布在 我们的虚拟明信片墙上

视频教程

此视频 中,这是 Mailcoach 视频课程的一部分,你可以看到如何安装和使用此包。

安装

您可以通过 composer 安装此包

composer require spatie/laravel-honeypot

可选,您可以发布包的配置文件。

php artisan vendor:publish --provider="Spatie\Honeypot\HoneypotServiceProvider" --tag=config

这是将在 config/honeypot.php 发布的配置文件内容

use Spatie\Honeypot\SpamResponder\BlankPageResponder;

return [
    /*
     * Here you can specify name of the honeypot field. Any requests that submit a non-empty
     * value for this name will be discarded. Make sure this name does not
     * collide with a form field that is actually used.
     */
    'name_field_name' => env('HONEYPOT_NAME', 'my_name'),

    /*
     * When this is activated there will be a random string added
     * to the name_field_name. This improves the
     * protection against bots.
     */
    'randomize_name_field_name' => env('HONEYPOT_RANDOMIZE', true),

    /*
     * When this is activated, requests will be checked if
     * form is submitted faster than this amount of seconds
     */
    'valid_from_timestamp' => env('HONEYPOT_VALID_FROM_TIMESTAMP', true),
    
    /*
     * This field contains the name of a form field that will be used to verify
     * if the form wasn't submitted too quickly. Make sure this name does not
     * collide with a form field that is actually used.
     */
    'valid_from_field_name' => env('HONEYPOT_VALID_FROM', 'valid_from'),

    /*
     * If the form is submitted faster than this amount of seconds
     * the form submission will be considered invalid.
     */
    'amount_of_seconds' => env('HONEYPOT_SECONDS', 1),

    /*
     * This class is responsible for sending a response to requests that
     * are detected as being spammy. By default a blank page is shown.
     *
     * A valid responder is any class that implements
     * `Spatie\Honeypot\SpamResponder\SpamResponder`
     */
    'respond_to_spam_with' => BlankPageResponder::class,

    /*
     * This class is responsible for applying all protection
     * rules for a request. By default uses `request()`.
     *
     * It throws the `Spatie\Honeypot\ExceptionsSpamException` if the
     * request is flagged as spam, or returns void if it succeeds.
     */
    'spam_protection' => \Spatie\Honeypot\SpamProtection::class,

    /*
     * When activated, requests will be checked if honeypot fields are missing,
     * if so the request will be stamped as spam. Be careful! When using the
     * global middleware be sure to add honeypot fields to each form.
     */
    'honeypot_fields_required_for_all_forms' => false,

    /*
     * This switch determines if the honeypot protection should be activated.
     */
    'enabled' => env('HONEYPOT_ENABLED', true),
];

使用

首先,您必须将 x-honeypot Blade 组件添加到您想要保护的任何表单中。

<form method="POST" action="{{ route('contactForm.submit') }}")>
    <x-honeypot />
    <input name="myField" type="text">
</form>

或者,您可以使用 @honeypot Blade 指令

<form method="POST" action="{{ route('contactForm.submit') }}")>
    @honeypot
    <input name="myField" type="text">
</form>

使用 Blade 组件或指令将添加两个字段: my_namevalid_from_timestamp(您可以在配置文件中更改名称)。

接下来,您必须在处理表单提交的路由中使用 Spatie\Honeypot\ProtectAgainstSpam 中间件。此中间件将拦截任何提交非空值给 my_name 键的请求。如果请求提交速度比包在 valid_from_timestamp 中生成的加密时间戳要快,它也会拦截请求。

use App\Http\Controllers\ContactFormSubmissionController;
use Spatie\Honeypot\ProtectAgainstSpam;

Route::post('contact', [ContactFormSubmissionController::class, 'create'])->middleware(ProtectAgainstSpam::class);

如果您想将 Spatie\Honeypot\ProtectAgainstSpam 中间件与 Laravel 内置的认证路由集成,请将 Auth::routes(); 声明包裹在适当的中间件组中(确保将 @honeypot 指令添加到认证表单中)。

use Spatie\Honeypot\ProtectAgainstSpam;

Route::middleware(ProtectAgainstSpam::class)->group(function() {
    Auth::routes();
});

如果您的应用程序有大量由许多不同控制器处理的表单,您可以将其注册为全局中间件。

// inside app\Http\Kernel.php

protected $middleware = [
   // ...
   \Spatie\Honeypot\ProtectAgainstSpam::class,
];

Inertia 中的使用

当使用Inertia时,您必须手动传递用于蜜罐字段的值。以下是一个示例

// in a controller
public function create(\Spatie\Honeypot\Honeypot $honeypot) 
{
    return inertia('contactform.show', [
        'honeypot' => $honeypot,
    ]);
}

您的前端将获得一个具有以下键的honeypot对象:enablednameFieldNamevalidFromFieldNameencryptedValidFrom

以下是如何使用Vue渲染这些值的示例

<div v-if="honeypot.enabled" :name="`${honeypot.nameFieldName}_wrap`" style="display:none;">
    <input type="text" v-model="form[honeypot.nameFieldName]" :name="honeypot.nameFieldName" :id="honeypot.nameFieldName" />
    <input type="text" v-model="form[honeypot.validFromFieldName]" :name="honeypot.validFromFieldName" />
</div>

然后在您的Vue组件中,将这些值添加到表单数据中

props: ['honeypot'],

data() {
    return {
        form: this.$inertia.form({
            [this.honeypot.nameFieldName]: '',
            [this.honeypot.validFromFieldName]: this.honeypot.encryptedValidFrom,
        }),
    }
}

在Livewire中的使用

您可以使用此包来防止向由Livewire提供的表单提交垃圾邮件。

首先,将UsesSpamProtection特性添加到您的Livewire组件中

use Spatie\Honeypot\Http\Livewire\Concerns\UsesSpamProtection;

class YourComponent extends Component
{
    use UsesSpamProtection;

接下来,声明一个HoneypotData属性,并在处理表单提交的方法中调用protectAgainstSpam()

use Spatie\Honeypot\Http\Livewire\Concerns\HoneypotData;

class YourComponent extends Component
{
    // ...
    
    public HoneypotData $extraFields;
    
    public function mount()
    {
        $this->extraFields = new HoneypotData();
    }
 
   
    public function submit(): void 
    {
        $this->protectAgainstSpam(); // if is spam, will abort the request
    
        User::create($request->all());
    }
}

最后,在您的Livewire Blade组件中使用x-honeypot

<form method="POST" action="{{ route('contactForm.submit') }}")>
    <x-honeypot livewire-model="extraFields" />
    <input name="myField" type="text">
</form>

在Volt函数语法中的使用

要在Volt函数语法中使用此包,从guessHoneypotDataProperty方法返回HoneypotData属性。

use App\Models\User;
use Spatie\Honeypot\Http\Livewire\Concerns\HoneypotData;
use Spatie\Honeypot\Http\Livewire\Concerns\UsesSpamProtection;
use function Livewire\Volt\{uses, state, mount};

uses(UsesSpamProtection::class);

state([
    // ...
    'extraFields' => null,
]);

mount(function () {
    $this->extraFields = new HoneypotData();
});

$guessHoneypotDataProperty = fn () => $this->extraFields;

$submit = function () {
    $this->protectAgainstSpam(); // if is spam, will abort the request
    
    User::create($request->all());
};

测试中禁用

默认情况下,任何在1秒内提交的保护表单将被标记为垃圾邮件。当运行端到端测试时,这些测试应尽可能快地运行,您可能不希望这样。

要禁用代码中的所有蜜罐,可以将enabled配置值设置为false

config()->set('honeypot.enabled', false)

自定义响应

当检测到垃圾邮件提交时,该包默认将显示一个空白页面。您可以通过编写自己的SpamResponse并指定其完全限定类名在honeypot配置文件的respond_to_spam_with键中来自定义此行为。

有效的SpamResponse是实现了Spatie\Honeypot\SpamResponder\SpamResponder接口的任何类。以下是这个接口的样子

namespace Spatie\Honeypot\SpamResponder;

use Closure;
use Illuminate\Http\Request;

interface SpamResponder
{
    public function respond(Request $request, Closure $next);
}

尽管垃圾邮件响应者的主要目的是响应垃圾邮件请求,但您也可以在这里做其他事情。例如,您可以使用$request上的属性来确定垃圾邮件的来源(例如,所有请求都来自同一个IP),并添加一些逻辑来阻止该来源。

如果该包错误地确定了请求是垃圾邮件,您可以像在中间件中那样传递$request$next闭包来生成默认响应。

// in your spam responder
$regularResponse = $next($request)

自定义生成的蜜罐字段

要自定义输出,可以使用以下命令发布honeypot视图

php artisan vendor:publish --provider="Spatie\Honeypot\HoneypotServiceProvider" --tag=honeypot-views

视图将放置在resources/views/vendor/honeypot/honeypotFormFields.blade.php。这是默认内容

@if($enabled)
    <div id="{{ $nameFieldName }}_wrap" style="display:none;">
        <input name="{{ $nameFieldName }}" type="text" value="" id="{{ $nameFieldName }}">
        <input name="{{ $validFromFieldName }}" type="text" value="{{ $encryptedValidFrom }}">
    </div>
@endif

触发事件

每当检测到垃圾邮件时,都会触发Spatie\Honeypot\Events\SpamDetectedEvent事件。它将$request作为一个公共属性。

测试

composer test

更改日志

有关最近更改的更多信息,请参阅更改日志

替代方案

如果您需要更强的垃圾邮件保护,请考虑使用Google ReCaptchaAkismet

贡献

有关详细信息,请参阅贡献

安全

如果您发现了关于安全性的错误,请通过security@spatie.be发送邮件,而不是使用问题跟踪器。

鸣谢

此包受到了Maksim Surguy的Honeypot包的启发。

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件