diff --git a/app/Http/Controllers/PostMarkController.php b/app/Http/Controllers/PostMarkController.php index a6877b1e250c..81e618493c04 100644 --- a/app/Http/Controllers/PostMarkController.php +++ b/app/Http/Controllers/PostMarkController.php @@ -287,7 +287,6 @@ class PostMarkController extends BaseController $inboundEngine = new InboundMailEngine(); if ($inboundEngine->isInvalidOrBlocked($input["From"], $input["To"])) { - Log::info('Failed: Sender is blocked: ' . $input["From"] . " Recipient: " . $input["To"]); return response()->json(['message' => 'Blocked.'], 403); } diff --git a/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php b/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php index 02901132fee1..864b0bdb9af8 100644 --- a/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php +++ b/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php @@ -31,7 +31,7 @@ class ProcessBrevoInboundWebhook implements ShouldQueue public $tries = 1; - private InboundMailEngine $engine = new InboundMailEngine(); + private InboundMailEngine $engine; /** * Create a new job instance. @@ -111,6 +111,7 @@ class ProcessBrevoInboundWebhook implements ShouldQueue */ public function __construct(private array $input) { + $this->engine = new InboundMailEngine(); } /** @@ -128,8 +129,7 @@ class ProcessBrevoInboundWebhook implements ShouldQueue // Spam protection if ($this->engine->isInvalidOrBlocked($this->input["From"]["Address"], $recipient)) { - Log::info('Failed: Sender is blocked: ' . $this->input["From"]["Address"] . " Recipient: " . $recipient); - throw new \Error('Sender is blocked'); + return; } // match company diff --git a/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php b/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php index 3ac892d8d081..1bf6caeb44dc 100644 --- a/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php +++ b/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php @@ -29,7 +29,7 @@ class ProcessMailgunInboundWebhook implements ShouldQueue public $tries = 1; - private InboundMailEngine $engine = new InboundMailEngine(); + private InboundMailEngine $engine; /** * Create a new job instance. @@ -37,6 +37,7 @@ class ProcessMailgunInboundWebhook implements ShouldQueue */ public function __construct(private string $input) { + $this->engine = new InboundMailEngine(); } /** @@ -173,8 +174,7 @@ class ProcessMailgunInboundWebhook implements ShouldQueue // Spam protection if ($this->engine->isInvalidOrBlocked($from, $to)) { - Log::info('Failed: Sender is blocked: ' . $from . " Recipient: " . $to); - throw new \Error('Sender is blocked'); + return; } // match company diff --git a/app/Services/InboundMail/InboundMailEngine.php b/app/Services/InboundMail/InboundMailEngine.php index f5deef5a874b..5880c9ee5bfa 100644 --- a/app/Services/InboundMail/InboundMailEngine.php +++ b/app/Services/InboundMail/InboundMailEngine.php @@ -39,6 +39,8 @@ class InboundMailEngine private ?bool $isUnknownRecipent = null; private array $globalBlacklistDomains = []; private array $globalBlacklistSenders = []; + private array $globalWhitelistDomains = []; // only for global validation, not for allowing to send something into the company, should be used to disabled blocking for mass-senders + private array $globalWhitelistSenders = []; // only for global validation, not for allowing to send something into the company, should be used to disabled blocking for mass-senders public function __construct() { } @@ -70,15 +72,25 @@ class InboundMailEngine Log::info('E-Mail blocked, because from e-mail has the wrong format: ' . $from); return true; } + if (!filter_var($to, FILTER_VALIDATE_EMAIL)) { + Log::info('E-Mail blocked, because to e-mail has the wrong format: ' . $from); + return true; + } $parts = explode('@', $from); $domain = array_pop($parts); // global blacklist + if (in_array($from, $this->globalWhitelistDomains)) { + return false; + } if (in_array($domain, $this->globalBlacklistDomains)) { Log::info('E-Mail blocked, because the domain was found on globalBlocklistDomains: ' . $from); return true; } + if (in_array($domain, $this->globalWhitelistSenders)) { + return false; + } if (in_array($from, $this->globalBlacklistSenders)) { Log::info('E-Mail blocked, because the email was found on globalBlocklistEmails: ' . $from); return true; @@ -89,7 +101,7 @@ class InboundMailEngine } // sender occured in more than 500 emails in the last 12 hours - $senderMailCountTotal = Cache::get('inboundMailSender:' . $from, 0); + $senderMailCountTotal = Cache::get('inboundMailCountSender:' . $from, 0); if ($senderMailCountTotal >= 5000) { Log::info('E-Mail blocked permanent, because the sender sended more than ' . $senderMailCountTotal . ' emails in the last 12 hours: ' . $from); $this->blockSender($from); @@ -103,7 +115,7 @@ class InboundMailEngine } // sender sended more than 50 emails to the wrong mailbox in the last 6 hours - $senderMailCountUnknownRecipent = Cache::get('inboundMailSenderUnknownRecipent:' . $from, 0); + $senderMailCountUnknownRecipent = Cache::get('inboundMailCountSenderUnknownRecipent:' . $from, 0); if ($senderMailCountUnknownRecipent >= 50) { Log::info('E-Mail blocked, because the sender sended more than ' . $senderMailCountUnknownRecipent . ' emails to the wrong mailbox in the last 6 hours: ' . $from); $this->saveMeta($from, $to); @@ -111,8 +123,8 @@ class InboundMailEngine } // wrong recipent occurs in more than 100 emails in the last 12 hours, so the processing is blocked - $mailCountUnknownRecipent = Cache::get('inboundMailUnknownRecipent:' . $to, 0); // @turbo124 maybe use many to save resources in case of spam with multiple to addresses each time - if ($mailCountUnknownRecipent >= 100) { + $mailCountUnknownRecipent = Cache::get('inboundMailCountUnknownRecipent:' . $to, 0); // @turbo124 maybe use many to save resources in case of spam with multiple to addresses each time + if ($mailCountUnknownRecipent >= 200) { Log::info('E-Mail blocked, because anyone sended more than ' . $mailCountUnknownRecipent . ' emails to the wrong mailbox in the last 12 hours. Current sender was blocked as well: ' . $from); $this->blockSender($from); $this->saveMeta($from, $to); @@ -131,15 +143,15 @@ class InboundMailEngine public function saveMeta(string $from, string $to, bool $isUnknownRecipent = false) { // save cache - Cache::add('inboundMailSender:' . $from, 0, now()->addHours(12)); - Cache::increment('inboundMailSender:' . $from); + Cache::add('inboundMailCountSender:' . $from, 0, now()->addHours(12)); + Cache::increment('inboundMailCountSender:' . $from); if ($isUnknownRecipent) { - Cache::add('inboundMailSenderUnknownRecipent:' . $from, 0, now()->addHours(6)); - Cache::increment('inboundMailSenderUnknownRecipent:' . $from); // we save the sender, to may block him + Cache::add('inboundMailCountSenderUnknownRecipent:' . $from, 0, now()->addHours(6)); + Cache::increment('inboundMailCountSenderUnknownRecipent:' . $from); // we save the sender, to may block him - Cache::add('inboundMailUnknownRecipent:' . $to, 0, now()->addHours(12)); - Cache::increment('inboundMailUnknownRecipent:' . $to); // we save the sender, to may block him + Cache::add('inboundMailCountUnknownRecipent:' . $to, 0, now()->addHours(12)); + Cache::increment('inboundMailCountUnknownRecipent:' . $to); // we save the sender, to may block him } }