【问题标题】:web.py: How to selectively hide resources with 404s for any HTTP method?web.py:如何选择性地隐藏任何 HTTP 方法的 404 资源?
【发布时间】:2010-08-01 14:45:49
【问题描述】:

我想根据 web.py 中的某种形式的身份验证选择性地隐藏一些资源,但它们的存在是通过对我尚未实现的任何 HTTP 方法的 405 响应来揭示的。

这是一个例子:

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 方法,这样我可以确保运行安全检查?

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

【问题讨论】:

    标签: python http web.py


    【解决方案1】:

    受 Daniel Kluev 的回答启发,我最终从 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 的 POST 请求重定向到页面类的 DELETE 方法),在这里添加也很简单:

                ...
                meth = web.ctx.method
                if meth == 'POST' and 'method' in web.input():
                    meth = web.input()['method']
                ...
    

    【讨论】:

      【解决方案2】:

      你可以像这样实现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】:

      您可以在您的“秘密”类中定义任何方法,例如 DELETE 或 SHENANIGANS,如下所示:

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

      【讨论】:

      • 攻击者可以发明他们喜欢的任何方法名称。如果我开始为每一种可能性定义方法,我会错过最后期限:)
      猜你喜欢
      • 2020-05-30
      • 1970-01-01
      • 2014-12-30
      • 1970-01-01
      • 2014-01-17
      • 1970-01-01
      • 2019-03-14
      • 1970-01-01
      • 2021-03-20
      相关资源
      最近更新 更多