代码之家  ›  专栏  ›  技术社区  ›  Ian Mackinnon

PY:对于任何HTTP方法,如何有选择地隐藏404资源?

  •  5
  • Ian Mackinnon  · 技术社区  · 14 年前

    我想有选择地隐藏一些基于web.py中某种形式的认证的资源,但是它们的存在是由405对任何我没有实现的HTTP方法的响应所揭示的。

    下面是一个例子:

    import web
    
    urls = (
        '/secret', 'secret',
        )
    
    app = web.application(urls, globals())
    
    class secret():
        def GET(self):
            if web.cookies().get('password') == 'secretpassword':
                return "Dastardly secret plans..."
            raise web.notfound()
    
    if __name__ == "__main__":
        app.run()
    

    当发出未定义的方法请求时,将显示资源:

    $ curl -v -X DELETE http://localhost:8080/secret
    ...
    > DELETE /secret HTTP/1.1
    ...
    < HTTP/1.1 405 Method Not Allowed
    < Content-Type: text/html
    < Allow: GET
    ...
    

    我可以对HTTP规范中的其他常见方法执行相同的检查,但是一个有创意的错误者可能会发明自己的方法:

    $ curl -v -X SHENANIGANS http://localhost:8080/secret
    ...
    > SHENANIGANS /secret HTTP/1.1
    ...
    < HTTP/1.1 405 Method Not Allowed
    < Content-Type: text/html
    < Allow: GET
    ...
    

    有没有一种方法可以在web.py类中为任何http方法实现catch all方法,这样我就可以确保安全检查将运行?

    或者有没有其他方法来隐藏这些资源?

    3 回复  |  直到 14 年前
        1
  •  3
  •   Ian Mackinnon    14 年前

    在丹尼尔·克鲁夫的回答的启发下,我最终从 web.application 在中添加对默认方法的支持 _delegate 方法:

    import types
    
    class application(web.application):
        def _delegate(self, f, fvars, args=[]):
            def handle_class(cls):
                meth = web.ctx.method
                if meth == 'HEAD' and not hasattr(cls, meth):
                    meth = 'GET'
                if not hasattr(cls, meth):
                    if hasattr(cls, '_default'):
                        tocall = getattr(cls(), '_default')
                        return tocall(*args)
                    raise web.nomethod(cls)
                tocall = getattr(cls(), meth)
                return tocall(*args)
    
            def is_class(o): return isinstance(o, (types.ClassType, type))
            ...
    

    实例化:

    app = application(urls, globals())
    

    页类:

    class secret():
        def _default(self):
            raise web.notfound()
    
        def GET(self):
            ...
    

    我更喜欢这个解决方案,因为它可以保持页面类的整洁,并在一个地方提供授权过程的进一步定制。例如,我需要的另一个特性是透明的重载post(例如,使用 method=DELETE 对于page类的delete方法),也可以在这里简单地添加:

                ...
                meth = web.ctx.method
                if meth == 'POST' and 'method' in web.input():
                    meth = web.input()['method']
                ...
    
        2
  •  1
  •   Daniel Kluev    14 年前

    您可以实现handle all methods方法,如下所示:

    class HelloType(type):
        """Metaclass is needed to fool hasattr(cls, method) check"""
        def __getattribute__(obj, name):
            try:
                return object.__getattribute__(obj, name)
            except AttributeError:
                return object.__getattribute__(obj, '_handle_unknown')        
    
    class hello(object):
        __metaclass__ = HelloType
        def GET(self, *args, **kw):
            if web.cookies().get('password') == 'secretpassword':
                return "Dastardly secret plans..."
            raise web.notfound()
    
        def _handle_unknown(self, *args, **kw):
            """This method will be called for all requests, which have no defined method"""
            raise web.notfound()
    
        def __getattribute__(obj, name):
            try:
                return object.__getattribute__(obj, name)
            except AttributeError:
                return object.__getattribute__(obj, '_handle_unknown') 
    

    __getattribute__ 由于web.py检查方法存在的方式,实现了两次:

    def _delegate(self, f, fvars, args=[]):
        def handle_class(cls):
            meth = web.ctx.method
            if meth == 'HEAD' and not hasattr(cls, meth):
                meth = 'GET'
            if not hasattr(cls, meth): # Calls type's __getattribute__
                raise web.nomethod(cls)
            tocall = getattr(cls(), meth) # Calls instance's __getattribute__
    
        3
  •  0
  •   Xie Yanbo    14 年前

    您可以在“秘密”类中定义任何方法,如删除或恶作剧,如下所示:

    class secret():
    
        def DELETE(self):
           ...
    
        def SHENANIGANS(self):
           ...