# Postfix Backscatter Howto

## Overview

This document describes features that require Postfix version 2.0 or later.

Topics covered in this document:

- What is backscatter mail?
- How do I block backscatter mail to random recipient addresses?
- How do I block backscatter mail to real recipient addresses?
  - Blocking backscatter mail with forged mail server information
  - Blocking backscatter mail with forged sender information
  - Blocking backscatter mail with other forged information
  - Blocking backscatter mail from virus scanners

The examples use Perl Compatible Regular Expressions (Postfix pcre: tables), but also provide a translation to POSIX regular expressions (Postfix regexp: tables). PCRE is preferred primarily because the implementation is often faster.

## What is backscatter mail?

When a spammer or worm sends mail with forged sender addresses, innocent sites are flooded with undeliverable mail notifications. This is called backscatter mail. With Postfix, you know that you're a backscatter victim when your logfile goes on and on like this:

>     Dec  4 04:30:09 hostname postfix/smtpd[58549]: NOQUEUE: reject:
>     RCPT from xxxxxxx[x.x.x.x]: 550 5.1.1 <yyyyyy@your.domain.here>:
>     Recipient address rejected: User unknown; from=<>
>     to=<yyyyyy@your.domain.here> proto=ESMTP helo=<zzzzzz>

What you see are lots of "user unknown" errors with "from=<>". These are error reports from MAILER-DAEMONs elsewhere on the Internet, about email that was sent with a false sender address in your domain.

## How do I block backscatter mail to random recipient addresses?

If your machine receives backscatter mail to random addresses, configure Postfix to reject all mail for non-existent recipients as described in the LOCAL_RECIPIENT_README and STANDARD_CONFIGURATION_README documentation.

If your machine runs Postfix 2.0 and earlier, disable the "pause before reject" feature in the SMTP server. If your system is under stress then it should not waste time.

>     /etc/postfix/main.cf:
>         # Not needed with Postfix 2.1 and later.
>         smtpd_error_sleep_time = 0

>         # Not needed with Postfix 2.4 and later.
>         unknown_local_recipient_reject_code = 550

## How do I block backscatter mail to real recipient addresses?

When backscatter mail passes the "unknown recipient" barrier, there still is no need to despair. Many mail systems are kind enough to attach the message headers of the undeliverable mail in the non-delivery notification. These message headers contain information that you can use to recognize and block forged mail.

### Blocking backscatter mail with forged mail server information

Although my email address is "wietse@porcupine.org", all my mail systems announce themselves with the SMTP HELO command as "hostname.porcupine.org". Thus, if returned mail has a Received: message header like this:

>     Received: from porcupine.org ...

Then I know that this is almost certainly forged mail (almost; see next section for the fly in the ointment). Mail that is really sent by my systems looks like this:

>     Received: from hostname.porcupine.org ...

For the same reason the following message headers are very likely to be the result of forgery:

>     Received: from host.example.com ([1.2.3.4] helo=porcupine.org) ...
>     Received: from [1.2.3.4] (port=12345 helo=porcupine.org) ...
>     Received: from host.example.com (HELO porcupine.org) ...
>     Received: from host.example.com (EHLO porcupine.org) ...

Some forgeries show up in the way that a mail server reports itself in Received: message headers. Keeping in mind that all my systems have a mail server name of hostname.porcupine.org, the following is definitely a forgery:

>     Received: by porcupine.org ...
>     Received: from host.example.com ( ... ) by porcupine.org ...

Another frequent sign of forgery is the Message-ID: header. My systems produce a Message-ID: of <stuff@hostname.porcupine.org>. The following are forgeries, especially the first one:

>     Message-ID: <1cb479435d8eb9.2beb1.qmail@porcupine.org>
>     Message-ID: <yulszqocfzsficvzzju@porcupine.org>

To block such backscatter I use header_checks and body_checks patterns like this:

>     /etc/postfix/main.cf:
>         header_checks = pcre:/etc/postfix/header_checks
>         body_checks = pcre:/etc/postfix/body_checks

>     /etc/postfix/header_checks:
>         # Do not indent the patterns between "if" and "endif".
>         if /^Received:/
>         /^Received: +from +(porcupine\.org) +/
>             reject forged client name in Received: header: $1
>         /^Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
>             reject forged client name in Received: header: $2
>         /^Received:.* +by +(porcupine\.org)\b/
>             reject forged mail server name in Received: header: $1
>         endif
>         /^Message-ID:.* <!&!/ DUNNO
>         /^Message-ID:.*@(porcupine\.org)/
>     	reject forged domain name in Message-ID: header: $1

>     /etc/postfix/body_checks:
>         # Do not indent the patterns between "if" and "endif".
>         if /^[> ]*Received:/
>         /^[> ]*Received: +from +(porcupine\.org) /
>             reject forged client name in Received: header: $1
>         /^[> ]*Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
>             reject forged client name in Received: header: $2
>         /^[> ]*Received:.* +by +(porcupine\.org)\b/
>             reject forged mail server name in Received: header: $1
>         endif
>         /^[> ]*Message-ID:.* <!&!/ DUNNO
>         /^[> ]*Message-ID:.*@(porcupine\.org)/
>     	reject forged domain name in Message-ID: header: $1

Notes:

- The example uses pcre: tables mainly for speed; with minor modifications, you can use regexp: tables as explained below.

- The example is simplified for educational purposes. In reality my patterns list multiple domain names, as "`(domain|domain|...)`".

- The "`\.`" matches "`.`" literally. Without the "`\`", the "`.`" would match any character.

- The "`\(`" and "`\)`" match "`(`" and "`)`" literally. Without the "`\`", the "`(`" and "`)`" would be grouping operators.

- The "`\b`" is used here to match the end of a word. If you use regexp: tables, specify "`[[:>:]]`" (on some systems you should specify "`\>`" instead; for details see your system documentation).

- The "if /pattern/" and "endif" eliminate unnecessary matching attempts. DO NOT indent lines starting with /pattern/ between the "if" and "endif"!

- The two "`Message-ID:.* <!&!`" rules are workarounds for some versions of Outlook express, as described in the caveats section below.

Caveats

- Netscape Messenger (and reportedly, Mozilla) sends a HELO name that is identical to the sender address domain part. If you have such clients then the above patterns would block legitimate email.

  My network has only one such machine, and to prevent its mail from being blocked I have configured it to send mail as user@hostname.porcupine.org. On the Postfix server, a canonical mapping translates this temporary address into user@porcupine.org.

  >     /etc/postfix/main.cf:
  >         canonical_maps = hash:/etc/postfix/canonical

  >     /etc/postfix/canonical:
  >         @hostname.porcupine.org @porcupine.org

  This is of course practical only when you have very few systems that send HELO commands like this, and when you never have to send mail to a user on such a host.

  An alternative would be to remove the hostname from "hostname.porcupine.org" with address masquerading, as described in the ADDRESS_REWRITING_README document.

- Reportedly, Outlook 2003 (perhaps Outlook Express, and other versions as well) present substantially different Message-ID headers depending upon whether or not a DSN is requested (via Options "Request a delivery receipt for this message").

  When a DSN is requested, Outlook 2003 uses a Message-ID string that ends in the sender's domain name:

  >     Message-ID: <!&! ...very long string... ==@example.com>

  where example.com is the domain name part of the email address specified in Outlook's account settings for the user. Since many users configure their email addresses as username@example.com, messages with DSN turned on will trigger the REJECT action in the previous section.

  If you have such clients then you can exclude their Message-ID strings with the two "`Message-ID:.* <!&!`" patterns that are shown in the previous section. Otherwise you will not be able to use the two backscatter rules to stop forged Message ID strings. Of course this workaround may break the next time Outlook is changed.

### Blocking backscatter mail with forged sender information

Like many people I still have a few email addresses in domains that I used in the past. Mail for those addresses is forwarded to my current address. Most of the backscatter mail that I get claims to be sent from these addresses. Such mail is obviously forged and is very easy to stop.

>     /etc/postfix/main.cf:
>         header_checks = pcre:/etc/postfix/header_checks
>         body_checks = pcre:/etc/postfix/body_checks

>     /etc/postfix/header_checks:
>         /^(From|Return-Path):.*\b(user@domain\.tld)\b/
>             reject forged sender address in $1: header: $2

>     /etc/postfix/body_checks:
>         /^[> ]*(From|Return-Path):.*\b(user@domain\.tld)\b/
>             reject forged sender address in $1: header: $2

Notes:

- The example uses pcre: tables mainly for speed; with minor modifications, you can use regexp: tables as explained below.

- The example is simplified for educational purposes. In reality, my patterns list multiple email addresses as "`(user1@domain1\.tld|user2@domain2\.tld)`".

- The two "`\b`" as used in "`\b(user@domain\.tld)\b`" match the beginning and end of a word, respectively. If you use regexp: tables, specify "`[[:<:]]` and `[[:>:]]`" (on some systems you should specify "`\<` and `\>`" instead; for details see your system documentation).

- The "`\.`" matches "`.`" literally. Without the "`\`", the "`.`" would match any character.

### Blocking backscatter mail with other forged information

Another sign of forgery can be found in the IP address that is recorded in Received: headers next to your HELO host or domain name. This information must be used with care, though. Some mail servers are behind a network address translator and never see the true client IP address.

### Blocking backscatter mail from virus scanners

With all the easily recognizable forgeries eliminated, there is one category of backscatter mail that remains, and that is notifications from virus scanner software. Unfortunately, some virus scanning software doesn't know that viruses forge sender addresses. To make matters worse, the software also doesn't know how to report a mail delivery problem, so that we cannot use the above techniques to recognize forgeries.

Recognizing virus scanner mail is an error prone process, because there is a lot of variation in report formats. The following is only a small example of message header patterns. For a large collection of header and body patterns that recognize virus notification email, see https://web.archive.org/web/20100317123907/http://std.dkuug.dk/keld/virus/ or https://www.t29.dk/antiantivirus.txt.

>     /etc/postfix/header_checks:
>         /^Subject: *Your email contains VIRUSES/ DISCARD virus notification
>         /^Content-Disposition:.*VIRUS1_DETECTED_AND_REMOVED/
>             DISCARD virus notification
>         /^Content-Disposition:.*VirusWarning.txt/ DISCARD virus notification

Note: these documents haven't been updated since 2004, so they are useful only as a starting point.

A plea to virus or spam scanner operators: please do not make the problem worse by sending return mail to forged sender addresses. You're only harassing innocent people. If you must return mail to the purported sender, please return the full message headers, so that the sender can filter out the obvious forgeries.