I am maintaining RailsPlugins.org and just added post hooks and full integration with the GemCutter API v1. The API is very nice to play with.
It only took a few hours from yo to go, now RailsPlugins.org will automatically receive any version updates for gems that it hosts and create new versions on the fly for the owner, inheriting the previous version's compatibility, emailing the owner letting them know what is going on.
One thing that I have noticed though, is there is no way to be sure that RubyGems is the source of the web hook, so anyone who knows my web hook URL, could cause all sorts of havoc.
I want to add a signature to all payloads that get sent out from gemcutter. So I pushed a proposed commit to my fork of gemcutter, which you can find here:
It is very simple. I decided against just including the users API key clear text, simply because the user might make a mistake on the URL or whatever... I'm happy to just make this an API key as the signature if group consensus, but the method I have chosen treats the API key as a shared secret, which I think it should be.
Another point is that this is part of web_hook.rb, and so would apply to all existing APIs. As we are adding a signature, I don't think this would break any existing implementation of API v0 or v1 and as it is a non destructive change, the end user can just to implement or ignore verification without any impact on their system.
Anyway, the code is there, let me know, and thanks all again for GemCutter's API.
That's what I was worrying about when I writing gemhooker(.appspot.com, example of fake POST request: http://gist.github.com/272168) and went for verifying that 72.4.120.124 was the actual sender... which was kinda cheap solution.
I think your solution is way better (and clean, and smart... at least not based on IP addresses :)).
I don't think I'm entitled to comment your contribution, but I have one comment (based on a previous discussion I had with Nick): shouldn't we try to copy the authenticated content distribution of the PubSubHubbub protocol http://bit.ly/a8hIsf ?
So after reviewing your patch, this means: 1. Prefer SHA1 to MD5. 2. Implement the secret key per hooks (and not user). So that the secret API key (that allows to delete gems, manage owners, etc...) still remains secret and in a single place.
On Sun, Feb 28, 2010 at 5:54 AM, Mikel Lindsaar <raasd...@gmail.com> wrote: > Hi all,
> I am maintaining RailsPlugins.org and just added post hooks and full > integration with the GemCutter API v1. The API is very nice to play > with.
> It only took a few hours from yo to go, now RailsPlugins.org will > automatically receive any version updates for gems that it hosts and > create new versions on the fly for the owner, inheriting the previous > version's compatibility, emailing the owner letting them know what is > going on.
> One thing that I have noticed though, is there is no way to be sure > that RubyGems is the source of the web hook, so anyone who knows my > web hook URL, could cause all sorts of havoc.
> I want to add a signature to all payloads that get sent out from > gemcutter. So I pushed a proposed commit to my fork of gemcutter, > which you can find here:
> It is very simple. I decided against just including the users API key > clear text, simply because the user might make a mistake on the URL or > whatever... I'm happy to just make this an API key as the signature if > group consensus, but the method I have chosen treats the API key as a > shared secret, which I think it should be.
> Another point is that this is part of web_hook.rb, and so would apply > to all existing APIs. As we are adding a signature, I don't think > this would break any existing implementation of API v0 or v1 and as it > is a non destructive change, the end user can just to implement or > ignore verification without any impact on their system.
> Anyway, the code is there, let me know, and thanks all again for > GemCutter's API.
On Mon, Mar 1, 2010 at 1:42 AM, Franck Verrot <linuxsh...@gmail.com> wrote: > So after reviewing your patch, this means: > 1. Prefer SHA1 to MD5. > 2. Implement the secret key per hooks (and not user). So that the secret API > key (that allows to delete gems, manage owners, etc...) still remains secret > and in a single place. > I can give a hand if you allow me :)
On Mon, Mar 1, 2010 at 8:56 AM, Mikel Lindsaar <raasd...@gmail.com> wrote: > On Mon, Mar 1, 2010 at 1:42 AM, Franck Verrot <linuxsh...@gmail.com> wrote: >> So after reviewing your patch, this means: >> 1. Prefer SHA1 to MD5. >> 2. Implement the secret key per hooks (and not user). So that the secret API >> key (that allows to delete gems, manage owners, etc...) still remains secret >> and in a single place. >> I can give a hand if you allow me :)
OK, on further review, two things.
To do #2 we would have to release it as part of API v2 or v3, that is a bit of work and requires an implementation change for all current users.
But #1 we could do, if we follow the pattern I have already done, but use SHA instead of MD5, should be an easy change, then at least we are signing post hooks going down to the consumer.
I will change this to SHA1 today and change the payload signing algorithm to include the values.
This commit changes the MD5 hash to a SHA1, it also takes all the values in the payload, including development and runtime dependencies and turns it into a string, and then signs that.
Now one problem here, as the decoding has to happen at the other end, and we don't know what version of ruby they are running or how the other end decides to decode the payload (in what order) I can't just stringify the payload hash, so I have to do it manually. I have done this as concisely as possible that can be repeated regardless of the receiving party's ruby version, but if anyone else has a simpler way to do this, please let me know, the offending (?) code is at:
> This commit changes the MD5 hash to a SHA1, it also takes all the > values in the payload, including development and runtime dependencies > and turns it into a string, and then signs that.
> Now one problem here, as the decoding has to happen at the other end, > and we don't know what version of ruby they are running or how the > other end decides to decode the payload (in what order) I can't just > stringify the payload hash, so I have to do it manually. I have done > this as concisely as possible that can be repeated regardless of the > receiving party's ruby version, but if anyone else has a simpler way > to do this, please let me know, the offending (?) code is at:
So this is actually really neat, I was going to suggest that we just put the user's API key into the hash but I think this is better. I wonder why this is the case though...
> > This commit changes the MD5 hash to a SHA1, it also takes all the > > values in the payload, including development and runtime dependencies > > and turns it into a string, and then signs that.
> > Now one problem here, as the decoding has to happen at the other end, > > and we don't know what version of ruby they are running or how the > > other end decides to decode the payload (in what order) I can't just > > stringify the payload hash, so I have to do it manually. I have done > > this as concisely as possible that can be repeated regardless of the > > receiving party's ruby version, but if anyone else has a simpler way > > to do this, please let me know, the offending (?) code is at:
On Tue, Mar 2, 2010 at 10:59 AM, Nick Quaranto <n...@quaran.to> wrote: > So this is actually really neat, I was going to suggest that we just put the > user's API key into the hash but I think this is better. I wonder why this > is the case though... > signature = > Digest::SHA1.hexdigest("#{payload_string}#{development}#{runtime}#{user.api _key}") > on http://github.com/mikel/gemcutter/blob/master/app/models/web_hook.rb#...This > seems a little..much. Can we just do: > hexdigest(#{version.full_name}#{user.api_key}) > I guess I'm just wondering how hard it will be to reproduce the SHA as is > for the implementor.
Yeah, I know what you mean.
My initial idea on the signature was to sign the payload, for integrity, like a session hash. But looking at it, we really just need to know that the sender is authenticated, we just have to make sure each SHA1 is unique, so perhaps we do:
That would probably be enough. Then at least you know it is coming from Rubygems for THAT delivered gem, and not just a copied key from some other gem.
Actually, I like that, it is a lot more simple, and I just pushed the changes:
On Tue, Mar 2, 2010 at 12:50 PM, Mikel Lindsaar <raasd...@gmail.com> wrote: >> I guess I'm just wondering how hard it will be to reproduce the SHA as is >> for the implementor.
One more thing, where are the docs kept? Once this is all ok, I'll update the API docs to show how to verify the incoming payload.
I really don't think tossing in the downloads will be helpful for the hash. #{name}#{version}#{api_key} should be more than enough. This is pretty minor though and I can modify it when I merge it in if you don't get to it :)
On Thu, Mar 4, 2010 at 3:06 AM, Mikel Lindsaar <raasd...@gmail.com> wrote: > Are there any further opinions regarding accepting my most recent > version (1) of the SHA1 signature to the existing API v1 and future > APIs?
On Thu, Mar 11, 2010 at 12:19 PM, Nick Quaranto <n...@quaran.to> wrote: > Looking at this again... > http://github.com/mikel/gemcutter/commit/5594af29e6a1246566b0456b9491... > I really don't think tossing in the downloads will be helpful for the hash. > #{name}#{version}#{api_key} should be more than enough. This is pretty minor > though and I can modify it when I merge it in if you don't get to it :) > -Nick
No problem, the only reason I put it in there was for some more 'randomness' in the hash... but this probably doesn't matter. If you have people trying to hack your gem cutter api key you probably have bigger problems :)