HTTP Subscriptions Draft -- Revisiting Verification

359 views
Skip to first unread message

Jeff Lindsay

unread,
Dec 7, 2012, 4:33:41 PM12/7/12
to webh...@googlegroups.com
There's an informal draft for HTTP Subscriptions up here:

It's mostly based on the way PubSubHubbub works, but one thing that would be great to figure out is a better way to verify a callback URL wants to subscribe. 

The current method borrowed from PubSubHubbub is to do a verification request with the callback URL that includes a challenge token that needs to be echo'd back. 

This is not a very user friendly experience for anybody wanting to use HTTP Subscriptions for normal webhooks considering most webhooks implementations don't do any sort of verification. I think, though, that to push anything that has a chance as a standard this needs to be addressed, just like PSHB determined. 

Unlike PSHB, there are a lot more developers implementing callback URLs than the feed consumer / reader ecosystem PSHB is made for. Companies like Twilio depend on a friendly experience. 

One suggestion was to have a whitelist/blacklist approach for domains. This is the robots.txt business in the draft. Semantically it makes sense but you could argue it's overloading robots.txt too much. It also doesn't solve the issue of verifying the intent to unsubscribe.

This morning I had an idea for an alternative approach, but I wanted to see if anybody else had any ideas first. Or general feedback on the spec so far.

--
Jeff Lindsay
http://progrium.com

Steve Marx

unread,
Dec 7, 2012, 4:50:49 PM12/7/12
to webh...@googlegroups.com
This looks interesting. Thanks for sharing it.

Why is a verification step required at all for subscribing? If the goal is to prevent spamming an unwilling participant, why not just kill a subscription after a certain number of 4xx responses? I would see this as optional good behavior on the part of the producer.

On the unsubscribe step, I think I see why verification is there, but I would prefer a different mechanism. If the subscribe operation returned a secret that identified the subscription (likely just an unguessable string like a GUID), then this could just be a single step with something like  "Pragma: unsubscribe=<secret>." Perhaps this could just be what's returned in the Link header at the time of subscription. It wasn't clear to me whether that link was a secret. (If so, perhaps the RESTier DELETE <subscription URL> would be nice. You alluded to something similar in the spec, but maybe that should be the primary mechanism.)


--
You received this message because you are subscribed to the Google Groups "WebHooks" group.
To post to this group, send email to webh...@googlegroups.com.
To unsubscribe from this group, send email to webhooks+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/webhooks?hl=en.

Jeff Lindsay

unread,
Dec 7, 2012, 5:10:51 PM12/7/12
to webh...@googlegroups.com
Why is a verification step required at all for subscribing? If the goal is to prevent spamming an unwilling participant, why not just kill a subscription after a certain number of 4xx responses? I would see this as optional good behavior on the part of the producer.

You can't guarantee that the URL they're giving is even a URL intended to handle events, so you can't assume it's going to realize it's getting hammered and then change the status code. I think in general you have a point that it should do something smart when a callback URL is returning 4xx or 5xx codes, but I'm not sure that's a solution for verification of intent. 
 

On the unsubscribe step, I think I see why verification is there, but I would prefer a different mechanism. If the subscribe operation returned a secret that identified the subscription (likely just an unguessable string like a GUID), then this could just be a single step with something like  "Pragma: unsubscribe=<secret>." Perhaps this could just be what's returned in the Link header at the time of subscription. It wasn't clear to me whether that link was a secret. (If so, perhaps the RESTier DELETE <subscription URL> would be nice. You alluded to something similar in the spec, but maybe that should be the primary mechanism.)

Yeah, that's interesting. I'm not sure a subscription URL is a good idea to require, so I'm hesitant to make unsubscribing depend on it. And in terms of returning a secret key for unsubscribing, that's also interesting, but I think the ideal scenario avoids state as much as possible. We're already dealing with state with a callback URL and maybe a subscription URL, and the more you add the more complex it gets.

Another semi-stateful approach is to use HMAC like in content signing. If the callback URL always returns a header with an HMAC that includes a secret that's shared during the subscription or unsubscribe request, you're basically doing a form of authentication that verifies the agent performing an operation owns the callback URL. I say semi-stateful because it can be a configuration constant instead of a server generated token you have to store.

I'm just wondering if that's a suitable mechanism that, for example, Twilio would be willing to have their users implement.

-jeff

Steve Marx

unread,
Dec 7, 2012, 7:45:44 PM12/7/12
to webh...@googlegroups.com
Perhaps the reason I'm not very happy about verification is that I'm thinking of a different set of examples. Today, I can create a Twilio phone number and make them POST to your server every time a phone call comes in. I can also go to GitHub and set up a post-receive hook on my repo that POSTs to your server every time someone pushes a commit. In neither case is there any verification step, and I think this is okay.

How important verification is may have something to do with volume and how easy it is to generate requests. In the case of Twilio, someone has to be making lots of phone calls (or sending lots of texts) for the target server to care, and it would be easier to just spam the target server directly. The same holds for GitHub. I'm more in favor of verification for things that have the potential to generate a lot of traffic (at low cost to the "attacker"). For example, email addresses and phone numbers are good to verify to avoid spamming people.

Because I think it depends on the application (and volume), I'd prefer verification to be optional, even if it's still in the scope of this spec. (A standard protocol for verification, when desired, is useful. I just don't want it to be required.)

In terms of unsubscribe, I see what you're saying about state. I fully agree that state is to be avoided as much as possible. There are two places where state might be:
  1. On the producer side, a per-subscription secret (or URL) doesn't add much state. The producer needs to remember about the subscription anyway so it knows where to send the events, so this is just one more small piece of data to store with the subscription.
  2. On the consumer side, presumably the consumer needs to keep track of subscriptions somehow if it cares about unsubscribing. (It needs to know the URL to use when unsubscribing.) In that case, it's similar to the producer side, where remembering a secret doesn't add much state.
I'll complicate things slightly by adding that there's a potential third party here. A producer produces some notifications. A consumer receives those notifications. There's also a subscriber (for lack of a better term), which is the thing that created the subscription. In my world view, this isn't always the same as the consumer.

As a practical example, Webscript supports modules located in GitHub, and we ask that module authors add us as a post-receive hook, so we can easily keep up-to-date with their changes. In this scenario, GitHub is a producer, Webscript is a consumer, and the module author is the subscriber. In this three-party model, I would argue that both the module author (subscriber) and Webscript (consumer) should be able to end the subscription. That means they would both need to have the unsubscribe token/secret. In the current model, where unsubscribe requires verification, if the module author wanted to end a subscription, he/she would have to cooperate with Webscript so that Webscript would verify the unsubscribe.

Sorry for the long stream-of-consciousness email. I hope this is helpful feedback.

Tony Garnock-Jones

unread,
Dec 7, 2012, 8:13:36 PM12/7/12
to webh...@googlegroups.com
On 7 December 2012 19:45, Steve Marx <sm...@smarx.com> wrote:
I'll complicate things slightly by adding that there's a potential third party here. A producer produces some notifications. A consumer receives those notifications. There's also a subscriber (for lack of a better term), which is the thing that created the subscription. In my world view, this isn't always the same as the consumer.

This is spot on. Rabbithub (an implementation of parts of pubsubhubbub) extends PSHB for just this reason, adding a "generate_token" action that can be used by a subscriber to get permission from a consumer to subscribe it to a producer. It looks like I never documented this adequately; the only real sketch of the idea is https://raw.github.com/tonyg/rabbithub/master/doc/subscription-http-messaging.jpg. (Note use of Granovetter diagram arrows. PSHB is within epsilon of being capability-securable.) The implementation, I'm afraid, is the only place where details can be found.

Regards,
  Tony

Tony Garnock-Jones

unread,
Dec 7, 2012, 8:25:38 PM12/7/12
to webh...@googlegroups.com
On 7 December 2012 17:10, Jeff Lindsay <prog...@gmail.com> wrote:
Yeah, that's interesting. I'm not sure a subscription URL is a good idea to require, so I'm hesitant to make unsubscribing depend on it. And in terms of returning a secret key for unsubscribing, that's also interesting, but I think the ideal scenario avoids state as much as possible. We're already dealing with state with a callback URL and maybe a subscription URL, and the more you add the more complex it gets.

One thing the draft is missing at the moment is specification of how the unsubscription handler finds the subscription to delete. That is, how is equivalence of "Callback" header values determined? Binary or textual equivalence is probably not quite right. Perhaps the combination of URL + method + rel, in some canonical order, is appropriate. Perhaps the secret should be included as well, or perhaps it's superfluous given the verification step.

Regards,
  Tony

Ivan Žužak

unread,
Dec 9, 2012, 7:30:07 AM12/9/12
to webh...@googlegroups.com
Hi Jeff,

really appreciate your effort with the spec -- great stuff. Here's a few thoughts about verification.

On Friday, December 7, 2012 11:10:51 PM UTC+1, Jeff Lindsay wrote:

Why is a verification step required at all for subscribing? If the goal is to prevent spamming an unwilling participant, why not just kill a subscription after a certain number of 4xx responses? I would see this as optional good behavior on the part of the producer.

You can't guarantee that the URL they're giving is even a URL intended to handle events, so you can't assume it's going to realize it's getting hammered and then change the status code. I think in general you have a point that it should do something smart when a callback URL is returning 4xx or 5xx codes, but I'm not sure that's a solution for verification of intent. 

Why does the URL wrongly given as the callback have to realize that it's getting hammered? It will either dismiss the messages it receives or do something with them. In most cases it will be the former, and such cases aren't making any problems for the server of that URL since it's just bouncing requests, right? Happens all the time on the Web. If on the other hand the callback wants to do something with the messages it didn't ask to receive (store them, process them, whatever) - then just let it, why not? Because, if the callback is not doing any verification or authorization itself for messages it receives (and expects only verified and authorized messages) - then this is a larger problem, not something this spec should try to fix, imo. 

Basically, I think that servers of callback URLs can and should protect themselves in a general way (has nothing to do with HTTP subscriptions), and agree that the subscription server should also protect itself. Subscription servers can protect themselves in terms of monitoring the status of subscriptions and suspending them for whatever reason (e.g. too many 4xx/5xx responses), and by using authorization on subscription requests -- if a user created a subscription that is causing spam for someone else, then suspend both the subscription and the user. 

How about requiring subscription servers to have a special "suspend subscription for callback URL" mechanism (resource) which spammed servers could use to report that they are receiving spam on a specific URL? So the subscription server would then be able to suspend subscriptions that are causing this flood? A potential risk is random people reporting false spam to suspend other people's valid subscriptions, but since callback URLs are not publicly known and may be long+random -- it's not a big issue, imo.
 
On the unsubscribe step, I think I see why verification is there, but I would prefer a different mechanism. If the subscribe operation returned a secret that identified the subscription (likely just an unguessable string like a GUID), then this could just be a single step with something like  "Pragma: unsubscribe=<secret>." Perhaps this could just be what's returned in the Link header at the time of subscription. It wasn't clear to me whether that link was a secret. (If so, perhaps the RESTier DELETE <subscription URL> would be nice. You alluded to something similar in the spec, but maybe that should be the primary mechanism.)

Yeah, that's interesting. I'm not sure a subscription URL is a good idea to require, so I'm hesitant to make unsubscribing depend on it. And in terms of returning a secret key for unsubscribing, that's also interesting, but I think the ideal scenario avoids state as much as possible. We're already dealing with state with a callback URL and maybe a subscription URL, and the more you add the more complex it gets.

Both extra state and extra verification messages are forms of (additional) complexity. We just have to decide which is less worse for this case. 

One of the things I liked about PSHB was the goal to keep complexity hidden in the PSHB server. I think that should apply to verification also. Extra state does mean extra complexity in subscription servers, but that's the kind of complexity developers are used to handling -- there are know approaches for scaling. For "small" subscription server -- this is not even an issue. For "large" subscription servers -- developers will know what they are doing. For "small" becoming "large" -- scaling is something they will be researching and handling irregardless of PSHB subscriptions. 

On the other hand, extra verification messages mean extra burden for the network (+1 request/response), as well as for the server and for the subscriber since they have to manage this process internally. This means that the former (=not having a verify step) will be easier to specify in the spec and to implement by developers, while the latter (=having a verify step) will be harder in both ways and will probably make developers squeal just like PSHB verifications did. And I think that one of the goals of the spec should be simplicity, so +1 to not requiring extra verification steps and using a subscription URL + secret approach by default (or using the alternative approach you say you though of but didn't write about yet :))

+ I like the robots.txt idea, but I would just say that subscription servers must obey robots.txt rules for all requests they intend to send (and suspend subscriptions that violate these rules). 

Ivan

Tony Garnock-Jones

unread,
Dec 9, 2012, 9:33:55 AM12/9/12
to webh...@googlegroups.com
On 9 December 2012 07:30, Ivan Žužak <izu...@gmail.com> wrote:
PSHB verifications did. And I think that one of the goals of the spec should be simplicity, so +1 to not requiring extra verification steps and using a subscription URL + secret approach by default (or using the alternative approach you say you though of but didn't write about yet :))

What about having a CORS-like declaration from the callback URL permitting use as a subscription endpoint? If you GET or POST the callback URL and don't see a  "Pragma: subscribe-confirm" header (or similar) in the response, then the subscription is considered cancelled/not-verified.

Maybe even more CORS like: "Subscription-Control-Allow-Origin: *" as in "I am expecting deliveries from subscriptions originating at x.y.z domain".

I don't like the robots.txt so much because it doesn't feel specific enough. Unless I misunderstand, it looks like it wouldn't be able to distinguish between e.g. crawler traffic and subscription-delivery traffic.

(In principle you might not need an explicit unsubscribe definition with a CORS-like header: you could change the callback to no longer answer the header concerned, and then *resubscribe* it which would cause a ping from the publisher. The publisher would see the lack of the header, and would interpret this as a cancelled subscription.)

Tony

Jeff Lindsay

unread,
Feb 1, 2013, 8:06:18 PM2/1/13
to webh...@googlegroups.com
It sounds like maybe the first iteration should skip verification of intent, since it's not always necessary. Later we can add an extension for event sources to require verification of intent. 

That will help solidify something usable/useful quicker. Thoughts?


--
You received this message because you are subscribed to the Google Groups "WebHooks" group.
To post to this group, send email to webh...@googlegroups.com.
To unsubscribe from this group, send email to webhooks+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/webhooks?hl=en.

Steve Marx

unread,
Feb 1, 2013, 8:08:59 PM2/1/13
to webh...@googlegroups.com
That sounds smart to me.


To unsubscribe from this group and stop receiving emails from it, send an email to webhooks+u...@googlegroups.com.

To post to this group, send email to webh...@googlegroups.com.

Ivan Žužak

unread,
Feb 2, 2013, 2:43:01 AM2/2/13
to webh...@googlegroups.com
+1

Jeff Lindsay

unread,
Mar 4, 2013, 10:54:08 AM3/4/13
to webh...@googlegroups.com
If anybody wants to take a stab at just removing that section and putting in a pull request, I've been trying to get around to it but it hasn't happened yet.


--
You received this message because you are subscribed to the Google Groups "WebHooks" group.
To unsubscribe from this group and stop receiving emails from it, send an email to webhooks+u...@googlegroups.com.
To post to this group, send email to webh...@googlegroups.com.
Visit this group at http://groups.google.com/group/webhooks?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Ivan Žužak

unread,
Mar 5, 2013, 6:54:32 AM3/5/13
to webh...@googlegroups.com
Made a pull request a minute ago; hopefully I ironed out all the parts that used verification.

Cheers,
Ivan

Jeff Lindsay

unread,
Mar 5, 2013, 9:42:51 PM3/5/13
to webh...@googlegroups.com
Nice, thanks.
Reply all
Reply to author
Forward
0 new messages