Edgewall Software

Ticket #4049: trac-csrf.patch

File trac-csrf.patch, 3.0 KB (added by jonas, 2 years ago)

A proof of concept patch for trunk

  • trac/env.py

     
    373373        return self._abs_href 
    374374    abs_href = property(_get_abs_href, 'The application URL') 
    375375 
     376    def _get_form_secret(self): 
     377        return str(os.stat(os.path.join(self.path, 'VERSION')).st_ctime) 
     378    form_secret = property(_get_form_secret) 
    376379 
     380 
    377381class EnvironmentSetup(Component): 
    378382    implements(IEnvironmentSetupParticipant) 
    379383 
  • trac/web/chrome.py

     
    432432            'href': req and req.href, 
    433433            'perm': req and req.perm, 
    434434            'authname': req and req.authname or '<trac>', 
     435            'form_token': req.form_token, 
    435436 
    436437            # Date/time formatting 
    437438            'format_datetime': partial(format_datetime, tzinfo=tzinfo), 
  • trac/web/main.py

     
    1616# Author: Christopher Lenz <cmlenz@gmx.de> 
    1717#         Matthew Good <trac@matt-good.net> 
    1818 
     19import sha 
    1920import locale 
    2021import os 
    2122import sys 
     
    173174            'hdf': self._get_hdf, 
    174175            'perm': self._get_perm, 
    175176            'session': self._get_session, 
    176             'tz': self._get_timezone 
     177            'tz': self._get_timezone, 
     178            'form_token': self._get_form_token 
    177179        }) 
    178180 
    179181        # Select the component that should handle the request 
     
    193195        req.callbacks['chrome'] = partial(chrome.prepare_request, 
    194196                                          handler=chosen_handler) 
    195197 
     198        if (req.method == 'POST' and 
     199            req.args.get('__form_token__') != req.form_token): 
     200            raise TracError('Missing or invalid form token') 
     201 
    196202        # Process the request and render the template 
    197203        try: 
    198204            try: 
     
    251257        except: 
    252258            return localtz 
    253259 
     260    def _get_form_token(self, req): 
     261        return sha.sha(self.env.form_secret + req.authname).hexdigest() 
     262 
    254263    def _pre_process_request(self, req, chosen_handler): 
    255264        for filter_ in self.filters: 
    256265            chosen_handler = filter_.pre_process_request(req, chosen_handler) 
  • templates/layout.html

     
    8080    </div> 
    8181  </body> 
    8282 
     83  <form py:match="form[@method='post' and @protected!='true']"  
     84        method="post"  
     85        protected="true" 
     86        id="${select('@id')}" 
     87        class="${select('@class')}" 
     88        action="${select('@action')}"> 
     89    <input type="hidden" name="__form_token__" value="$form_token"/> 
     90    ${select('*|text()')} 
     91  </form> 
     92 
    8393  <xi:include href="site.html"><xi:fallback /></xi:include> 
    8494 
    8595</html>