0001"""
0002Secure Form Tag Helpers -- For prevention of Cross-site request forgery (CSRF)
0003attacks.
0004
0005Generates form tags that include client-specific authorization tokens to be
0006verified by the destined web app.
0007
0008Authorization tokens are stored in the client's session. The web app can then
0009verify the request's submitted authorization token with the value in the
0010client's session.
0011
0012This ensures the request came from the originating page. See
0013http://en.wikipedia.org/wiki/Cross-site_request_forgery for more information.
0014
0015Pylons provides an ``authenticate_form`` decorator that does this verfication
0016on the behalf of controllers.
0017"""
0018import random
0019
0020from routes import request_config
0021
0022from form_tag import form, hidden_field
0023from prototype import form_remote_tag
0024from tags import content_tag
0025
0026token_key = '_authentication_token'
0027
0028def get_session():
0029 """Return the current session from the environ provided by routes. A Pylons
0030 supported session is assumed. A KeyError is raised if one doesn't exist."""
0031 environ = request_config().environ
0032 session_key = environ['pylons.environ_config']['session']
0033 session = environ[session_key]
0034 return session
0035
0036def authentication_token():
0037 """Return the current authentication token, creating one if one doesn't
0038 already exist."""
0039 session = get_session()
0040 if not token_key in session:
0041 try:
0042 token = str(random.getrandbits(128))
0043 except AttributeError:
0044 token = str(random.randrange(2**128))
0045 session[token_key] = token
0046 if hasattr(session, 'save'):
0047 session.save()
0048 return session[token_key]
0049
0050def secure_form(url, **args):
0051 """Create a form tag (like webhelpers.rails.form_tag.form) including a
0052 hidden authentication token field.
0053 """
0054 id = authentication_token()
0055 form_html = form(url, **args)
0056 return '%s\n%s' % (form_html,
0057 content_tag('div', hidden_field(token_key, id),
0058 style='display: none;'))
0059
0060def secure_form_remote_tag(**args):
0061 """Create a form tag (like webhelpers.rails.prototype.form_remote_tag)
0062 including a hidden authentication token field.
0063 """
0064 id = authentication_token()
0065 form_html = form_remote_tag(**args)
0066 return '%s\n%s' % (form_html,
0067 content_tag('div', hidden_field(token_key, id),
0068 style='display: none;'))
0069
0070__all__ = ['secure_form', 'secure_form_remote_tag']