Tuesday, November 24, 2009

Django template urlencode unicode characters (Google App Engine)

I have just started work on my next Google App Engine project, and I have been using the Django Template urlencode filter to urlencode strings before displaying them as components inside an URL like this: <a href="/?{{ unsafe_string|urlencode }}" title="..">..</a>, to prevent XSS (Cross Site Scripting) attack, etc. This is all good as long as unsafe_string does not contain unicode characters. Underneath, the Django urlencode filter calls urllib.quote method to do the encoding. Unfortunately, this method does not like unicode characters, and a string containing unicode (utf-8) characters has to be utf-8 encoded before it can be put through urllib.quote (and hence the Django urlencode filter).

So I created a custom Django template filter to circumvent this issue:

Note: I have only tested it in Google App Engine (GAE) using its Django 0.96 template support. Also, the registration mechanism described below is specific to Google App Engine's own templating engine. However, there is no reason why this custom filter would not work in non-GAE Python apps using Django 0.96 or 1.0 templates. Please follow instructions here to configure your app if you are not using GAE.

The custom tag (which handles both unicode and "non-unicode" characters) itself is very straightforward to create.

I created a module customtags.xss (you can name it something else) as follows:

1) under the root folder of my GAE application, I created a folder "customtags"

2) inside customtags, I created two empty files xss.py and __init__.py (__init__.py will remain empty)

3) inside xss.py, I put
import types
import urllib
from django import template

register = template.Library()

def unicode_urlencode(value):
if type(value) is types.UnicodeType:
return urllib.quote(value.encode("utf-8"))
return urllib.quote(value)
So, the filter utf-8 encodes the string first if it is of type UnicodeType. (You may want to handle exceptions more gracefully though - at the moment, I am not catching exceptions...)

Let's look at how to register this template library so that you can use the filter inside your Django templates.

In your application script, for example, YOUR_APP_NAME.py, simply insert the line:
after the import statements at the top of the script. Obviously, if you are calling your module something else, you have to change the line above accordingly.

Now you can write <a href="/?{{ unsafe_string|unicode_urlencode }}" title="..">..</a> in your template!


Unknown DBA said...

Awesome. Thank you!

j_l_larson said...

How to adapt this to webapp2?