RE: CBV
Mark Shewmaker <[email protected]>
| Newsgroups | gmane.mail.spam.srs.general |
|---|---|
| Message-ID | <[email protected]> |
On Fri, 2004-04-30 at 11:24, Seth Goodman wrote:
> Let me try to address both Tony's and Wayne's questions at the same time.
> Wayne's second point is a valid objection about mailing lists breaking
> content checks. This is a real problem today and it breaks any content
> check, including S/MIME.
I don't think these are valid objections.
Mailing lists shouldn't be sending out emails to subscribers using the
same MAIL FROM given by the original sender anyway, so even if they have
edited the message contents before sending them to their subscribers,
they'll have created a new MAIL FROM to go with that edited content.
As far as MS exchange and other MTA's munging the body of emails and
thus causing checksums to become invalidated--well, I would consider it
a feature that corrupted emails can be detected.
Looking at corrupting MTA's from the viewpoint of both Senders and
Receivers:
Receivers: If receiving machines are behind a corrupting MTA, then
those admins will be aware of their broken mail server.
They'll simply have to configure their server to ignore
messages with corrupted checksums until they get their
broken MTA fixed.
If that's politically difficult, well, they aren't any
worse off than today, and as everyone else adopts workable
solutions, it will become politically easier to push for
an actual fix to their broken MTA.
Senders: Senders who are behind a corrupting MTA will be unlikely to
tell people to verify checksums in the first place, up until
they get their broken MTAs fixed.
Minor rant: In both cases it will become easier for technical types
to point manager types to real business reasons to fix
these technical problems if the problems cause them to
miss out on the ability to detect forgeries, reject likely
spam, and lower help desk costs.
> Though something ultimately needs to be done about
> this in general, let's assume that nothing changes and we have to deal with
> the status quo. For the purpose at hand, which is preventing a harvested
> SES address from being used for spoofing, I will change my proposal and
> suggest that we only need to protect the headers, not the body content to
> avoid Wayne's valid objection.
Except for being prepared ahead of time for any future SMTP extension
that allows body headers to be sent before the rest of DATA, I don't see
any advantage in limiting the checksum to body headers.
More below.
> In Wayne's exploit of SES, the spammer harvests signed addresses from
> responses sent from general sales accounts or other loosely administered
> accounts at large sites. Since this method of exploit is now known, you can
> protect yourself against it by limiting the outgoing mail to a single
> recipient and include a hash in the MAIL FROM: that protects the same
> headers that S/MIME would protect. Please correct me if I have these wrong,
> but I believe that these are To:, cc:, bcc:, From:, Sender: and Date:. With
> a single recipient on the outgoing message and a hash that protects these
> headers, the signed return path would only be useful for the spammer to spam
> himself.
I don't think I'd be the only one to object to solutions that disallow
multiple recipients in the envelope headers.
> Wayne's first point was that we couldn't really protect the content with a
> checksum. I certainly agree with that and I proposed a SHA-1 hash rather
> than a checksum to fix this. If we are protecting only headers and not the
> message body, I think it becomes an intractable problem to synthesize a
> group of headers that generates the same SHA-1 hash as the original message
> headers.
I disagree with the implication that an attacker would effectively find
it any easier to find a collision by adding/changing random characters
in the full message body versus adding/changing random characters in a
limited set of message body headers.
Remember that for a good hash, when you change one bit of the data being
hashed, there's a 50% chance that each bit in the hash output is
flipped.
You don't get any advantage when searching for a hash-collision in
altered 10M files versus 10k files, if a hash collision is your only
goal. (In fact, it's probably slightly easier, given that the hash will
have less data to have to, well, hash.)
The attacker could simply limit the search to changing and testing bits
in *any* of the headers that are looked at. If you're truncating the
hash to four characters then there's more than enough space in even
these few headers in which one could find a collision. (Most users
wouldn't notice a CC: to "dfb89q3n028 <[email protected]>", just as they
wouldn't notics an html comment of "dfb89q3n028bas98hjg" were it
embedded in the message body instead.)
BTW, I think four characters is not nearly enough..
If the hash data is truncated to four characters, (as you suggest in
another message I've quoted below) then you should expect a 50%
likelyhood of finding a hash collision by going through every
just-as-many-bits changes as bits in the truncated hash output. In this
case that's 4 characters multiplied by 6 bits per character, or 24 bits,
meaning 16 million tests--pretty easy to run through, and no matter if
the data being hashed is limited to a few headers or consists of the
entire body, you're still merely cpu bound.
I'm typing this email on a 1.6GHz Athlon, where doing an sha1sum of
/etc/inittab took me .002 seconds. Making the simplifying assumption
that I can do any sha1sum in .002 seconds means that in less than 9
hours and twenty minutes I can test 64^4=2^24=16777216 combinations of
data, for which I would expect a 50% likelyhood of coming across any
specific truncated-to-4-base-64-characters hash I wanted to find.
(In reality the time should be much less, as I wouldn't be actually
loading and dynamically linking the same sha1sum program 64^4 times, but
the point is that this is very doable.)
In any event, four characters is not possibly enough.
Truncating less would obviously improve things, perhaps having the SES
format allow for a variable number of characters for the
verifiable-SES-hash section, and just including enough characters so
that the cpu bound process is intractable at any given time.
However, if SES addresses aren't SRS rewritten, (I'm not yet convinced
that's a good idea in most-people-do-SRS-world, even though I am
admittedly a proponent of whitelisting instead of SRS), then there
shouldn't be any problem with simply increasing the verifiable-SES-hash
section as close as possible to the full 160-bits that sha1 produces.
(Which takes 27 base-64 characters.)
Which brings me to a curve ball of my own to throw into the mix.
Quoting out-of-order the SES format you suggest:
>SES0=HHHH=TT=hhhh=local-part@domain
>
>where
> HHHH = first four base 64 digits of the
> SHA-1 hash of the remainder of the
> address prepended with the hash
> secret (MSA login password) for
> local-part@domain
>
> TT = first two base 32 digits of the UNIX
> integer day number mod 1024 (same as SRS)
>
> hhhh = first four base 64 digits of the
> SHA-1 hash of the specified header
> lines
Working off an assumption that SES addresses aren't ever rewritten by
SRS forwarders, but instead gets passed directly through, (I think this
is what's being proposed by the SES modifier), I have a completely
off-the-wall idea for altering SES..
1. Imagine an SES-enabled MTA sends out emails with the following
mail_from format, slightly different from the above:
SES0=H27=T4=L27=local-part@domain
Or:
SES0= Unchanged.
H27= A 27-character base-64 representation of all 160 sha1
bits of all of the following: ("SES0", local secret,
T4, C27, local-part@domain).
T4= A 4-character base-64 representation of a 32-bit time_t,
the number of seconds since the epoch.
L27= A 27-character base-64 representation of all 160 sha1
bits of all of the following ("SES0", local secret, TT,
local-part@domain.)
For completeness:
B27= A 27-character base-64 representation of all 160 sha1
bits of all of the following ("SES0", TT,
local-part@domain, message body.)
Note that C27 is not included in this mail_from, but
it is included in the computation of H27.
2. Doing the math on the above to look at character-lengths,
two 27-character hashes, plus the remaining "SES0==TT=" text
adds up to 66 characters before we include the original, unSESed
local-part.
But we're not supposed to make local-parts that are larger than
64 characters, and we can't exactly say that the included local-part
has to be shorter than negative two characters long.
So, change the format definition again and say that in making a new
mail_from of the format SES0=H27=T4=L27=local-part@domain, that
the H, T, and L components can be made using "up to" 27, 4, and 27
characters.
So, the SES sender will truncate H, T, and L such that the total
new local-part doesn't exceed 64 characters in length.
It doesn't particularly matter what algorithm is used, merely that
given a length of a local-part and a T4, that the SES-creating-code
used will *always* come up with the same H, T, and L lengths.
A side implication is that any particular local-policy decisions
on minimum acceptable H, T, and L lengths will translate into a
local-policy maximum size of an unSESed localpart.
(For instance, having H, T, and L be required to be at least
4, 2, and 4 characters each results in a maximum local-part of
46 characters.)
3. Recipients who do simple CBVs will submit this
SES0=H27=T4=L27=local-part@domain
for validation.
In other words, they're submitting for CBV the same mail_from
line they received.
The sending-machine can look at the local-part and its policy,
and figure the needed lengths of H, T, and L, and figure out
whether the CBV's given local-part@domain and T match up with
its given L, *ignoring H27 altogether*.
If there's a match, then the CBV will succeed.
Note that as a protection against replay attacks, the sending
machine could note how many CBVs it's recently been requested to
do for any particular T, L, and local-part@domain combination,
perhaps slowing down the response to CBVs with that combination
after a time, perhaps even invalidating that combination after
some threshold of number/timing of CBVs have been made.
(This is vaguely similar to unix logins slowing down after a
unsuccessful login attempts.)
This is a Sender-side protection against replay attacks.
This protection against replay attacks requires a database
of sorts for the MX machines, but it can be pretty ephemeral,
keeping stat info that ages out for instance. That would
protect against replay attacks of a million copies of the same
mail_from within a half-hour interval, but not replay attacks
of large numbers of separately harvested mail_froms.
A more extensive database, keeping track of the number of
recipients for any T, L, local-part@domain combination can
help tune the triggering of CBV-response slowdown and lockouts.
(If you know you only had one recipient but had 10000 callbacks,
then something is probably wrong, though it's conceivable that
legitimate alias expansion forwarding is being used.)
4. Recipients who are willing to do body checks can submit an
alternative mail_from of the format:
SES0=H27=T4=B27=local-part@domain
This is the same as the mail_from that the sender sent,
except the L section is replaced with the body checksum B.
When the sending machine receives a CBV of that format, it
computes the H checksum that it would have made sent out,
were it to have sent out an SES0 address with the included
T, B, and local-part@domain, (taking into account the
local secret applicable for that T and local-part@domain
of course.)
Note that the sending machine, given local-part@domain,
will know how many characters it would have put in its H27,
so even if it only sent out an H10 instead of H27, and the
recipient only has an H10 to send back, the Sender will
see that that smaller H10 is a legitimate length to receive
for a CBV, and it will simply do compare the first 60 bits
of the would-have-been-H27 sha1 hash, (H10 gives 60 bits.)
This check protects recipients from replay attacks,
even if thousands of other recipients are falling victim
to replay attacks and only do normal, non-checksumming CBVs.
It also requires no database on the Sending machine side of
things.
5. Since I didn't specify that CBV checks from (3) or (4) have
any different format, the Sending machine has to potentially
do two tests if it receives an [LC]27 of 27 actual characters,
and the length of the local-part given would have caused it
to have sent out an L27 of 27 characters.
So it would have to do the test in 3, and if that failed, do the
test in (4), returning success if either succeeded or failure if
both failed.
Of course, if the length of the local-part meant that the Sending
machine would have truncated L27 below, err 27 characters, then
the receipt of a mail_from with a full B27 means that test (3)
can be skipped.
6. A full H27 and B27 means the mail_from local part is more than 64
characters, which is not nice according to rfc2821.
One solution to that bit of awkwardness is to take a server
advertising SES capability in its SES string to be understood
to be claiming that it can *accept* local-parts that are not just
64 characters, but 64+2*27+12=130 characters. (That accounts for
an H27, a T4, and a B27.)
Recipient machines who want to do SES body checks can then only
do them if they know that their outgoing MTAs can deal with that
length of local-parts, and their incoming MTAs don't corrupt message
bodies.
This alternative way of doing SES allows for body checks to be done or
not done on either side, while using as many characters as possible for
any given original local-part.
(Note that even if the sender doesn't send out a full H27 but instead
truncates the sent hash down to an H4, an attacker trying to modify a
message such that their mail-from (with C27) matches to a valid H4 can't
just compute themselves through all 16 million H4 combinations, but
would instead have to ask the server to verify each of these in turn,
something that should be relatively easy to detect, and even thwarted by
increasing incoming CBV delays after some threshold of
bad-CBVs-per-unit-time.)
(Now my brain hurts. Although I think something like this is a better
way of doing SES, in that it accounts for the problems folks point out
with it such as replay attacks, I'm not sure if a scheme of this
additional complexity would ultimately help or hinder spf adoption in
the long run. I'm also not sure how it would or should interact with
VERP.)
(I'm also not sure whether I think that the necessary assumption, that
SES addresses should never be rewritten with SRS, is a good idea or
not.)
Any thoughts?
--
Mark Shewmaker
[email protected]