ばぁど・うぉっちんぐ

セキュリティに強いWeb屋。自由と春を求めて羽ばたく渡り鳥。

このブログはGoogle Analyticsを利用しています

Django を触った所感 - Webアプリケーションフレームワークを扱うための3つのポイントとDjangoのセキュリティ対策 -

Djangoとは?

Python で書かれている Web アプリケーションフレームワーク

Djangoを用いることで、特定のルールに沿って開発するだけでWebアプリケーションを作成することができます。

docs.djangoproject.com

Djangoの特徴

Pythonで機能豊富なWebアプリケーションフレームワークというイメージ。

Webアプリケーションを作成するときに、ほぼ必須であるDBを主体とするWebアプリを簡単に構築できるようにすることが第一の目的。

実際に触ってみても、モデルファイルを作成してコマンドを打つだけでテーブルが作成されるのは、確かに簡素化されているという感触を持つことができた。

Webアプリケーションフレームワークを使用するための3つのポイント

Webアプリケーションフレームワークを使えるようになるためのポイントはルーティング、DBとのコネクション、HTMLへのレンダリングの3つ。

業務で使う場合もとりあえずこの3つを覚えておけば、新しいページを作るとかであれば事足りる。 Djangoにおいて、このポイントがそれぞれどのように書かれているかを調査してみた。

ルーティング

ディレクトリ内の urls.py にルーティング情報を記載する。

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),  # ex: /polls/5/
    path('<int:question_id>/results/', views.results, name='results'),  # ex: /polls/5/results/
    path('<int:question_id>/vote/', views.vote, name='vote'),  # ex: /polls/5/vote/
]

DBとの連携

models.py にモデル情報を記述する。 またDBテーブルのmigrateなどもこのファイルをもとに生成される。

classがDBのテーブル名になるし、中に宣言されている変数がテーブルのカラムとなる。

import datetime

from django.db import models
from django.utils import timezone

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

    def was_published_recentry(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

HTMLへのレンダリング

値を引き渡す側

views.pyに記述をする。 今回はurls.pyで記述されているように/polls/5/でアクセスがあれば、detail()が呼び出され、必要な値を受け取りrender()にhtml側に渡す値を渡している。

{'question': question} という記述のように辞書形式でhtml側に渡しているため、html側としては{{ question }}を参照すれば中に入っている値を取得することが可能。

from django.shortcuts import get_object_or_404, render

from .models import Choice, Question

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

値を受け取る側

値を表示する際は{{ }}で囲う。 Pythonを用いて、for文やif文を用いたい場合は {% %} で囲い値を表示する。

<h1>{{ question.question_text }}</h1>

{% if error_message %}<p><strong>{{error_message}}</strong></p>{% endif %}

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
    <input type="radio" name="choice" id="choice{{forloop.counter}}" value="{{ choice.id }}">
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="vote">
</form>

Djangoのセキュリティ対策

XSS

XSSとは、悪意のあるスクリプト(JavaScript)を埋め込まれ、ブラウザ側が認識してしまい悪意のあるものが埋め込んだスクリプトが実行されてしまう脆弱性。 一般的な対策としては、ブラウザ側で認識してしまう特殊文字(<, >, ', " など)をエスケープすること。

HTML を始めよう - ウェブ開発を学ぶ | MDN

Djangoでの対策としては{{ hoge }}で表示されるあたいは全てエスケープされる。

f:id:UltraBirdTech:20190727121223p:plain
Test about XSS

あまりやらないだろうが、デフォルトのXSS対策を無効化することも可能。その場合は{% autoescape off %}で無効化したい部分を囲ってあげる。

{% for choice in question.choice_set.all %}
    <input type="radio" name="choice" id="choice{{forloop.counter}}" value="{{ choice.id }}">
    {% autoescape off %}
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
    {% endautoescape %}
{% endfor %}

上記のような形でXSSが適用されるようなコードを入れた場合は、JavaScriptのコードとして実行される。

エスケープがoffになっているので、JavaScriptが実行される。 f:id:UltraBirdTech:20190727123216p:plain

JavaScriptとして認識されているため、画面上に描画はされない。 f:id:UltraBirdTech:20190727123231p:plain

csrf脆弱性

csrf脆弱性とは、サーバーの持つ情報に変更要求を行うPOST処理に対して、リクエストをおこなったものが正しい利用者かチェックをする機構を持たず受け入れてしまう脆弱性のこと。 一般的な対策としては、POST情報を送る際にサーバー側で生成したランダムな文字列(token)を一緒に送り、tokenが一致するかを確認している。

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}  # ☆ここ☆
{% for choice in question.choice_set.all %}
    <input type="radio" name="choice" id="choice{{forloop.counter}}" value="{{ choice.id }}">
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="vote">
</form>

まとめ

個人的にWebアプリケーションをPythonで作るときはDjangoで作ると思います。