DEV Community

Cover image for Fighting spam by verifying user emails by inbound message receiving
Alex Yatsenko
Alex Yatsenko

Posted on

Fighting spam by verifying user emails by inbound message receiving

Hi there,

It's Alex software engineer and solo founder of ProxiedMail.

This article is dedicated to people who want to fight potential spammers and email existence checkers on your product websites.
It would be mostly beneficial for owners of products that require people to confirm emails on the websites of their apps or just software engineers who want to prevent spammers from using their products in a bad way.

Background: delivery trust

Image description

As I'm working on ProxiedMail, a product that mostly sells services related to email forwarding between mailboxes with privacy and API focus it's important to keep a good record of email activity in the eyes of GMail, ProtonMail, Tutanota, and other email providers.
If trust in other email services is low we won't be able to deliver emails to our recent users.
The level of trust from email providers is separated between mailboxes that recently started receiving letters from your mail servers/domains and people who have been receiving and reading them for a long time. While the last ones aren't a problem for us, new ones could be challenging.

Background: spammers cases

I've told you about the importance of a good email record, so email delivery, especially to email services is high.
That's a reason why it should be closely tracked.

Our spam tracking methodology

We don't have advanced spam protection yet, but we have rate limits for particular accounts. So, we mostly trying to prevent people from getting multiple accounts by checking their IP, geo, and email verification.
Also, we do track the successful rate of email verification.

Image description

Email verification exploit

We're asking users to verify their emails and they also can see if their email address is marked as verified or not.
Additionally, all of our users are capable of adding more than one email and tracking the verification status.
So, it could be a potential case for spammers and intruders to verify some cold databases of emails.

Our intruder's case

Image description

Because we have had that email verification capability, at some moments I've seen increased signs up without verified email from the US and all accounts shared the same Turkish IP.
So, probably the case was: someone tried to verify the email database through our verification feature singing up from different US IPs.

What we're standing against

At this moment it's become pretty clear that we have been attacked through an email verification system with butch sign-ups in a manual way.
We've been sending email verification to our new users.
Potential consequences of that are lowering email delivery rates.

Solution to spam/abuse of email verification

Our intruders were trying different emails to see if they were valid and we were sending many emails to people who hadn't seen our services before we decided to solve it somehow.
As we're building services focused on email protection and API we decided to use it to solve this problem.
The most obvious solution was changing email verification from inbound to outbound.
So, it's not we who will send the email to the customer for verification, but the customer should send us the email for verification instead.

Image description

UX changes to email verification

The email verification scheme is moving from emailing links by us to sending emails by customers to some dedicated email address provided by us.
For example, we sign up and get a popup: "Sent email with any text/code from your email address to this address (blabla@websiteverify.com". When the email reaches the email server backend gets a webhook about the message and marks the email as verified.

Simple scheme on new flow

Let's look at how the new email verification flow looks like

Image description

Real-life look

Let's see how it works with the real implementation on ProxiedMail.

This is what the confirmation page looks like just after signing up:

Image description

"Confirm your email by sending any message from your real address yatsenkolesh@gmail.com to confirmation@pxdmail.com.
Once you confirm your first email, you will get the standard email confirmation by receiving the links."

So, to confirm our email we would need to send an email from yatsenkolesh@gmail.com to confirmation@pxdmail.com.

Let's see what it looks like:

Image description

And 30 seconds after we have replied that our email is confirmed.

Image description

So, as you may see everything went great!

Take a moment to enjoy the great solution

Now we will require people to confirm their emails by sending messages from their email and we will avoid the attacks.

Techincal details

Our solution is based on ProxiedMail webhooks. You can attach any webhook URL to an email address created on ProxiedMail.
It could be using the ProxiedMail domain or your domain as well.
For your website email verifications I would recommend buying a new domain like verifymyapp.com, so you would be able to get an email address for verifications like email@verifymyapp.com.

If you feel like a step-by-step list it's:

  1. Sign up on ProxiedMail
  2. Optional - Attach your domain
  3. Create a proxy email using your custom domain and go to webhook settings

Image description

  1. Set up your webhook URL:

Image description

  1. That's it. You would be able to receive webhooks on incoming emails.

Coding details

Now you can see a code inside of our Laravel app that verifying incoming emails:

    public function confirmationWebhook(
        Request $request,
        Dispatcher $dispatcher
    ) {
        $payload = $request->json('payload');
        $sender = $payload['sender'];

        /**
         * @var EmailConfirmation|null $model
         */
        $model = EmailConfirmation::query()->where('raw_email', $sender)
            ->where('confirmed', 0)
            ->first();
        if ($model) {
            $model->confirmed = 1;
            $model->save();

            $email = new EmailConfirmedLetter($model->email);
            Mail::to($model->email)->send($email);

            $dispatcher->dispatch(new EmailConfirmedEvent($model));
        }

        return response()->json([
            'status' => 'ok',
        ]);
    }

Enter fullscreen mode Exit fullscreen mode

So, we're getting the sender's email and just confirming it in our database. After we mark it as verifying we send the confirmation letter and firing confirmation event in our application.

Now we have email verification working.

Resources

To implement verification yourself I can recommend the following resources:

Webhook on the received email
ProxiedMail Docs
Sign up

We learned

(we mostly want to describe it for the SEO of this post)

  1. Protecting our email verification functionality against abuse
  2. Transferring email verification from outbound to inbound
  3. Ability to verify customer email by sending a message
  4. Protecting email verification against spam.

Good luck with your implementation!

Top comments (0)