Edgewall Software

Ticket #4049: trac-csrf.3.patch

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

A safer and cleaner version of the patch.

  • 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

     
    173173            'hdf': self._get_hdf, 
    174174            'perm': self._get_perm, 
    175175            'session': self._get_session, 
    176             'tz': self._get_timezone 
     176            'tz': self._get_timezone, 
     177            'form_token': self._get_form_token 
    177178        }) 
    178179 
    179180        # Select the component that should handle the request 
     
    193194        req.callbacks['chrome'] = partial(chrome.prepare_request, 
    194195                                          handler=chosen_handler) 
    195196 
     197        # Protect against CSRF attacks. 
     198        # We can only block against suck attacks if the user is logged in 
     199        # or if we have an incoming session cookie. 
     200        if (req.method == 'POST' and 
     201            req.args.get('__FORM_TOKEN') != req.form_token and 
     202            (req.incookie.has_key('trac_auth') or 
     203             req.incookie.has_key('trac_session'))): 
     204            raise TracError('Missing or invalid form token') 
     205 
    196206        # Process the request and render the template 
    197207        try: 
    198208            try: 
     
    251261        except: 
    252262            return localtz 
    253263 
     264    def _get_form_token(self, req): 
     265        """Used to protect against CSRF. 
     266 
     267        The 'trac_auth' cookie is a good and strong shared secret, only 
     268        known by the user it belongs to and Trac itself. 
     269 
     270        The session id is our second best option, not as reliable since 
     271        it will change on each request if the user has cookies disabled in 
     272        his/her browser. 
     273        """ 
     274        if req.incookie.has_key('trac_auth'): 
     275            return req.incookie['trac_auth'].value 
     276        else: 
     277            return req.session.sid 
     278 
    254279    def _pre_process_request(self, req, chosen_handler): 
    255280        for filter_ in self.filters: 
    256281            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 @avoidloop!='true']"  
     84        avoidloop="true" py:attrs="select('@*')"> 
     85    <input type="hidden" name="__FORM_TOKEN" value="$form_token"/> 
     86    ${select('*|text()')} 
     87  </form> 
     88 
    8389  <xi:include href="site.html"><xi:fallback /></xi:include> 
    8490 
    8591</html>