The last chance you have to reject a message from Postfix directly is by checking the contents of the message itself. Postfix offers simple content checking through the parameters:
These checks are an all-or-nothing feature with Postfix. There is no way to bypass checks for certain senders or recipients. For more sophisticated analysis, you should use a separate content filter specifically designed to detect spam. See Chapter 14 for more information on using filters with Postfix.
Each parameter points to a lookup table containing regular expression patterns and actions. The patterns are compared to strings within email messages. If Postfix finds a match, the specified action is executed. By default regular expression checking is not case-sensitive. See Chapter 4 for information on using regular expressions with Postfix lookup tables.
11.9.1 Content Checking Configuration
By default mime_header_checks and nested_header_checks use the same lookup tables as header_checks. If you want to distinguish checks for each one, you can configure them separately; otherwise, configuring header_checks causes mime_header_checks and nested_header_checks to use the same patterns as header_checks. When you assign the checking parameters, indicate both the lookup table and which type of regular expression you are using (see Chapter 4):
header_checks = regexp:/etc/postfix/header_checks body_checks = regexp:/etc/postfix/body_checks
In a pattern-checking lookup table, the lefthand key is a regular expression enclosed by two delimiters (usually forward slashes):
/match string/ REJECT
A typical header_checks file contains lines like the following:
/free mortgage quote/ REJECT /repair your credit/ REJECT /take advantage now/ REJECT
If any of the strings shown appear in any of the headers of a message (these would most likely show up in the Subject: header), the message is rejected. Postfix logs the rejection along with the offending line, and if you specified a message, it is also logged and sent to the client.
11.9.2 Content Checking Actions
The right hand action can be one of the following values. The values that allow an optional text message are indicated. The specified message is sent to the client and logged with the rejection. If you don't supply a message, Postfix uses the default.
Rejects the message when a line from the message matches the regular expression.
Logs a rejection without actually rejecting the message. This action is useful for testing a regular expression to see what happens in the log before using a REJECT to actually reject the message.
Provides a way to delete headers or lines from the body of a message. If the regular expression matches, the line is dropped from the message. This can be useful to strip out internal network information before sending a message outside your network. Be careful about what you delete since most headers are required by the standards and can be very useful in tracking down email problems.
Causes the message to be placed in the HOLD queue. See Chapter 5 for information about the HOLD queue.
Causes Postfix to claim successful delivery and silently discard the message. Sometimes spammer software won't take no for an answer. Even if you reject the message with a 5xx error, the client continues to try to deliver it. DISCARD makes it look as if the message was delivered even though it was simply thrown away. DISCARD can also be useful to minimize the backscatter problem mentioned earlier in the chapter. If an innocent user's email address is used as the sender address, you can claim successful delivery, so that the innocent user does not receive bounce messages.
After queuing the message, Postfix sends it through a separate content filter. See Chapter 14 for more information about setting up separate content filters.
Actions cannot include specific error reply codes or customized restrictions as with access maps.
11.9.3 Comparing Patterns
Header checks compare each header against every pattern in the listed lookup files. Multiline headers are combined into a single line before making comparisons. Each pattern is checked in the order you list them, and checking stops as soon as Postfix finds a match, at which point the message is handled according to the action you specified.
The patterns indicated by the body_checks parameter are checked against each line of the body of the message. Lines are compared one at a time, and each one is checked against every pattern in the order you list them. Checking stops as soon as Postfix finds a match, at which point the message is handled according to the action you specified.
Very long body lines are compared in chunks that are at most as long as the value of the parameter line_length_limit. The default is 2048. Also, by default, Postfix checks the contents of the body only up to the value of body_checks_size_limit. The default is 50 KB. Message headers are compared in chunks that are limited by header_size_limit. These limits are useful in preventing Postfix from scanning the entire file when messages contain large attachments.
Some administrators use header checks for simple virus scanning. You can reject all messages that include attachments with file extensions that might be dangerous to your users:
/name ?="?.*.(bat|com|dll|exe|hta|pif|vbs)"?/ REJECT
You should include any other extensions that you know might pose a problem for your users. Be aware, however, that this pattern is not really sufficient for true virus scanning since you are certain to miss some extensions, and many PC clients may execute files regardless of their extension.
A typical body_checks file contains lines like the following:
/increase your sales by/ REJECT /lowest rates.*!/ REJECT /in compliance (with|of) strict/ REJECT /[:alpha:][:alpha:]/ REJECT Suspicious embedded HTML comments
The second line matches any string that starts with "lowest rates" followed by any text leading to an exclamation point ("We have our lowest rates in 40 years!"). The fourth line checks for HTML comments that are embedded in the middle of words. Remember that this is a common spammer trick to defeat your content filters, but it's also a dead giveaway that the message contains spam.
You can test your regular expressions with the postmap command. Place the contents of a message into a file, then redirect the file to postmap:
$ postmap -q - regexp:/etc/postfix/body_checks < msg.txt opportunity. increase your sales by 500%. Consider REJECT
postmap prints any lines that match any of the regular expressions along with the action specified.
Study the spam you receive to refine and add to your patterns. However, be aware of potential performance problems with poorly written regular expressions. Another potential issue with content checking is that there is no way to whitelist individual messages that you might want to receive despite their containing phrases that trigger a rejection. In particular, if a message is whitelisted during the restriction parameter checking (described earlier in this chapter), it might still be rejected by header and body checks.
As you create rules for detecting spam, keep in mind that your users may differ in what balance they'll accept between some spam and the possibility of blocking some real messages. If you must create different rules for different users, it's probably best not to try to accomplish this with an MTA. Instead consider a specialized delivery agent such as procmail, maildrop, or sieve to set up per-user UBE rules. You can use Postfix to set up broad per-user class restrictions, as you'll see in the next section.