Skip to content
This repository

Security issue. Exposing all model to client #47

Closed
rma4ok opened this Issue April 25, 2012 · 16 comments

9 participants

Kiryl Yermakou Nate Smith Zach Smith Juzer Ali David Rees Andres Riofrio daslicht D. Allen Morrigan SLaks
Kiryl Yermakou
Collaborator

Problem

1

Let's look at chat example

2

If I set breakpoint on line 45 in browser (I mean equal line in compiled js)

42        ## CONTROLLER FUNCTIONS ##
43
44        ready (model) ->
45          months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
46          displayTime = (time) ->

3

I can do window.model = model in console

And continue script execution

4

In console

for (i in model.get('users'))
  model.set('users.'+i+'.name', 'lolz')

5

Name of every user is lolz

P.S.

Even in production environment, when js is uglified. I can do code reformat in chrome and put a breakpoint on function that has closure for model variable. And make that function happen. Like

c.postMessage = function() {
  return a.push("_room.messages", {userId: a.get("_session.userId"),comment: a.get("_newComment"),time: +(new Date)}), a.set("_newComment", "")
}

Where a is the model

I catch that breakpoint when I post a new message

Zach Smith

This is something being actively developed. There will be a way to authorize model paths very soon, which should allow you to prevent this type of thing.

Kiryl Yermakou
Collaborator

Guys!
I am really in love with derby because of transparent server-client rendering logic. But I am experiencing problems with architecting my application and having many security issues. And they all related to racer model representation. I understand that derby was created as upper layer for racer. But derbi is a really powerful framework for rendering, not for handling backend logic yet. As example you can't populate chat messages with user names on data managing level. If u have 1,000,000 users, u don't wanna do
model.fetch('users',....) and transfer it all on client, just to populate 10 chat messages with usernames through a template.

You don't have server-client logic separation. To be exact, you have some client only logic, but don't have server-only.
No web framework can be designed without server-private logic. And chat written on derby is a clear example that you can't write any application with user-private data with derby as it exists today. Because I can hack and change any user name, picture or message.

Please, let's consider other problems that I describe here.

1

I can't have any private things in app code. For example FB app token and secret. It all gets exposed to client which I don't want.
The temporary solution
I am putting lots of logic into racer middleware.

Solution
Every app piece should be able to have 2 files

app.js

// Rendering Routes
app.get ( pattern, callback(page, model, params, next) )

..........

// Server side listeners
ready (model) ->
  model.on 'set', 'user.email', (path, value, e) ->
    showEmailWarning() unless isValidEmail(value)

app.server.js

app.get ( pattern, callback(model, params, next){

  model.on 'set', 'user.email', (path, value, e) ->
    e.preventDefault() unless isValidEmail(value)
} )

2

Model needs to be able to store server only variables
Like model.set '_date', Today() is client only

Solution
We should have model.set '_session.$_datingWebsiteAccessToken', token as server only property.
And that code should be running on server only

3

I can run on client side
model.set('sessions',{all: 'loggedOut' })
or
model.subscribe('whatever', messUpAllDatabaseInRealTime)```

My temporary solution
I won't use subscribe at all. Even if somebody starts to hack around - normal users won't see it in real time.
And for every route I use model.fetch to pull fresh data from my api (read more below).

Solution
There is should be "grand access" functionality. That runs on server only.

4

If I have in route
model.on('set',....)
And model.set happens on client - the listener is never firing on server.

Solution
Event emitter should fire both on client and server.

Conclusion

I use derby without any realtime cross-user racer functionality. And I use custom racer-db-driver that talks to our python written api.

Every user gets unhackable data like "#{hashedUserID}.updates.recent" witch is "updates/recent" endpoint of our api. And make reference "_updates" right away.

In this case I can even use subscribe to safely manage realtime data for one user if he has opened several tabs with the website.

Zach Smith
Kiryl Yermakou
Collaborator

Yeah, you are right about #4.

Please let me know what do you think about my updated conclusion

Kiryl Yermakou
Collaborator

#1 Also it needs ability to do module = require 'module' that won't go through browserify to client.

Zach Smith
Juzer Ali

How about nowjs style authorization? Where we have groups and each model path belongs to a group.

David Rees

A little for insight on what they are thinking for AuthZ:

On 5/31/2012 3:08 PM, Nate Smith wrote:

Jae is correct that we don't have a solution for auth yet. We have an approach in mind, but the implementation is still forthcoming.
Unlike Meteor, you will not have to specify explicit publish functions in Derby. You will use the same dynamic subscriptions and methods from the app code. On top of that, you will be able to write server only code that acts as a gatekeeper before any requested gets, queries, or sets are allowed to access the database or the PubSub layer. Because incoming updates are coming via a WebSockets connection and not an HTTP request, we will take care of associating incoming updates with a session that can be used to perform authorization.
The necessary context and the specifics of the incoming get or set will be passed to a handler that can decide to allow or reject the incoming access or mutation.
Guarding gets and sets to a specific model path (like setting user.5.name to 'John') is relatively straightforward, but setting up auth for queries (like get the top 5 users sorted by high score) is more difficult. Queries are likely to have parameters that are a little different for every request but that have a pattern which should be matched against.
Once you turn on auth, the default will mostly likely be to simply block everything and then you will write handlers that allow gets or sets based on path patterns (like users.*.name) and queries with some ability to specify which parameters are dynamic.

  • Nate
Nate Smith
Owner
nateps commented July 16, 2012

First version of access control is now in. Needs more documentation and examples, but here is readme: https://github.com/codeparty/racer/blob/master/src/accessControl/README.md

Nate Smith nateps closed this July 16, 2012
daslicht

the new link is dead again :)

D. Allen Morrigan

Any new movement here? I am moving away from Meteor and need a solid auth solution.

D. Allen Morrigan

Are there any examples as to how to use this? The Meteor accounts-password package exposes a users collection. In some of the examples for Derby I see reference to users only as stored sessions. No passwords. They are only used to differentiate one browser session from another. racer-access seems to be akin to the Roles package from Meteor.

So perhaps my question should have been is there a user authentication package and some sort of authorization package available?

I apologize for the Meteor references, but it's the experiences I have to draw from for node based frameworks.

SLaks
Collaborator

You're asking about authentication.

Derby does not include an authentication system.

Use https://github.com/lefnire/derby-auth (0.5 branch)

daslicht

the examples do not work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.