Security problem with LOAD()

34 views
Skip to first unread message

pbreit

unread,
May 13, 2011, 7:40:15 PM5/13/11
to web...@googlegroups.com
I just ran into a security problem where a component is revealing a whole auth_user record!


What is the preferred way to avoid this? I could specify individual fields in my select(). There was a recommendation to decorate the component with @auth.requires(request.cid). Would that close up the loophole? Any other solutions?

pbreit

unread,
May 13, 2011, 7:53:02 PM5/13/11
to web...@googlegroups.com
It looks like one solution is to get rid of the "generic.load" file. And I'm thinking that I don't want any of the "generic.*" files in production.

pbreit

unread,
May 13, 2011, 8:47:40 PM5/13/11
to web...@googlegroups.com
I closed up the hole so you won't see the problem anymore. But I feel like a need to rigorously review further potential problems.

Anthony

unread,
May 13, 2011, 9:32:43 PM5/13/11
to web...@googlegroups.com
Since we can no longer see the problem, can you explain it in more detail?

pbreit

unread,
May 13, 2011, 9:47:41 PM5/13/11
to web...@googlegroups.com
If you access a component without the ".load" extension, it automatically uses the "generic.load" view which is a BEAUTIFY of all the returned data. So since my data included an auth_user record, all the auth_user fields were displayed in the browser window, including the password (encrypted, but still!).

Are there any other situations where manipulating the extension or URL can lead to data disclosure like this?

Is it a best practice to lock down queries by only selecting fields that you need? I guess ordinarily it is suggested to avoid "select *" so maybe that's what I need to do.

Anthony

unread,
May 13, 2011, 11:47:08 PM5/13/11
to web...@googlegroups.com
On Friday, May 13, 2011 9:47:41 PM UTC-4, pbreit wrote:
If you access a component without the ".load" extension, it automatically uses the "generic.load" view which is a BEAUTIFY of all the returned data. So since my data included an auth_user record, all the auth_user fields were displayed in the browser window, including the password (encrypted, but still!).
 
Are you talking about calling the component via a regular URL (i.e., not via the LOAD function) without the .load extension? In that case, web2py should assume a .html extension, and assuming there is no func.html view, it will use generic.html (not generic.load). The only case in which it should use generic.load is if you explicitly call it with the .load extension and there is no func.load view.
 
In any case, you still have the same problem with generic.html, which also does a BEAUTIFY (and probably a similar problem with all the generic views). This is a good observation -- the generic views can be dangerous if your function returns more data than you really want to expose. All a user has to do is call your function with any extension for which you have not defined an explicit view, and they'll get the generic view, which may show more than you want. I've seen some people use "return locals()" instead of returning a dict with explicit values, which may be dangerous if locals() includes some other variables that you don't want to expose to the public. It may be wise to return in the dict only objects that you truly want exposed by the function.
 
Anthony
 

pbreit

unread,
May 14, 2011, 1:04:01 AM5/14/11
to web...@googlegroups.com
Yeah, it looks like it is the generic.html file. Probably this loophole should be closed by default. Perhaps it could check if it's on localhost or something.

I was just returning someone's first name but the record included all auth_user fields.

pbreit

unread,
May 15, 2011, 1:13:10 AM5/15/11
to web...@googlegroups.com
I'm going to try this out to reduce the risk of a security breach:

in a model:

def is_localhost():
    import socket

    http_host = request.env.http_host.split(':')[0]
    try:
        hosts = (http_host, socket.gethostname(),
                 socket.gethostbyname(http_host),
                 '::1','127.0.0.1','::ffff:127.0.0.1')
    except:
        hosts = (http_host,)

    if request.env.remote_addr in hosts:
        return True

in generic.xxx:

{{if is_localhost():}}
    <!-- content -->
{{else:}}
    Not allowed
{{pass}}


Reply all
Reply to author
Forward
0 new messages