Sharing web sessions between Rails and Go

979 views
Skip to first unread message

Matt Aimonetti

unread,
Nov 28, 2013, 1:23:25 AM11/28/13
to golan...@googlegroups.com
This is just a quick note to let you know that I started open sourcing the work I've been doing to share a web session between a Ruby on Rails app and a Golang app.
The typical use case is that you start implementing a fast and concurrent API in Go and you would like users to make calls from JS but you don't have a safe way to authenticate users once they hit the Go public web API.

I will probably write a blog post to explain things more in details for those who care, but in a nutshell:

* Rails serializes permanent/signed messages using Ruby's Marshal serialization which isn't safe (can execute any code) and isn't available cross languages.
* I have a Rails "monkey patch" to change the serialization to JSON: https://gist.github.com/mattetti/7624413  (The Rails team is considering changing that next release)
* I have ported Rails' crypto to Go so if you have access to the secrets/keys, you can read/write encrypted/signed data: http://godoc.org/github.com/mattetti/goRailsYourself/crypto#example-MessageEncryptor-DecryptAndVerify

Using this two pieces of code, you can now safely share web sessions, signed/encoded cookies and messages.

Note that https://github.com/mattetti/goRailsYourself is a repo I started to collect useful code when you have Rails and Go apps running in the same environment. 
Contribution very much appreciated.

Thanks,

- Matt

ni...@insightmethods.com

unread,
Nov 28, 2013, 2:05:32 AM11/28/13
to golan...@googlegroups.com
Interesting!

I needed similar thing, what I ended up doing is when Go service receive direct request, it will forward cookies along with action about to be performed to rails, and rails will authenticate and authorize send result to Go service.

Matt Aimonetti

unread,
Nov 28, 2013, 2:14:36 AM11/28/13
to ni...@insightmethods.com, golang-nuts
Niket, I chose to avoid doing the auth forwarding for a few reasons, namely:
* I want my API response time to be really fast and adding an http call to a Rails app is too slow (I might as well not use Go).
* I don't want more load on my Rails app.
* I don't want to have to write private APIs in the Rails app to for the authentication/authorization.
* I try to care as much as possible about security.

It was a pain to understand Rails crypto and process (and have to patch Rails) but honestly, if you are using a Rails app, you really really have to consider switching the serialization. If you do, the only reason you can't share the session is because you need to crypto code to decrypt/verify the session in Goland. Turns out, I now have it done, so it should be trivial for any any Rails developer to rewrite big chunks of the architecture in Go without having to worry about authentication ;)

Hopefully, I convinced you to maybe reconsider forwarding the authentication call.

- Matt



On Wed, Nov 27, 2013 at 11:05 PM, <ni...@insightmethods.com> wrote:
Interesting!

I needed similar thing, what I ended up doing is when Go service receive direct request, it will forward cookies along with action about to be performed to rails, and rails will authenticate and authorize send result to Go service.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/_nUQ1brlPBY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Niket Patel

unread,
Nov 28, 2013, 3:47:19 AM11/28/13
to Matt Aimonetti, golang-nuts
Hi Matt,

First three points you mentioned doesn't concern much for my use, but I'm curious about forth point as I'm not sure. To me it seem like good separation of concern. Rails do auth and Go do heavy lifting like PDF or Zip creation and delivering it with high efficiency - I'm not worried about few milliseconds but more about ruby memory consumption for such things.

But, if I'm missing really critical thing about security. I would like to know. 

And sure I'm going evaluate your solution for our problem ASAP. Our approach of combining Go with rails is very similar to yours.

Thanks.

As side note, take a look at https://github.com/nexneo/easyreq Its inspired by one of your blog post. I tried to make multipart request easy. (although it doesn't do streaming so useless for large files but my main use case was mailgun)


Matt Aimonetti

unread,
Nov 28, 2013, 12:26:45 PM11/28/13
to Niket Patel, golang-nuts
My concern about security is simple and you might already have addressed it: how do you keep the Go <=> Rails interaction safe? 

I might be a bit paranoid, but having a safe interface between the two isn't easy and it usually means not having a public facing API, using encryption, no private data logging etc...

If performance isn't an issue for you, why not running your Go app in the local network (not publicly available) and forward the API requests from Rails to Go after authentication? You can use something like Nginx X-Accel-Redirect to get the Go app to actual send the response back to the user while the app is still not publicly available (a trick I used when I was using CouchDb and was returning some responses from couch directly after auth).

The less surface you expose, the safer your code is, that said I'm planning on moving all my authentication logic into the Go API and make is available via a thrift || protobuf || jsonrpc API. But even when doing that, I will keep the session deciphering available on each platform to avoid unneeded back and forth.

- Matt

Rodrigo Kochenburger

unread,
Nov 28, 2013, 1:17:14 PM11/28/13
to Matt Aimonetti, golan...@googlegroups.com
Matt, I was considering doing the exact same thing. Thanks for sharing :-)

off topic: It actually came to me as surprise that Rails was using Marshal instead of JSON, good to see they will change that.



- RK


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Niket Patel

unread,
Nov 28, 2013, 2:19:15 PM11/28/13
to Matt Aimonetti, golang-nuts
Hi Matt,

Let me explain bit more. 

Most of Go based services are not publicly available like you said just backend for rails app and available on local network only. Though one service where I'm doing following is for pdf rendering, it works as follow.

Browser directly post request to Go app (nginx route it to Go app if path starts with "/pdf" for example, otherwise to Rails)

Ex, /pdf?path=/user/need/this/page-as-pdf 

Then Go app will just proxy it to Rails app by making regular requests to given path(s) (one or multiple) Rails respond with html to Go and Go covert and combine pdf and respond to browser.

This limited to GET and I think this as just a proxy with behavior. So, what you think? I don't know how to make it work without proxy approach.

I admit that I was thinking to expand this into other services by putting more Go services in front and asking Rails to do Auth, but you have convinced me to not to take that route. 

Thanks

Matt Aimonetti

unread,
Nov 28, 2013, 3:15:50 PM11/28/13
to Niket Patel, golang-nuts
Seems fine to me.

Nicolas Grilly

unread,
Nov 28, 2013, 4:37:26 PM11/28/13
to golan...@googlegroups.com
We did exactly the same at my company for our Python application. We have ported our secure cookie implementation from Python to Go. It's really nice to enjoy the raw power of Go to write our web services, and sharing web sessions without any communication between the Python and Go servers.

Thanks for sharing your experience about this! It's comforting to know we're not alone :)

-- Nicolas Grilly

Matt Silverlock

unread,
Nov 28, 2013, 6:37:03 PM11/28/13
to golan...@googlegroups.com, Matt Aimonetti
> off topic: It actually came to me as surprise that Rails was using Marshal instead of JSON, good to see they will change that.

This is likely for two reasons: a) speed and b) storage overhead (I assume that Marshal has less encoding overhead than JSON does, but correct me otherwise). HTTP Cookies have a 4K limit in most browsers, and that's a very easy limit to run up against. If you're using server-side sessions you don't have that problem, but a lot of people use cookie-based stores.

Go's gorilla/sessions similarly uses Go's gob encoding rather than JSON.

Matt Aimonetti

unread,
Nov 29, 2013, 3:45:44 PM11/29/13
to Matt Silverlock, golang-nuts
@Nicolas I would love to see your code, especially if you used Django's web session or another popular Python lib.
@Matt true, but Marshal isn't safe since you can store any Ruby objects and they get deserialized allowing someone with the secret key to execute code server side.

I updated the package documentation (and setup travis-ci), this should explain a bit more how Rails does its crypto using derived keys. Of course this package can be used on its own without a Rails app:


Edward Middleton

unread,
Nov 30, 2013, 5:23:20 AM11/30/13
to Matt Silverlock, golan...@googlegroups.com, Matt Aimonetti
Why not use msgpack http://msgpack.org/

Edward

On 11/29/2013 08:37 AM, Matt Silverlock wrote:
> > off topic: It actually came to me as surprise that Rails was using
> Marshal instead of JSON, good to see they will change that.
>
> This is likely for two reasons: a) speed and b) storage overhead (I
> assume that Marshal has less encoding overhead than JSON does, but
> correct me otherwise). HTTP Cookies have a 4K limit in most browsers,
> and that's a very easy limit to run up against. If you're using
> server-side sessions you don't have that problem, but a /lot/ of people
> use cookie-based stores.
>
> Go's gorilla/sessions similarly uses Go's gob encoding rather than JSON.
>
>
> On Friday, November 29, 2013 2:17:14 AM UTC+8, Rodrigo Kochenburger wrote:
>
> Matt, I was considering doing the exact same thing. Thanks for
> sharing :-)
>
> off topic: It actually came to me as surprise that Rails was using
> Marshal instead of JSON, good to see they will change that.
>
>
>
> - RK
>

Matt Aimonetti

unread,
Nov 30, 2013, 12:47:43 PM11/30/13
to Edward Middleton, Matt Silverlock, golang-nuts

My "crypto" package supports defining a msg serializer using message pack. You just need a struct implementing the interface and everything will work fine.

I already added a few serializers, PR welcome (or the serializer could live on its own since it has external dependencies).

- Matt

jno...@gmail.com

unread,
Nov 30, 2013, 11:27:33 PM11/30/13
to golan...@googlegroups.com, Edward Middleton, Matt Silverlock
Matt,

We did nearly this same thing at UserVoice. I benchmarked Yajl vs Marshal.dump -- Marshal is faster, but not by much (within 20% of a really small number).

Nicolas Grilly

unread,
Dec 4, 2013, 5:01:18 AM12/4/13
to golan...@googlegroups.com, Matt Silverlock
On Friday, November 29, 2013 9:45:44 PM UTC+1, Matt Aimonetti wrote:
@Nicolas I would love to see your code, especially if you used Django's web session or another popular Python lib.

Here is the Python and Go code:

As you will see, the code was written only for an internal usage and is not ready for public consumption: documentation is missing in some places, tests are missing in the Python version, code is not general enough, etc.

The Python version is designed to work with CherryPy, a light-weight web framework which was popular a few years ago, but is less popular now. Thus, the crypto implementation is very similar to the one in Django, which means the Go code can be easily adapted by people wanting to share sessions with Django.

I'm interested in any useful comment about this stuff :)

Nicolas
Reply all
Reply to author
Forward
0 new messages