Subject: [art] Review of draft-levine-herkula-oneclick

From: Martin Thomson <[email-masked]>
Date: Mon, 7 Nov 2016 17:10:12 +1100

This draft seems pretty useful.  It's a very simple idea.  It needs
quite a bit of work before I would consider it fit for publication on
the standards track.

(Note: I reviewed -07; my review of the diff to -08 shows that there
are no relevant changes between the two versions.)

#1  Abstract and Introduction

The first rationale for this draft is completely wrong (abstract and
introduction, p1).  The need for one-click arises because MTAs want to
present UX for unsubscribing and the list providers generally don't
have any programmatic way to access this function.

The HTTPS URI in a List-Unsubscribe is intended to be opened in a
browser and shown to a user. This is what makes it unsuitable for
consumption by a machine or piece of software.  That's the relevant
point here.

That anti-spam software automatically fetches the link is what causes
GETs to these URIs to do the wrong thing is not something this draft
addresses.  It's something to bemoan, but you aren't going to get
people who deploy bad software to write good software by publishing an
RFC.  I would add this point as an aside, noting that this sad truth
is not one that this document will be likely to do anything about.

#2 Section 3.1

Can the List-Unsubscribe header field include more than one HTTPS URI?
 If it can, are all URIs expected to behave equivalently?

#3 Section 3.1

   The list of key/
   value pairs MUST contain the pair "List-Unsubscribe=One-Click".

This is a pointless requirement.  If the mailer wants to mark
one-click subscriptions specially, it can provide whatever string it
likes in the -Post header field.  (It could include that string if
that was what it wanted.)

#4 Section 3.1

   The URI and POST arguments SHOULD include an opaque identifier or
   other hard to forge component in addition to or instead of the plain-
   text names of the list and the subscriber.

This needs to be a MUST.

Is this both the URI and the -Post header field, or either?  I would
have said that either was OK.  You should note that the latter is only
OK if loading the URI triggers user authentication or some other
process that an attacker would be unable to complete.  However, this
doesn't prevent an attacker from unsubscribing a user if ambient
authority (e.g., cookies) were used to authenticate the user because
then the attacker could use the spam (or spear-phishing) trick.  If
ambient authority is used, then the URI MUST include the opaque
identifier.

Also, the entropy requirement needs to be explicit.  96-bits of data
that cannot be predicted by an attacker should suffice.  The security
considerations should observe here that all mail servers that receive
the message will be able to unsubscribe the user.  You should make a
point of that in the security considerations.

#5 Section 3.1; Section 8

   This will deter attacks in which a malicious party sends spam
   with List-Unsubscribe links for a victim list, with the intention of
   causing list unsubscriptions from a victim list as a side effect of
   users reporting the spam.

This is not the only way to cause a user to be unsubscribed if the
links are low entropy.  An attacker can just send POST requests if it
came to that.  You don't need to go to the effort of sending mail to
someone else unless the mailer is going to insist that the mail
receiver authenticate itself.  I don't see that as likely.

What you have created here is what is lovingly referred to as a
capability URI (more info: https://w3ctag.github.io/capability-urls/).
I would strongly encourage you to reference the W3C document here and
maybe give a brief overview of the hazards involved in this design.
It's a fine design, but not without risks.

#6 Section 3.1

   The One-Click action triggered by this URI SHOULD complete promptly
   and not burden the requester in an inappropriate way.

This has 2119-strength language, but I don't see how to enforce it.  I
totally get the next sentence, but if that is the extent of the
requirement, then this sentence doesn't need any of that language.
(Usually, prompt responses are of most benefit to the server, so
insisting on that isn't necessary.)

#7 Section 3.2; Section 5; Section 7.3

   The POST content SHOULD be sent as "multipart/form-data" [RFC7578]
   and MAY be sent as "application/x-www-form-urlencoded".

I think that this is crazy.  This implies that the contents of the
-Post header field is parsed by the mail receiver and then re-composed
into a different form.  I would prefer if the header field were
treated as an opaque string that was passed directly as the body of
the POST request.  This ensures a) that the information is unmodified,
and b) the that mail receiver implementation is as simple as possible.

I think that you could use text/plain with equal efficacy.  Or, better
yet, insist that the server not validate the content-type of the POST
request.  (Note that many implementations on the server side will
ignore the content-type header field when expecting form POSTs, but
insisting on "application/x-www-form-urlencoded" would be reasonable
if you are aware of server code that is deployed and cannot be
updated.)

For Section 5, I think that it would be sufficient to define this as a
completely free-form header field.

#8 Section 3.2

   The
   target of the POST action is the same as or similar to the one in the
   GET action for a manual unsubscription, ...

I don't understand this.  Isn't the target of the POST a URI from the
List-Unsubscribe header field?

Nit in this section: "the the HTTPS URI"

#9 Section 4

Just a question: is there any reason that you insist on DKIM, but
don't tie the DKIM information back to the URI in question?  Is there
any value in matching SDID to the hostname in the HTTPS URI?

---

From: "John Levine" <[email-masked]>
Date: 6 Nov 2016 17:21:17 -0000

Thanks for your review.

>is not one that this document will be likely to do anything about.

No, really, the motivation is what we say it is.  A decade ago,
list-unsubscribe URIs unsubscribed you on the GET and returned a page
saying goodbye so you could script them.  The anti-spam fetches made
the GETs ambiguous, and provoked the confirmation pages.

I suppose I can reword it, although the people who've implemented
it don't seem to have any trouble understanding the motivation.

> If it can, are all URIs expected to behave equivalently?

For this spec, you only get one.

>This is a pointless requirement.

The mailers who've implemented this disagree with you; they find it
useful to verify that the specific intention of the POST is a one-click
unsubscribe.  Do you see any reason it's so harmful that it's worth
breaking the existing implementations?

The latest version of the draft says that the -post header *only*
contains the "List-Unsubscribe=One-Click" tag, to prevent bad
guys from using it to remotely script arbitrary web forms.

>Is this both the URI and the -Post header field, or either?

Just the URI since the -Post header contents is now fixed.

>ambient authority is used, then the URI MUST include the opaque

The latest draft also says don't send cookies or other context info.

>that cannot be predicted by an attacker should suffice.

I think you're making unwarranted assumptions about the security goals
of the people who implement this.  List-Unsubscribe typically contain
mailto: URLs that aren't authenticated at all, and that hasn't been a
big problem in practice.  They want it to be non-trivial to fake an
unsubscribe, but I don't see any evidence that anyone expects military
level security here.  Also keep in mind that the identifier often is
a key to a record in the mailer's database with no relationship to
any other identifier in the message, so as long as the identifier space
is reasonably sparse, it's pretty secure.

>  The security

I suppose, but if someone's mail is going through hostile servers,
he's got worse problems than the occasional unsubscription.  Keep in
mind that the way this works, every recipient gets a separate
customized copy of the message, so only the recipient's own mail
servers are going to see it.  (If you're unfamiliar with the bulk mail
market, they all send separate copies anyway.)

>receiver authenticate itself.  I don't see that as likely.

Honestly, I don't see either as likely.  I'll just take it out.

>It's a fine design, but not without risks.

Sure.  Is there a more stable reference than a github file?

>This has 2119-strength language, but I don't see how to enforce it.

SHOULD means "do this if you want to interoperate unless you have
a good reason to do something else."  I don't see where enforcement
is relevant.

>into a different form.

Yes, the mail receiver does that, but the people who have implemented
this like it just fine.  They typically hand it to a http client
library where it's treated like the contents of a web form. 

> I would prefer if the header field were

That would be bad, since that would let bad guys put arbitrary crud
into http requests.  It would also be a lot harder to program since
they'd have to go around the libraries they're already using.

>List-Unsubscribe header field?

Editing error, it is indeed the same URI as the one you'd use for a manual unsub.

>any value in matching SDID to the hostname in the HTTPS URI?

That's deliberate.  There's lots of service bureaus that send mail on
behalf of clients, the division of work varies a lot, and it's quite
common for the domain in the unsub URI to be different from all the
other domains in the message.  I asked a large mail receiver about
this, and he said the point of the DKIM signature is to have some
identity they can use to say yeah, this is someone we recognize as not
totally evil.

R's,
John

---

From: Martin Thomson <[email-masked]>
Date: Tue, 8 Nov 2016 10:20:37 +1100

> it don't seem to have any trouble understanding the motivation.

You really need to explain that GET to the URI sometimes unsubscribes
immediately (i.e., is unsafe) and sometimes shows a confirmation page.
Nonetheless, my point stands.  The point is that there is no reliable
way for a machine to unsubscribe.  Your point about automatic fetches
of links might muddy the waters some, but it isn't the real
motivation.  (Again, see my point about bad software.)

> For this spec, you only get one.

Say that please.

> guys from using it to remotely script arbitrary web forms.

OK, now I'm really confused.  Why would you waste so many octets to
send a single bit?  I could see the value in having the mailer set an
arbitrary string here.  That's valuable because it allows it to bake
in entropy in the case that it sends a generic unsubscribe link.

> The latest draft also says don't send cookies or other context info.

That doesn't help.  Because if the URI is guessable I can make your
web browser load it (with cookies).  And if the link is opened by the
mail agent in a browser, then I can guarantee that it will have
cookies.

> is reasonably sparse, it's pretty secure.

No, I'm not making assumptions, I'm describing what it takes to make
something secure.  I would also assert that given how easy it is to do
so, it's worth doing.

Of course, people can and will ignore advice.  But we write RFCs on
the understanding that some won't.  I'd like to think that people who
read RFCs also want to know what the best thing to do is and that we
can occasionally help make things better by writing those things down.

> market, they all send separate copies anyway.)

Let's not adopt the policy that because worse things are possible we
shouldn't fix the things that are fixable.  Hostile works in many
ways.  (Ideally mail security would be such that mail servers have
only one choice: drop mail or not.)

> Honestly, I don't see either as likely.  I'll just take it out.

I thought that it was useful to point out the consequences of the
design (i.e., the capability URLs part), but whatever you have there
should cover this well enough.

> Sure.  Is there a more stable reference than a github file?

Welcome to the new W3C.  I don't think so unfortunately :/

> a good reason to do something else."  I don't see where enforcement

So, I've learned that SHOULD needs: a) good interoperability reasons
for complying and b) an explanation of why someone might choose not to
comply.  I see neither here.

> library where it's treated like the contents of a web form.

That's not a good reason.  See below.

> they'd have to go around the libraries they're already using.

Why?  I believe that ALL HTTP libraries allow you to POST octets.
It's actually easier than fiddling with form posting.  And since you
are not including cookies, all you are doing is allowing the bad guy
to ask you to make one single request.  They could make the same
request themselves, far more cheaply.

Allowing mail receivers to modify the data makes this worse for the
mail sender, who has to parse this gunk.  (I'm not the only one who
thinks that HTML form posting is a legacy the web could happily leave
behind.)

> identity they can use to say yeah, this is someone we recognize as not

Acknowledged.  I figured that it would be something like that.

---

From: "John R Levine" <[email-masked]>
Date: 8 Nov 2016 13:25:32 +0530

> immediately (i.e., is unsafe) and sometimes shows a confirmation page.

I thought I did.  We seem to agree about what it should say, I'll see if I 
can make it clearer.

> send a single bit?

Basically, because we want to and it doesn't hurt anything.  At this stage 
I'd want a much stronger reason to change the running code than that it's 
ugly.

>>> [ re URIs and ]the entropy requirement needs to be explicit. 
>>> 96-bits of data that cannot be predicted by an attacker should suffice.
>>
>> of the people who implement this.

> something secure.  I would also assert that given how easy it is to do

A list with a million names is quite large.  If there were a 40 bit 
identifier space, you'd get one in a million tries, a 50 bit space one in 
a billion, and since a try requires an http transaction, they are not 
particularly fast.  Where does 96 bits come from?

Also with respect to cookies and other context, the reason -08 says not to 
send them is to prevent marketers from connecting the unsub to the user's 
previous actvities in the scenario where it's coming from an MUA.  If a 
bad guy were scripting something and doing multiple unsub tries with the 
same cookies, that'd be an immediate tell.

>>>   and not burden the requester in an inappropriate way.

Upon further thought, this is just wrong.  Humans need a fast response 
since they are impatient, but computers can be arbitrarily patient.   Out 
it goes.

[ re sending the POST argument as a web form ]

> thinks that HTML form posting is a legacy the web could happily leave

Perhaps, but the people who are using this use web forms all the time, 
notably on the two-click confirmation page, so the gunk parser is already 
there.  The libraries they use have well debugged support for forms, both 
sending and receiving.  I can believe that in whatever library one might 
use there is some way to send a blob and for the receiver to fetch it 
back, but in my apps using frameworks like Django or Bottle, the web form 
arguments show up as a dictionary for free.

If we were rebuilding the web from scratch, we would no doubt do this 
differently, but we're not.  This is a small tweak to systems that use 
forms now.

Regards,
John Levine, [email-masked], Taughannock Networks, Trumansburg NY
Please consider the environment before reading this e-mail. https://jl.ly