Djangoセキュリティ完全ガイド
Djangoセキュリティ完全ガイド
Section titled “Djangoセキュリティ完全ガイド”Djangoアプリケーションのセキュリティ対策を、実務で使える実装例とともに詳しく解説します。
1. セキュリティとは
Section titled “1. セキュリティとは”Djangoのセキュリティ機能
Section titled “Djangoのセキュリティ機能”Djangoは、多くのセキュリティ機能を標準で提供しています。適切に設定することで、一般的なWebアプリケーションの脆弱性から保護できます。
セキュリティ対策 ├─ CSRF対策 ├─ XSS対策 ├─ SQLインジェクション対策 ├─ 認証・認可 └─ セキュリティヘッダー2. CSRF対策
Section titled “2. CSRF対策”CSRFとは
Section titled “CSRFとは”CSRF(Cross-Site Request Forgery)は、ユーザーが意図しないリクエストを送信させられる攻撃です。
DjangoのCSRF対策
Section titled “DjangoのCSRF対策”Djangoは、デフォルトでCSRF保護を提供しています。
MIDDLEWARE = [ # ... 'django.middleware.csrf.CsrfViewMiddleware', # CSRF保護 # ...]フォームでのCSRFトークン
Section titled “フォームでのCSRFトークン”<!-- テンプレート --><form method="post"> {% csrf_token %} <input type="text" name="username"> <button type="submit">送信</button></form>APIでのCSRF対策
Section titled “APIでのCSRF対策”from django.views.decorators.csrf import csrf_exemptfrom django.utils.decorators import method_decoratorfrom rest_framework.views import APIView
# CSRF保護を無効化(APIの場合)@method_decorator(csrf_exempt, name='dispatch')class MyAPIView(APIView): def post(self, request): # 処理 pass本番環境での設定
Section titled “本番環境での設定”# CSRF Cookieの設定CSRF_COOKIE_SECURE = True # HTTPSのみで送信CSRF_COOKIE_HTTPONLY = True # JavaScriptからアクセス不可CSRF_COOKIE_SAMESITE = 'Strict' # 同一サイトからのみ送信
# 信頼できるオリジンの設定CSRF_TRUSTED_ORIGINS = [ 'https://example.com', 'https://www.example.com',]3. XSS対策
Section titled “3. XSS対策”XSS(Cross-Site Scripting)は、悪意のあるスクリプトを注入する攻撃です。
Djangoの自動エスケープ
Section titled “Djangoの自動エスケープ”Djangoのテンプレートは、デフォルトで自動エスケープを行います。
<!-- 自動的にエスケープされる -->{{ user_input }}
<!-- エスケープを無効化(注意が必要) -->{{ user_input|safe }}
<!-- 手動でエスケープ -->{{ user_input|escape }}マークセーフの使用
Section titled “マークセーフの使用”from django.utils.safestring import mark_safe
def my_view(request): # 信頼できるHTMLのみマークセーフにする safe_html = mark_safe('<p>Trusted HTML</p>') return render(request, 'template.html', {'html': safe_html})Content Security Policy(CSP)
Section titled “Content Security Policy(CSP)”MIDDLEWARE = [ # ... 'django.middleware.security.SecurityMiddleware', # ...]
# CSPヘッダーの設定SECURE_CONTENT_SECURITY_POLICY = { 'default-src': "'self'", 'script-src': "'self' 'unsafe-inline'", 'style-src': "'self' 'unsafe-inline'",}4. SQLインジェクション対策
Section titled “4. SQLインジェクション対策”ORMの使用
Section titled “ORMの使用”DjangoのORMは、自動的にSQLインジェクションを防ぎます。
# 安全な方法(ORMを使用)User.objects.filter(name=user_input)
# 危険な方法(生のSQL、非推奨)from django.db import connectioncursor = connection.cursor()cursor.execute(f"SELECT * FROM users WHERE name = '{user_input}'") # 危険!生のSQLを使用する場合
Section titled “生のSQLを使用する場合”# 安全な方法(パラメータ化クエリ)from django.db import connection
cursor = connection.cursor()cursor.execute("SELECT * FROM users WHERE name = %s", [user_input])5. 認証・認可
Section titled “5. 認証・認可”AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend',]
# パスワードの検証AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { 'min_length': 8, } }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', },]from django.contrib.auth.decorators import login_required, permission_requiredfrom django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixinfrom django.views.generic import ListView
# 関数ベースビュー@login_required@permission_required('blog.add_post', raise_exception=True)def create_post(request): # 処理 pass
# クラスベースビューclass PostCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): permission_required = 'blog.add_post' model = Post fields = ['title', 'content']6. セキュリティヘッダー
Section titled “6. セキュリティヘッダー”セキュリティヘッダーの設定
Section titled “セキュリティヘッダーの設定”# HTTPSの強制SECURE_SSL_REDIRECT = True
# セキュリティヘッダーSECURE_BROWSER_XSS_FILTER = TrueSECURE_CONTENT_TYPE_NOSNIFF = TrueX_FRAME_OPTIONS = 'DENY' # クリックジャッキング対策
# Cookieの設定SESSION_COOKIE_SECURE = True # HTTPSのみで送信SESSION_COOKIE_HTTPONLY = True # JavaScriptからアクセス不可SESSION_COOKIE_SAMESITE = 'Strict' # 同一サイトからのみ送信
# HSTS(HTTP Strict Transport Security)SECURE_HSTS_SECONDS = 31536000 # 1年間SECURE_HSTS_INCLUDE_SUBDOMAINS = TrueSECURE_HSTS_PRELOAD = True7. セキュリティミドルウェア
Section titled “7. セキュリティミドルウェア”SecurityMiddleware
Section titled “SecurityMiddleware”MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', # セキュリティヘッダー # ...]カスタムセキュリティミドルウェア
Section titled “カスタムセキュリティミドルウェア”class SecurityHeadersMiddleware: def __init__(self, get_response): self.get_response = get_response
def __call__(self, request): response = self.get_response(request) response['X-Content-Type-Options'] = 'nosniff' response['X-Frame-Options'] = 'DENY' response['X-XSS-Protection'] = '1; mode=block' return response8. パスワードの管理
Section titled “8. パスワードの管理”パスワードのハッシュ化
Section titled “パスワードのハッシュ化”# Djangoは自動的にパスワードをハッシュ化from django.contrib.auth.models import User
user = User.objects.create_user( username='john', password='secure_password' # 自動的にハッシュ化される)パスワードの検証
Section titled “パスワードの検証”from django.contrib.auth import authenticate
def login_view(request): if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) return redirect('home') else: # エラーメッセージ pass9. セッション管理
Section titled “9. セッション管理”セッションの設定
Section titled “セッションの設定”# セッションの設定SESSION_ENGINE = 'django.contrib.sessions.backends.db' # データベース# またはSESSION_ENGINE = 'django.contrib.sessions.backends.cache' # キャッシュ
SESSION_COOKIE_AGE = 3600 # 1時間SESSION_EXPIRE_AT_BROWSER_CLOSE = True # ブラウザを閉じたらセッション終了SESSION_SAVE_EVERY_REQUEST = True # 毎リクエストでセッションを保存セッションの使用
Section titled “セッションの使用”def my_view(request): # セッションに値を保存 request.session['user_id'] = 123
# セッションから値を取得 user_id = request.session.get('user_id')
# セッションの削除 del request.session['user_id']10. ファイルアップロードのセキュリティ
Section titled “10. ファイルアップロードのセキュリティ”ファイルアップロードの検証
Section titled “ファイルアップロードの検証”from django.core.validators import FileExtensionValidator
class Document(models.Model): file = models.FileField( upload_to='documents/', validators=[ FileExtensionValidator(allowed_extensions=['pdf', 'doc', 'docx']) ] )ファイルサイズの制限
Section titled “ファイルサイズの制限”FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # 2.5MBDATA_UPLOAD_MAX_MEMORY_SIZE = 2621440 # 2.5MBファイルタイプの検証
Section titled “ファイルタイプの検証”from django import formsimport magic
class DocumentForm(forms.ModelForm): class Meta: model = Document fields = ['file']
def clean_file(self): file = self.cleaned_data['file'] # ファイルタイプの検証 file_type = magic.from_buffer(file.read(), mime=True) allowed_types = ['application/pdf', 'application/msword'] if file_type not in allowed_types: raise forms.ValidationError('Invalid file type.') return file11. レート制限
Section titled “11. レート制限”django-ratelimitの使用
Section titled “django-ratelimitの使用”pip install django-ratelimitfrom django_ratelimit.decorators import ratelimit
@ratelimit(key='ip', rate='5/m', method='POST')def login_view(request): # 1分間に5回まで pass12. 実務でのベストプラクティス
Section titled “12. 実務でのベストプラクティス”パターン1: 環境変数の使用
Section titled “パターン1: 環境変数の使用”from decouple import config
SECRET_KEY = config('SECRET_KEY')DEBUG = config('DEBUG', default=False, cast=bool)ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=lambda v: [s.strip() for s in v.split(',')])パターン2: セキュリティチェックリスト
Section titled “パターン2: セキュリティチェックリスト”# Djangoのセキュリティチェックpython manage.py check --deploy
# セキュリティの問題を確認python manage.py check13. よくある問題と解決策
Section titled “13. よくある問題と解決策”問題1: CSRFエラー
Section titled “問題1: CSRFエラー”原因:
- CSRFトークンが送信されていない
- セッションが無効
解決策:
<!-- テンプレートにCSRFトークンを追加 --><form method="post"> {% csrf_token %} <!-- フォームフィールド --></form>問題2: セッションが保持されない
Section titled “問題2: セッションが保持されない”原因:
SESSION_COOKIE_SECUREがTrueでHTTPを使用している- セッションの設定が間違っている
解決策:
# 開発環境ではFalseSESSION_COOKIE_SECURE = False # 開発環境# 本番環境ではTrueSESSION_COOKIE_SECURE = True # 本番環境(HTTPS必須)これで、Djangoのセキュリティ対策の基礎知識と実務での使い方を理解できるようになりました。