Ticket #4049: trac-0.10-csrf.2.patch
| File trac-0.10-csrf.2.patch, 5.1 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 20 from trac.util.html import Markup, Fragment, escape 19 21 from trac.util.text import to_unicode … … 274 276 cs.parseStr(string) 275 277 return cs 276 278 277 def render(self, template ):279 def render(self, template, form_token=None): 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 if form_token: 293 from cStringIO import StringIO 294 out = StringIO() 295 injector = FormTokenInjector(form_token, out) 296 injector.feed(template.render()) 297 return out.getvalue() 298 else: 299 return template.render() 291 300 301 302 class FormTokenInjector(HTMLParser): 303 """Identify and protect forms from CSRF attacks 304 305 This filter works by adding a input type=hidden field to POST forms. 306 """ 307 def __init__(self, form_token, out): 308 HTMLParser.__init__(self) 309 self.out = out 310 self.token = form_token 311 312 def handle_starttag(self, tag, attrs): 313 self.out.write(self.get_starttag_text()) 314 if tag.lower() == 'form': 315 for name, value in attrs: 316 if name.lower() == 'method' and value.lower() == 'post': 317 self.out.write('<input type="hidden" name="__FORM_TOKEN"' 318 ' value="%s"/>' % self.token) 319 320 def handle_startendtag(self, tag, attrs): 321 self.out.write(self.get_starttag_text()) 322 323 def handle_charref(self, name): 324 self.out.write('&#%s;' % name) 325 326 def handle_entityref(self, name): 327 self.out.write('&%s;' % name) 328 329 def handle_comment(self, data): 330 self.out.write('<!--%s-->' % data) 331 332 def handle_decl(self, data): 333 self.out.write('<!--%s-->' % data) 334 335 def handle_pi(self, data): 336 self.out.write('<?%s>' % data) 337 338 def handle_data(self, data): 339 self.out.write(data) 340 341 def handle_endtag(self, tag): 342 self.out.write('</' + tag + '>') 343 344 292 345 if __name__ == '__main__': 293 346 import doctest, sys 294 347 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
