2010-08-01 22 views
6

Je veux cacher sélectivement certaines ressources basées sur une certaine forme d'authentification dans web.py, mais leur existence est révélée par 405 réponses à n'importe quelle méthode HTTP que je n'ai pas implémentée.web.py: Comment masquer sélectivement des ressources avec 404 pour n'importe quelle méthode HTTP?

Voici un exemple:

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

Lorsqu'une demande de méthode non définie est émise, la ressource est révélé:

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

je pourrais mettre en œuvre la même vérification pour les autres méthodes communes dans la spécification HTTP , mais un méchant créatif pourrait inventer leur propre:

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

Est il existe un moyen d'implémenter une méthode catch all dans une classe web.py pour n'importe quelle méthode HTTP, donc je peux m'assurer que la vérification de sécurité sera exécutée?

Ou existe-t-il une autre façon de masquer ces ressources?

Répondre

4

Eclairé par la réponse de Daniel Kluev, j'ai fini par tirer de web.application pour ajouter le support pour une méthode par défaut dans la méthode _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)) 
     ... 

instanciation:

app = application(urls, globals()) 

page Classe:

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

    def GET(self): 
     ... 

Je préfère cette solution car elle garde les classes de pages propres et permet une personnalisation plus poussée du processus de délégation en un seul endroit. Par exemple, une autre caractéristique que je voulais était transparent POST surchargé (par exemple rediriger une requête POST avec method=DELETE la méthode SUPPRIMER de la classe page.) Et il est simple d'ajouter que, là aussi:

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

vous pouvez définir une méthode dans votre classe 'secret', comme SUPPRIMER ou SHENANIGANS, comme ceci:

class secret(): 

    def DELETE(self): 
     ... 

    def SHENANIGANS(self): 
     ... 
+1

Un attaquant peut inventer quelque méthode nom qu'ils aiment. Si je commence à définir des méthodes pour chaque possibilité, je vais manquer mon délai :) –

1

Vous pouvez implémenter la méthode poignée-all-méthodes comme ceci:

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__ est mis en œuvre deux fois en raison des contrôles de web.py façon pour l'existence de la méthode:

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__ 
+0

Merci beaucoup pour cette explication claire et utile. J'ai décidé d'utiliser une méthode différente avec laquelle je me sentais plus à l'aise, mais je n'avais aucune idée par où commencer avant de lire votre réponse! –