Commits

Mike Orr committed 845a323

secure_form helper contributed by Jon Rosenbaugh.

  • Participants
  • Parent commits f579886

Comments (0)

Files changed (1)

webhelpers/html/secure_form.py

+"""
+Secure Form Tag Helpers -- For prevention of Cross-site request forgery (CSRF)
+attacks.
+
+Generates form tags that include client-specific authorization tokens to be
+verified by the destined web app.
+
+Authorization tokens are stored in the client's session. The web app can then
+verify the request's submitted authorization token with the value in the
+client's session.
+
+This ensures the request came from the originating page. See
+http://en.wikipedia.org/wiki/Cross-site_request_forgery for more information.
+
+Pylons provides an ``authenticate_form`` decorator that does this verfication
+on the behalf of controllers.
+
+These helpers depend on Pylons' ``session`` object.  Most of them can be easily 
+ported to another framework by changing the API calls.
+
+The helpers are implemented in such a way that it should be easy to create your
+own helpers if you are using helpers for AJAX calls.
+
+authentication_token() returns the current authentication token, creating one
+and storing it in the session if it doesn't already exist.
+
+auth_token_hidden_field() creates a hidden field (wrapped in an invisible div;
+I don't know if this is necessary, but the old WebHelpers had it like this)
+containing the authentication token.
+
+secure_form() is form() plus auth_token_hidden_field().
+"""
+
+# Do not import Pylons at module level; only within functions.  All WebHelpers
+# modules should be importable on any Python system for the standard
+# regression tests.
+
+import random
+
+from webhelpers.html.builder import HTML, literal
+from webhelpers.html.tags import form as insecure_form
+from webhelpers.html.tags import hidden
+
+token_key = "_authentication_token"
+
+def authentication_token():
+    """Return the current authentication token, creating one if one doesn't
+    already exist.
+    """
+    from pylons import session
+    if not token_key in session:
+        try:
+            token = str(random.getrandbits(128))
+        except AttributeError: # Python < 2.4
+            token = str(random.randrange(2**128))
+        session[token_key] = token
+        if hasattr(session, 'save'):
+            session.save()
+    return session[token_key]
+
+def auth_token_hidden_field():
+    token = hidden(token_key, authentication_token())
+    return HTML.div(token, style="display: none;")
+
+def secure_form(url, method="POST", multipart=False, **attrs):
+    """Start a form tag that points the action to an url. This
+    form tag will also include the hidden field containing
+    the auth token.
+
+    The url options should be given either as a string, or as a 
+    ``url()`` function. The method for the form defaults to POST.
+
+    Options:
+
+    ``multipart``
+        If set to True, the enctype is set to "multipart/form-data".
+    ``method``
+        The method to use when submitting the form, usually either 
+        "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
+        hidden input with name _method is added to simulate the verb
+        over POST.
+
+    """
+    form = insecure_form(url, method, multipart, **attrs)
+    token = auth_token_hidden_field()
+    return literal("%s\n%s" % (form, token))