maintaining secure sessions between web app and data server

10 views
Skip to first unread message

Larry Siden

unread,
Mar 9, 2010, 4:12:27 PM3/9/10
to sprou...@googlegroups.com, rubyd...@googlegroups.com, a2...@googlegroups.com
Here is another issue I alluded to in a previous post (on groups.google.com/group/sproutcore).  Again, I don't know whether this is the ideal forum to post it one, but I'm thinking that everyone here who does web-apps should encounter it at some time or another, so I'm going to go ahead.  Besides, I can't find a more suitable group to post it on.

I'm developing a web app that asks users to submit personal financial information that they can return to later, but guarantees their anonymity.  Moreover, I don't want my app and data server to have to deal with passwords and e-mails and all the use cases that maintaining authentication and privacy might involve.  So I decided to use OpenId for authentication.  Before displaying the Sproutcore app, the user must first initiate authentication with the data server, which redirects him to an OpenId provider, such as Google or Yahoo, which then returns his OpenId URL (essentially, a username).  The server promptly obfuscates the OpenId URL/username with SHA-1.  This then becomes the username for my SC app data server.  So ultimately, even I would not be able to know who my users are, unless they explicitly contact me with their OpenId URL.

My next goal is to assure that the data server itself is secure from tampering.  In particular, I want to assure that:
  1. An unauthenticated user cannot access or alter any data.
  2. An authenticated user can only access and modify his own data.
So my data server creates a random session ID that it associates with the user-id in a database table.  (I could have also done this in memory, but then a session would not survive a server-restart, which might be useful, and I would need a separate thread instead of a cron job to clean-up expired sessions.)   Then it redirects the user's browser to the Sproutcore app.  

My first problem was how to get the newly minted session-id to the SC app.  I tried in vain to put it in a cookie.  But each time I examined the response with Firebug, there was no sign of this cookie in the response.  Instead, I find only the cookie 'rack.session'.  Trying to decode it myself, I got:

$ irb
ruby-1.9.1-p243 > data="BAh7AA%3D%3D%0A"
 => "BAh7AA%3D%3D%0A" 
ruby-1.9.1-p243 > data.unpack("m*")
 => ["\x04\b{\x00\r\xC3\xDC=\x00"] 
ruby-1.9.1-p243 > unpacked = data.unpack("m*")
 => ["\x04\b{\x00\r\xC3\xDC=\x00"] 
ruby-1.9.1-p243 > Marshal.load(unpacked)
TypeError: instance of IO needed
from (irb):4:in `load'
from (irb):4
from /home/lsiden/.rvm/ruby-1.9.1-p243/bin/irb:15:in `<main>'

I never figured out how to get Marshal.load it's IO instance that it wants, but I'm pretty sure that this is not the rack.session from my data server, but from the instance of sc-server that is supporting my SC app while it is still in development.  I'm guessing, but not sure where to confirm, that Firefox (and other browsers) will ignore any cookie set from a different domain or even by a different port on the same domain.

So I'm left with the much uglier method of attaching the session id to my sproutcore app URL like so:

http://<my.domain.com/my-app?session_id=13245...

This works, but am I the only one who thinks this "smells" a little funny?

I should have mentioned sooner that this entire transaction will take place over HTTPS once it is deployed.  So the session-id is secure and the session is safe from session-hijacking (at least as safe as HTTPS can provide).

This is my first experience building this kind of app, so I want to check with others whether I am taking the right approach or whether there might be a better way to do this.

One solution that comes to mind might be to deploy same port number to the Sproutcore app as the data server, so that the data server can plant the session cookie when it redirects to the SC app, but this has consequences.  In particular, it means that now these two apps must be deployed together, when before they could be logically deployed separately.  That, too, has a bad odor.

Larry Siden, 734-926-9614, http://umich.edu/~lsiden

The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993

Daniel Parker

unread,
Mar 9, 2010, 8:23:36 PM3/9/10
to rubyd...@googlegroups.com, sprou...@googlegroups.com, a2...@googlegroups.com
Another possible solution is to host one app "inside" the other app -- even while being on different servers -- by use of an Apache internal proxy. I've done this just recently to solve an otherwise complex problem slightly similar to yours. This means that, for example:

http://your.deployedapp.com/... -- goes to your deployment server
http://your.deployedapp.com/data/... -- goes to your data server

Meanwhile, the cookies end up being the same cookies for both apps, even though they live on entirely different servers and could be listening on different ports and different domains.

As far as I know, passing the session_id in the query string is not insecure if you are using https; but it is visible to the user, for better or for worse. This solution would allow you to pass data in the cookies themselves.

Here's an example of the Apache directives you can just put in a .htaccess file in the public directory of the deployed app, as long as "AllowOverride all" is set for that directory:
===============
    RewriteEngine on
    RewriteRule ^(data/.*)$ http://data-server.deployedapp.com/$1 [P,L]
===============

- daniel parker -

"You have granted me life and steadfast love, and your care has preserved my spirit." Job 10:12
"The LORD is my chosen portion and my cup . . . indeed, I have a beautiful inheritance." Psalm 16:5-6
"Give what you can ... take nothing back!"


--
You received this message because you are subscribed to the Google
Groups "Detroit.rb" group.
To post to this group, send email to rubyd...@googlegroups.com
To unsubscribe from this group, send email to
rubydetroit...@googlegroups.com

Larry Siden

unread,
Mar 9, 2010, 9:01:52 PM3/9/10
to rubyd...@googlegroups.com, sprou...@googlegroups.com, Wischmeyer, Marcus
Just wanted to be sure I'm sharing this response with all groups this was originally posted to. 


Larry Siden, 734-926-9614, http://umich.edu/~lsiden

The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993


On Tue, Mar 9, 2010 at 8:58 PM, Larry Siden <lsi...@gmail.com> wrote:
I appreciate your responding to this.  I think there's a little misunderstanding, due to the unique nature of Sproutcore apps. 

In a "traditional" web-app (think of any Java/PHP/Rails web-app that sends requests and loads new pages to/from a web app server), the user authenticates, then obtains some credential from the server that it uses in subsequent requests.  This applies regardless of the actual mechanism used.  If OpenId is used for authentication, then there is an additional user interaction with an OpenId service that provides the user's credential directly to the app server.  The rest of the transactions are the same.

Think of a SC app as a web app built entirely with one monolithic page that talks to a separate web service that may or may not be on the same domain.  The key here is that the SC app only gets to load once and then it must be up and ready to send requests to the data server with whatever credentials that service requires.  

If the data-server (not necessarily the same domain or server that serves the SC page) maintains its own database of user credentials (usernames and passwords), then no problem.  The SC app just has to display an appropriate form for username and password, send them to the dataserver and receive the credential (if username/password pass muster).

But if I want my app to use OpenId to make it more secure (no password file or table with e-mails that can be hacked), then there has to be a separate user interaction with the OpenId server.  If the SC app is already up, this means that this must occur in a separate window.  This already creates a complication for a mass audience since all the popular browsers now regulate pop-ups much more actively, and this might already scare some people off.  Remember - this app is targeted to seniors and there care-givers - an already techno-phobic audience!  

That still leaves me needing to find a way to get the logon credential back to the SC app in the already open window, probably through a callback (openener.user_session_id = ...).  I'm no security expert but I can already see this raising hackles among web security illuminati and possibly being blocked by browsers that are suspicion of XSS attacks.

So the only way to avoid all this possible mayhem is to first do the OpenId authentication, then load the SC app into the browser with the necessary credential (session_id).

Whew!  If I can explain all that I probably deserve a teaching position!  If you can understand it, you probably deserve an advanced degree!  :)

Thanks again, for your interest and response.

Larry Siden, 734-926-9614, http://umich.edu/~lsiden

The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993


On Tue, Mar 9, 2010 at 7:32 PM, Matthew Wilson <m...@hypomodern.com> wrote:
Larry,

Perhaps I am thinking too far outside what you can control, but why are your users needing to authenticate with both the database and the application server? Should they not simply authenticate to the application server and handle authentication and authorization between the application and the database simply as a matter between the two--where you'd have all kinds of options since you aren't needing to involve the user?

That's the "traditionalist" approach, I guess: lock down requests to the database so that everything runs through the application. That way you can maintain a single authorization mechanism for all requests that touch the DB. I don't really know sproutcore, though. Maybe it doesn't have an OpenID authentication mechanism?

You could transfer the user's session via a quick three-way token exchange: the redirect from the data server includes a one-time token; the SC app fires off a separate request with the user's login and the token back to the authenticating server, which responds yea or nay. I could share some ruby code that does this is you need to. It does smell funny.

-mhw

p.s. You can convert a regular String into StringIO by:

require 'stringio'
StringIO.new("mystring")

And yeah, that's going to be the session that you've establish with the SC app.

--
You received this message because you are subscribed to the Google Groups "Ann Arbor Ruby Brigade" group.
To post to this group, send email to a2...@googlegroups.com.
To unsubscribe from this group, send email to a2rb+uns...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/a2rb?hl=en.

--
You received this message because you are subscribed to the Google Groups "Ann Arbor Ruby Brigade" group.
To post to this group, send email to a2...@googlegroups.com.
To unsubscribe from this group, send email to a2rb+uns...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/a2rb?hl=en.


Larry Siden

unread,
Mar 9, 2010, 9:03:21 PM3/9/10
to rubyd...@googlegroups.com, sprou...@googlegroups.com, a2...@googlegroups.com
Daniel, you da' man!  I like that solution.  :)


Larry Siden, 734-926-9614, http://umich.edu/~lsiden

The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993


Larry Siden

unread,
Mar 9, 2010, 9:06:53 PM3/9/10
to rubyd...@googlegroups.com, sprou...@googlegroups.com, a2...@googlegroups.com
PS - I probably won't need to use a proxy, because I'm most likely going to deploy both the SC app (a little HTML, CSS and a whole lotta Javascript!) and the data service (a little Sinatra/DataMapper/MySQL app) on the same server, but it's nice to know the proxy "trick" if I need to separate them.


Larry Siden, 734-926-9614, http://umich.edu/~lsiden

The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993


Daniel Parker

unread,
Mar 9, 2010, 9:15:19 PM3/9/10
to rubyd...@googlegroups.com
So in your case, you can probably do without actually proxying, but instead use some other clever Apache directives to get certain requests to one app and other requests to the other app, all on the same domain,
= same magic achieved, higher efficiency.


- daniel parker -

"You have granted me life and steadfast love, and your care has preserved my spirit." Job 10:12
"The LORD is my chosen portion and my cup . . . indeed, I have a beautiful inheritance." Psalm 16:5-6
"Give what you can ... take nothing back!"


Reply all
Reply to author
Forward
0 new messages