web.py:如何有选择地隐藏任何 HTTP 方法的 404 资源?

发布于 2024-09-12 12:20:15 字数 1017 浏览 1 评论 0原文

我想根据 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
...

是否有一种方法可以实现 catch web.py 类中的所有方法适用于任何 HTTP 方法,这样我就可以确保安全检查将运行?

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

I want to selectively hide some resources based on some form of authentication in web.py, but their existence is revealed by 405 responses to any HTTP method that I haven't implemented.

Here's an example:

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()

When an undefined method request is issued, the resource is revealed:

$ 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
...

I could implement the same check for the other common methods in the HTTP specification, but a creative miscreant might invent their own:

$ 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
...

Is there a way to implement a catch all method in a web.py class for any HTTP method, so I can ensure the security check will be run?

Or is there an alternative way to hide these resources?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梓梦 2024-09-19 12:20:16

受到 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']
            ...

Enlightened by Daniel Kluev's answer, I ended up deriving from web.application to add support for a default method in the _delegate method:

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))
        ...

Instantiation:

app = application(urls, globals())

Page class:

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

    def GET(self):
        ...

I prefer this solution because it keeps the page classes clean and affords further customisation of the delegation process in a single place. For example, another feature I wanted was transparent overloaded POST (eg. redirecting a POST request with method=DELETE to the DELETE method of the page class) and it's simple to add that here too:

            ...
            meth = web.ctx.method
            if meth == 'POST' and 'method' in web.input():
                meth = web.input()['method']
            ...
短暂陪伴 2024-09-19 12:20:16

您可以像这样实现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') 

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

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__

You can implement handle-all-methods method like this:

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__ is implemented twice due to the way web.py checks for method existence:

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__
我恋#小黄人 2024-09-19 12:20:16

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

class secret():

    def DELETE(self):
       ...

    def SHENANIGANS(self):
       ...

you can define any method in your 'secret' class, such as DELETE or SHENANIGANS, like this:

class secret():

    def DELETE(self):
       ...

    def SHENANIGANS(self):
       ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文