Ticket #4049: trac-0.10-csrf.patch
| File trac-0.10-csrf.patch, 4.9 KB (added by jonas, 2 years ago) |
|---|
-
trac/web/api.py
350 350 content_type = 'text/plain' 351 351 data = str(self.hdf) 352 352 else: 353 data = self.hdf.render(template )353 data = self.hdf.render(template, self.form_token) 354 354 355 355 self.send_response(status) 356 356 self.send_header('Cache-control', 'must-revalidate') -
trac/web/clearsilver.py
14 14 # 15 15 # Author: Christopher Lenz <cmlenz@gmx.de> 16 16 17 from HTMLParser import HTMLParser 18 17 19 from trac.core import TracError 18 from trac.util.html import Markup, Fragment, escape 20 from trac.util.html import Markup, Fragment, escape, _EMPTY_TAGS 19 21 from trac.util.text import to_unicode 20 22 21 23 … … 274 276 cs.parseStr(string) 275 277 return cs 276 278 277 def render(self, template ):279 def render(self, template, form_token=''): 278 280 """Render the HDF using the given template. 279 281 280 282 The template parameter can be either an already parse neo_cs.CS … … 286 288 import neo_cs 287 289 template = neo_cs.CS(self.hdf) 288 290 template.parseFile(filename) 289 return template.render()290 291 292 from cStringIO import StringIO 293 out = StringIO() 294 injector = FormTokenInjector(form_token+'x', out) 295 injector.feed(template.render()) 296 return out.getvalue() 291 297 298 299 class FormTokenInjector(HTMLParser): 300 """Identify and protect forms from CSRF attacks 301 302 This filter works by adding a input type=hidden field to POST forms. 303 """ 304 def __init__(self, form_token, out): 305 HTMLParser.__init__(self) 306 self.out = out 307 self.token = form_token 308 309 def handle_starttag(self, tag, attrs): 310 strs = [] 311 attr_map = {} 312 for name, val in attrs: 313 attr_map[name] = val 314 strs.append('%s="%s"' % (name, val)) 315 chunk = '<' + tag 316 if strs: 317 chunk += ' ' + ' '.join(strs) 318 if tag in _EMPTY_TAGS: 319 chunk += '/' 320 chunk += '>' 321 self.out.write(chunk) 322 if tag == 'form' and attr_map.get('method', '').lower() == 'post': 323 self.out.write('<input type="hidden" name="__FORM_TOKEN"' 324 ' value="%s"/>' % self.token) 325 326 def handle_entityref(self, name): 327 self.out.write('&%s;' % name) 328 329 def handle_data(self, data): 330 self.out.write(escape(data, quotes=False)) 331 332 def handle_endtag(self, tag): 333 if tag not in _EMPTY_TAGS: 334 self.out.write('</' + tag + '>') 335 336 292 337 if __name__ == '__main__': 293 338 import doctest, sys 294 339 doctest.testmod(sys.modules[__name__]) -
trac/web/main.py
189 189 req.authname = self.authenticate(req) 190 190 req.perm = PermissionCache(self.env, req.authname) 191 191 req.session = Session(self.env, req) 192 req.form_token = self._get_form_token(req) 192 193 except: 193 194 anonymous_request = True 194 195 early_error = sys.exc_info() … … 221 222 try: 222 223 try: 223 224 try: 225 # Protect against CSRF attacks. 226 # We can only block against such attacks if the user 227 # is logged in or if we have an incoming session cookie. 228 if (req.method == 'POST' and 229 req.args.get('__FORM_TOKEN') != req.form_token and 230 (req.incookie.has_key('trac_auth') or 231 req.incookie.has_key('trac_session'))): 232 raise TracError('Missing or invalid form token') 233 224 234 resp = chosen_handler.process_request(req) 225 235 if resp: 226 236 template, content_type = \ … … 257 267 content_type) 258 268 return template, content_type 259 269 270 def _get_form_token(self, req): 271 """Used to protect against CSRF. 260 272 273 The 'trac_auth' cookie is a good and strong shared secret, only 274 known by the user it belongs to and Trac itself. 275 276 The session id is our second best option, not as reliable since 277 it will change on each request if the user has cookies disabled in 278 his/her browser. 279 """ 280 if req.incookie.has_key('trac_auth'): 281 return req.incookie['trac_auth'].value 282 else: 283 return req.session.sid 284 285 261 286 def dispatch_request(environ, start_response): 262 287 """Main entry point for the Trac web interface. 263 288
