From 8e1dc42bfb5b07385447ff8b1cf5260a0688b82d Mon Sep 17 00:00:00 2001 From: paulwer Date: Wed, 3 Apr 2024 08:06:39 +0200 Subject: [PATCH] prevent downloading actions, when sender is blocked --- app/Http/Controllers/PostMarkController.php | 7 ++- app/Jobs/Brevo/ProcessBrevoInboundWebhook.php | 6 +++ .../Mailgun/ProcessMailgunInboundWebhook.php | 6 +++ .../InboundMail/InboundMailEngine.php | 44 +++++++++---------- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/app/Http/Controllers/PostMarkController.php b/app/Http/Controllers/PostMarkController.php index 5fa1049ca607..917c8ed44c84 100644 --- a/app/Http/Controllers/PostMarkController.php +++ b/app/Http/Controllers/PostMarkController.php @@ -11,12 +11,11 @@ namespace App\Http\Controllers; -use App\Jobs\PostMark\ProcessPostmarkInboundWebhook; use App\Jobs\PostMark\ProcessPostmarkWebhook; use App\Services\InboundMail\InboundMail; use App\Services\InboundMail\InboundMailEngine; use App\Utils\TempFile; -use Carbon\Carbon; +use Illuminate\Support\Carbon; use Illuminate\Http\Request; use Log; @@ -285,6 +284,10 @@ class PostMarkController extends BaseController // if (!($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('services.postmark.token'))) // return response()->json(['message' => 'Unauthorized'], 403); + if ((new InboundMailEngine())->isInvalidOrBlocked($input["From"], $input["To"])) { + Log::info('Failed: Sender is blocked: ' . $input["From"] . " Recipient: " . $input["To"]); + return response()->json(['message' => 'Blocked.'], 403); + } try { // important to save meta if something fails here to prevent spam diff --git a/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php b/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php index 706b76e8fdaa..957a7a9ae162 100644 --- a/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php +++ b/app/Jobs/Brevo/ProcessBrevoInboundWebhook.php @@ -121,6 +121,12 @@ class ProcessBrevoInboundWebhook implements ShouldQueue // brevo defines recipients as array, we check all of them, to be sure foreach ($this->input["Recipients"] as $recipient) { + // Spam protection + if ((new InboundMailEngine())->isInvalidOrBlocked($this->input["From"]["Address"], $recipient)) { + Log::info('Failed: Sender is blocked: ' . $this->input["From"]["Address"] . " Recipient: " . $recipient); + throw new \Error('Sender is blocked'); + } + // match company $company = MultiDB::findAndSetDbByInboundMailbox($recipient); if (!$company) { diff --git a/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php b/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php index a108b5f29c40..ff0792c52e38 100644 --- a/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php +++ b/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php @@ -167,6 +167,12 @@ class ProcessMailgunInboundWebhook implements ShouldQueue $to = explode("|", $this->input)[1]; // $messageId = explode("|", $this->input)[2]; // used as base in download function + // Spam protection + if ((new InboundMailEngine())->isInvalidOrBlocked($from, $to)) { + Log::info('Failed: Sender is blocked: ' . $from . " Recipient: " . $to); + throw new \Error('Sender is blocked'); + } + // match company $company = MultiDB::findAndSetDbByInboundMailbox($to); if (!$company) { diff --git a/app/Services/InboundMail/InboundMailEngine.php b/app/Services/InboundMail/InboundMailEngine.php index 0383fbf80d3b..b9f293d4f9f2 100644 --- a/app/Services/InboundMail/InboundMailEngine.php +++ b/app/Services/InboundMail/InboundMailEngine.php @@ -48,7 +48,7 @@ class InboundMailEngine */ public function handle(InboundMail $email) { - if ($this->isInvalidOrBlocked($email)) + if ($this->isInvalidOrBlocked($email->from, $email->to)) return; $isUnknownRecipent = true; @@ -64,59 +64,59 @@ class InboundMailEngine } // SPAM Protection - private function isInvalidOrBlocked(InboundMail $email) + public function isInvalidOrBlocked(string $from, string $to) { // invalid email - if (!filter_var($email->from, FILTER_VALIDATE_EMAIL)) { - Log::info('E-Mail blocked, because from e-mail has the wrong format: ' . $email->from); + if (!filter_var($from, FILTER_VALIDATE_EMAIL)) { + Log::info('E-Mail blocked, because from e-mail has the wrong format: ' . $from); return true; } - $parts = explode('@', $email->from); + $parts = explode('@', $from); $domain = array_pop($parts); // global blacklist if (in_array($domain, $this->globalBlacklistDomains)) { - Log::info('E-Mail blocked, because the domain was found on globalBlocklistDomains: ' . $email->from); + Log::info('E-Mail blocked, because the domain was found on globalBlocklistDomains: ' . $from); return true; } - if (in_array($email->from, $this->globalBlacklistSenders)) { - Log::info('E-Mail blocked, because the email was found on globalBlocklistEmails: ' . $email->from); + if (in_array($from, $this->globalBlacklistSenders)) { + Log::info('E-Mail blocked, because the email was found on globalBlocklistEmails: ' . $from); return true; } - if (Cache::has('inboundMailBlockedSender:' . $email->from)) { // was marked as blocked before, so we block without any console output + if (Cache::has('inboundMailBlockedSender:' . $from)) { // was marked as blocked before, so we block without any console output return true; } // sender occured in more than 500 emails in the last 12 hours - $senderMailCountTotal = Cache::get('inboundMailSender:' . $email->from, 0); + $senderMailCountTotal = Cache::get('inboundMailSender:' . $from, 0); if ($senderMailCountTotal >= 5000) { - Log::info('E-Mail blocked permanent, because the sender sended more than ' . $senderMailCountTotal . ' emails in the last 12 hours: ' . $email->from); - $this->blockSender($email->from); - $this->saveMeta($email->from, $email->to); + Log::info('E-Mail blocked permanent, because the sender sended more than ' . $senderMailCountTotal . ' emails in the last 12 hours: ' . $from); + $this->blockSender($from); + $this->saveMeta($from, $to); return true; } if ($senderMailCountTotal >= 1000) { - Log::info('E-Mail blocked, because the sender sended more than ' . $senderMailCountTotal . ' emails in the last 12 hours: ' . $email->from); - $this->saveMeta($email->from, $email->to); + Log::info('E-Mail blocked, because the sender sended more than ' . $senderMailCountTotal . ' emails in the last 12 hours: ' . $from); + $this->saveMeta($from, $to); return true; } // sender sended more than 50 emails to the wrong mailbox in the last 6 hours - $senderMailCountUnknownRecipent = Cache::get('inboundMailSenderUnknownRecipent:' . $email->from, 0); + $senderMailCountUnknownRecipent = Cache::get('inboundMailSenderUnknownRecipent:' . $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: ' . $email->from); - $this->saveMeta($email->from, $email->to); + 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); return true; } // wrong recipent occurs in more than 100 emails in the last 12 hours, so the processing is blocked - $mailCountUnknownRecipent = Cache::get('inboundMailUnknownRecipent:' . $email->to, 0); // @turbo124 maybe use many to save resources in case of spam with multiple to addresses each time + $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) { - 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: ' . $email->from); - $this->blockSender($email->from); - $this->saveMeta($email->from, $email->to); + 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); return true; }