comparison MoinMoin/wikiutil.py @ 5469:cce24183de9e

improved ticketing code security support ticket creation for other pages than the current one (needed if POST happens to other page). use hmac/sha1 rather than just sha1 to improve security. add session id and user id to hmac computation input data, so you can't create usable tickets for other (logged in) users or users having a anon session. side effect: if you lose the session (log out, time out), you lose the ticket. if you change your sid or uid (log in), you lose the ticket. remove data from the ticket's "cleartext" part that are not needed there.
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 31 Jan 2010 22:50:49 +0100
parents eaef92e05011
children cded5f776bc1
comparison
equal deleted inserted replaced
5468:be7c57d8e2a3 5469:cce24183de9e
2457 return parts 2457 return parts
2458 else: 2458 else:
2459 return pagename, "" 2459 return pagename, ""
2460 2460
2461 ######################################################################## 2461 ########################################################################
2462 ### Tickets - used by RenamePage and DeletePage 2462 ### Tickets - usually used in forms to make sure that form submissions
2463 ### are in response to a form the same user got from moin for the same
2464 ### action and same page.
2463 ######################################################################## 2465 ########################################################################
2464 2466
2465 def createTicket(request, tm=None, action=None): 2467 def createTicket(request, tm=None, action=None, pagename=None):
2466 """ Create a ticket using a configured secret 2468 """ Create a ticket using a configured secret
2467 2469
2468 @param tm: unix timestamp (optional, uses current time if not given) 2470 @param tm: unix timestamp (optional, uses current time if not given)
2469 @param action: action name (optional, uses current action if not given) 2471 @param action: action name (optional, uses current action if not given)
2470 Note: if you create a ticket for a form that calls another 2472 Note: if you create a ticket for a form that calls another
2471 action than the current one, you MUST specify the 2473 action than the current one, you MUST specify the
2472 action you call when posting the form. 2474 action you call when posting the form.
2473 """ 2475 @param pagename: page name (optional, uses current page name if not given)
2474 2476 Note: if you create a ticket for a form that posts to another
2475 from MoinMoin.support.python_compatibility import hash_new 2477 page than the current one, you MUST specify the
2478 page name you use when posting the form.
2479 """
2480
2481 from MoinMoin.support.python_compatibility import hmac_new
2476 if tm is None: 2482 if tm is None:
2483 # for age-check of ticket
2477 tm = "%010x" % time.time() 2484 tm = "%010x" % time.time()
2478 2485
2479 # make the ticket specific to the page and action: 2486 # make the ticket very specific:
2480 try: 2487 if pagename is None:
2481 pagename = quoteWikinameURL(request.page.page_name) 2488 try:
2482 except: 2489 pagename = request.page.page_name
2483 pagename = 'None' 2490 except:
2491 pagename = ''
2484 2492
2485 if action is None: 2493 if action is None:
2486 try: 2494 action = request.action
2487 action = request.action 2495
2488 except: 2496 if request.session:
2489 action = 'None' 2497 # either a user is logged in or we have a anon session -
2490 2498 # if session times out, ticket will get invalid
2491 secret = request.cfg.secrets['wikiutil/tickets'] 2499 sid = request.session.sid
2492 digest = hash_new('sha1', secret) 2500 else:
2493 2501 sid = ''
2494 ticket = "%s.%s.%s" % (tm, pagename, action) 2502
2495 digest.update(ticket) 2503 if request.user.valid:
2496 2504 uid = request.user.id
2497 return "%s.%s" % (ticket, digest.hexdigest()) 2505 else:
2506 uid = ''
2507
2508 hmac_data = []
2509 for value in [tm, pagename, action, sid, uid, ]:
2510 if isinstance(value, unicode):
2511 value = value.encode('utf-8')
2512 hmac_data.append(value)
2513
2514 hmac = hmac_new(request.cfg.secrets['wikiutil/tickets'],
2515 ''.join(hmac_data))
2516 return "%s.%s" % (tm, hmac.hexdigest())
2498 2517
2499 2518
2500 def checkTicket(request, ticket): 2519 def checkTicket(request, ticket):
2501 """Check validity of a previously created ticket""" 2520 """Check validity of a previously created ticket"""
2502 try: 2521 try:
2509 now = time.time() 2528 now = time.time()
2510 if timestamp < now - 10 * 3600: 2529 if timestamp < now - 10 * 3600:
2511 # we don't accept tickets older than 10h 2530 # we don't accept tickets older than 10h
2512 logging.debug("checkTicket: too old ticket, timestamp %r" % timestamp) 2531 logging.debug("checkTicket: too old ticket, timestamp %r" % timestamp)
2513 return False 2532 return False
2533 # Note: if the session timed out, that will also invalidate the ticket,
2534 # if the ticket was created within a session.
2514 ourticket = createTicket(request, timestamp_str) 2535 ourticket = createTicket(request, timestamp_str)
2515 logging.debug("checkTicket: returning %r, got %r, expected %r" % (ticket == ourticket, ticket, ourticket)) 2536 logging.debug("checkTicket: returning %r, got %r, expected %r" % (ticket == ourticket, ticket, ourticket))
2516 return ticket == ourticket 2537 return ticket == ourticket
2517 2538
2518 2539