[資安入門] 007 後端程式語言基礎:給資安新手的完整指南

前言

在前幾篇文章中,我們學習了網路協定和前端技術。現在,讓我們深入到網站的核心——後端。

如果說前端是餐廳的門面和服務生,那後端就是廚房和廚師。使用者在前端點餐(發送請求),後端負責處理訂單、從冰箱(資料庫)取出食材、烹飪(業務邏輯)、最後把料理送回給客人(回應)。

對資安人員來說,理解後端運作方式至關重要。大多數嚴重的安全漏洞——SQL Injection、Command Injection、認證繞過、權限提升——都發生在後端。你必須了解後端程式如何處理資料、如何與資料庫互動、如何驗證使用者身份,才能有效地發現和利用這些漏洞。

這篇文章將帶你認識後端開發的基礎知識,涵蓋程式語言概念、Web 框架、資料庫互動,以及常見的安全問題。

為什麼資安人員需要懂後端?

理解漏洞根本原因

當你看到一個 SQL Injection 漏洞時:

# 有漏洞的程式碼
query = "SELECT * FROM users WHERE username = '" + username + "'"

如果你不懂程式,你只能機械式地測試 ' OR '1'='1。但如果你理解後端:
– 你知道這是字串拼接造成的問題
– 你知道為什麼 Prepared Statement 可以防禦
– 你可以構造更複雜的 payload
– 你可以向開發者解釋如何修復

原始碼審計

許多資安工作需要審計原始碼:
– 白箱滲透測試
– 安全程式碼審查
– 漏洞研究
– 惡意程式分析

沒有程式基礎,這些工作根本無法進行。

開發安全工具

資安人員常常需要自己寫工具:
– 自動化掃描腳本
– 漏洞利用程式(Exploit)
– 資料處理腳本
– 客製化的測試工具

與開發團隊溝通

當你發現漏洞需要向開發者報告時,如果你能說:

「在 UserController.php 第 47 行,$_GET['id'] 直接拼接到 SQL 查詢中,建議使用 PDO Prepared Statement」

比起只說「這裡有 SQL Injection」要專業得多,也更容易被採納。

後端概述

什麼是後端?

後端(Backend)是指在伺服器上執行的程式,負責:

┌─────────────────────────────────────────────────────────────────┐
│                          後端職責                                │
├─────────────────────────────────────────────────────────────────┤
│  1. 接收請求     ← 處理來自前端的 HTTP 請求                      │
│  2. 身份驗證     ← 確認使用者身份(登入、Token 驗證)            │
│  3. 權限控制     ← 確認使用者有權執行該操作                      │
│  4. 業務邏輯     ← 處理核心功能(計算、轉換、驗證)              │
│  5. 資料存取     ← 與資料庫互動(CRUD 操作)                     │
│  6. 外部服務     ← 呼叫第三方 API(支付、郵件、簡訊)            │
│  7. 回傳結果     ← 將處理結果回傳給前端                          │
└─────────────────────────────────────────────────────────────────┘

後端架構示意圖

                    ┌─────────────────────────────────────────┐
                    │              Internet                    │
                    └─────────────────┬───────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────┐
│                        Web Server                                │
│                   (Nginx / Apache)                               │
│                    處理靜態檔案、反向代理、負載均衡               │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Application Server                            │
│              (Python/Flask, Node.js, PHP, Java)                  │
│                     執行後端程式邏輯                              │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐           │
│  │   路由處理    │  │   中介軟體    │  │   控制器     │           │
│  │   (Router)   │  │ (Middleware) │  │ (Controller) │           │
│  └──────────────┘  └──────────────┘  └──────────────┘           │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐           │
│  │   模型層     │  │   服務層     │  │   工具函數    │           │
│  │   (Model)    │  │  (Service)   │  │   (Utils)    │           │
│  └──────────────┘  └──────────────┘  └──────────────┘           │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        Database                                  │
│              (MySQL, PostgreSQL, MongoDB)                        │
│                       儲存資料                                    │
└─────────────────────────────────────────────────────────────────┘

常見後端程式語言

語言 特點 常用框架 資安領域應用
Python 簡潔易學、生態豐富 Django, Flask, FastAPI 滲透測試腳本、自動化工具、資料分析
PHP 專為 Web 設計、部署簡單 Laravel, Symfony 很多網站仍使用 PHP,審計常見
JavaScript (Node.js) 前後端統一、非同步優勢 Express, NestJS 全端開發、API 測試
Java 企業級、強型別、穩定 Spring Boot 企業應用審計、Android 安全
Go 高效能、並發優秀 Gin, Echo 安全工具開發(如 nuclei)
Ruby 優雅語法、開發快速 Ruby on Rails Metasploit 是用 Ruby 寫的
C# 微軟生態、企業應用 ASP.NET Core Windows 環境、企業應用

本文將以 Python 為主要範例語言,因為:
1. 語法簡潔,適合初學
2. 資安領域廣泛使用
3. 大量資安工具用 Python 開發
4. 快速驗證概念和撰寫 POC

程式語言基礎概念

無論使用哪種語言,以下概念都是通用的。

變數與資料型態

變數是儲存資料的容器。

# Python 範例

# 字串 (String)
name = "Alice"
message = 'Hello, World!'
multiline = """這是
多行字串"""

# 數字
age = 25              # 整數 (Integer)
price = 19.99         # 浮點數 (Float)
count = 1_000_000     # 可用底線增加可讀性

# 布林值 (Boolean)
is_admin = True
is_active = False

# 空值
nothing = None

# 列表 (List) - 可變的有序集合
fruits = ["apple", "banana", "cherry"]
fruits.append("orange")    # 加入元素
fruits[0]                  # "apple"(索引從 0 開始)

# 元組 (Tuple) - 不可變的有序集合
coordinates = (10, 20)

# 字典 (Dictionary) - 鍵值對
user = {
    "name": "Alice",
    "age": 25,
    "is_admin": False
}
user["name"]               # "Alice"
user.get("email", "N/A")   # 取得值,不存在則回傳預設值

# 集合 (Set) - 不重複的無序集合
unique_numbers = {1, 2, 3, 3, 3}  # {1, 2, 3}

# 型態檢查
type(name)     # <class 'str'>
type(age)      # <class 'int'>
type(fruits)   # <class 'list'>

# 型態轉換
str(25)        # "25"
int("42")      # 42
float("3.14")  # 3.14
list("abc")    # ['a', 'b', 'c']
// JavaScript (Node.js) 對照

let name = "Alice";            // 字串
const age = 25;                // 數字(不分整數浮點數)
let isAdmin = true;            // 布林值
let nothing = null;            // 空值
let notDefined = undefined;    // 未定義

let fruits = ["apple", "banana"];  // 陣列
let user = { name: "Alice", age: 25 };  // 物件
// PHP 對照

name = "Alice";           // 字串age = 25;                 // 整數
price = 19.99;            // 浮點數isAdmin = true;           // 布林值
nothing = null;           // 空值fruits = ["apple", "banana"];  // 陣列
$user = [
    "name" => "Alice",
    "age" => 25
];  // 關聯陣列

運算子

# 算術運算子
a + b    # 加
a - b    # 減
a * b    # 乘
a / b    # 除(回傳浮點數)
a // b   # 整數除法
a % b    # 取餘數(模運算)
a ** b   # 次方

# 比較運算子
a == b   # 等於
a != b   # 不等於
a > b    # 大於
a < b    # 小於
a >= b   # 大於等於
a <= b   # 小於等於

# 邏輯運算子
a and b  # 且
a or b   # 或
not a    # 非

# 成員運算子
"a" in "abc"           # True
"x" not in "abc"       # True
1 in [1, 2, 3]         # True

# 身份運算子
a is b       # 是否為同一物件
a is not b   # 是否不為同一物件

# 字串運算
"Hello" + " " + "World"  # "Hello World"
"Ha" * 3                  # "HaHaHa"

條件判斷

# if-elif-else
age = 18

if age < 13:
    print("兒童")
elif age < 20:
    print("青少年")
else:
    print("成人")

# 三元運算子
status = "成年" if age >= 18 else "未成年"

# 多條件判斷
if age >= 18 and has_id:
    print("可以購買")

if is_admin or is_moderator:
    print("有管理權限")

# 真值判斷(Truthy/Falsy)
# 以下值視為 False:
# - False
# - None
# - 0, 0.0
# - "", [], {}, set()

if username:  # 如果 username 不是空字串
    print(f"歡迎,{username}")
// JavaScript 對照
if (age < 13) {
    console.log("兒童");
} else if (age < 20) {
    console.log("青少年");
} else {
    console.log("成人");
}

// 三元運算子
let status = age >= 18 ? "成年" : "未成年";

// JavaScript 的 Falsy 值:
// false, null, undefined, 0, NaN, ""
// PHP 對照
if (age<13) {
    echo "兒童";
} elseif (age < 20) {
    echo "青少年";
} else {
    echo "成人";
}

// 三元運算子
status =age >= 18 ? "成年" : "未成年";

// Null 合併運算子
username =_GET['username'] ?? 'Guest';

迴圈

# for 迴圈
fruits = ["apple", "banana", "cherry"]

for fruit in fruits:
    print(fruit)

# 帶索引的迴圈
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")

# range 迴圈
for i in range(5):      # 0, 1, 2, 3, 4
    print(i)

for i in range(1, 6):   # 1, 2, 3, 4, 5
    print(i)

for i in range(0, 10, 2):  # 0, 2, 4, 6, 8(步長 2)
    print(i)

# 遍歷字典
user = {"name": "Alice", "age": 25}

for key in user:
    print(f"{key}: {user[key]}")

for key, value in user.items():
    print(f"{key}: {value}")

# while 迴圈
count = 0
while count < 5:
    print(count)
    count += 1

# 迴圈控制
for i in range(10):
    if i == 3:
        continue  # 跳過此次迭代
    if i == 7:
        break     # 終止迴圈
    print(i)

# 列表推導式(List Comprehension)
squares = [x ** 2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

函數

# 基本函數定義
def greet(name):
    return f"Hello, {name}!"

result = greet("Alice")  # "Hello, Alice!"

# 預設參數
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

greet("Alice")              # "Hello, Alice!"
greet("Alice", "Hi")        # "Hi, Alice!"

# 關鍵字參數
greet(greeting="Hey", name="Bob")  # "Hey, Bob!"

# 可變參數
def sum_all(*numbers):
    return sum(numbers)

sum_all(1, 2, 3, 4, 5)  # 15

# 關鍵字可變參數
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=25, city="Taipei")

# Lambda 函數(匿名函數)
square = lambda x: x ** 2
square(5)  # 25

# 搭配高階函數使用
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))

# 函數作為參數
def apply_operation(numbers, operation):
    return [operation(n) for n in numbers]

apply_operation([1, 2, 3], lambda x: x * 2)  # [2, 4, 6]

類別與物件(Object-Oriented Programming)

# 類別定義
class User:
    # 類別屬性(所有實例共享)
    user_count = 0

    # 建構函數
    def __init__(self, username, email):
        # 實例屬性
        self.username = username
        self.email = email
        self.is_active = True
        User.user_count += 1

    # 實例方法
    def greet(self):
        return f"Hello, I'm {self.username}"

    def deactivate(self):
        self.is_active = False

    # 類別方法
    @classmethod
    def get_user_count(cls):
        return cls.user_count

    # 靜態方法
    @staticmethod
    def validate_email(email):
        return "@" in email

    # 特殊方法
    def __str__(self):
        return f"User({self.username})"

    def __repr__(self):
        return f"User(username='{self.username}', email='{self.email}')"

# 建立實例
user1 = User("alice", "[email protected]")
user2 = User("bob", "[email protected]")

# 使用方法
print(user1.greet())           # "Hello, I'm alice"
print(User.get_user_count())   # 2
print(User.validate_email("[email protected]"))  # True

# 繼承
class AdminUser(User):
    def __init__(self, username, email, admin_level):
        super().__init__(username, email)  # 呼叫父類別建構函數
        self.admin_level = admin_level
        self.permissions = []

    def add_permission(self, permission):
        self.permissions.append(permission)

    # 覆寫父類別方法
    def greet(self):
        return f"Hello, I'm admin {self.username}"

admin = AdminUser("admin", "[email protected]", 5)
print(admin.greet())  # "Hello, I'm admin admin"

例外處理

# 基本例外處理
try:
    result = 10 / 0
except ZeroDivisionError:
    print("除以零錯誤!")

# 捕捉多種例外
try:
    value = int(input("輸入數字:"))
    result = 10 / value
except ValueError:
    print("輸入的不是數字!")
except ZeroDivisionError:
    print("不能除以零!")
except Exception as e:
    print(f"發生錯誤:{e}")

# else 和 finally
try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("檔案不存在")
else:
    print("讀取成功")  # 沒有例外時執行
finally:
    print("清理資源")  # 無論如何都會執行
    if 'file' in locals():
        file.close()

# 使用 with 語句(Context Manager)
try:
    with open("data.txt", "r") as file:
        content = file.read()
except FileNotFoundError:
    print("檔案不存在")

# 拋出例外
def validate_age(age):
    if age < 0:
        raise ValueError("年齡不能為負數")
    if age > 150:
        raise ValueError("年齡不合理")
    return True

# 自訂例外
class AuthenticationError(Exception):
    pass

class PermissionDeniedError(Exception):
    def __init__(self, user, action):
        self.user = user
        self.action = action
        super().__init__(f"User {user} cannot perform {action}")

# 使用自訂例外
def check_permission(user, action):
    if not user.is_admin:
        raise PermissionDeniedError(user.username, action)

模組與套件

# 匯入模組
import os
import sys
import json

# 匯入特定函數
from datetime import datetime, timedelta
from urllib.parse import urlparse, urlencode

# 匯入並重命名
import numpy as np
import pandas as pd

# 匯入所有(不建議)
from math import *

# 常用標準函式庫
import os           # 作業系統互動
import sys          # 系統相關
import json         # JSON 處理
import re           # 正規表達式
import hashlib      # 雜湊函數
import base64       # Base64 編解碼
import urllib       # URL 處理
import http         # HTTP 相關
import socket       # 網路 Socket
import subprocess   # 執行外部命令
import threading    # 多執行緒
import logging      # 日誌記錄

# 使用範例
import os
print(os.getcwd())              # 當前目錄
print(os.listdir('.'))          # 列出檔案
os.environ.get('PATH')          # 環境變數

import json
data = {"name": "Alice", "age": 25}
json_str = json.dumps(data)     # 轉成 JSON 字串
parsed = json.loads(json_str)   # 解析 JSON

import re
pattern = r'\d+'                # 匹配數字
matches = re.findall(pattern, "abc123def456")  # ['123', '456']

import hashlib
md5_hash = hashlib.md5(b"password").hexdigest()
sha256_hash = hashlib.sha256(b"password").hexdigest()

import base64
encoded = base64.b64encode(b"Hello").decode()  # 'SGVsbG8='
decoded = base64.b64decode(encoded)             # b'Hello'

Web 後端開發

現在讓我們看看如何用程式語言建立 Web 後端。

HTTP 請求處理流程

┌──────────────────────────────────────────────────────────────────┐
│                      HTTP 請求處理流程                            │
├──────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. 接收請求                                                      │
│     ┌─────────────────────────────────────────────────────────┐  │
│     │ POST /api/users HTTP/1.1                                │  │
│     │ Host: example.com                                       │  │
│     │ Content-Type: application/json                          │  │
│     │ Authorization: Bearer xxx                               │  │
│     │                                                         │  │
│     │ {"username": "alice", "email": "[email protected]"}     │  │
│     └─────────────────────────────────────────────────────────┘  │
│                              │                                    │
│                              ▼                                    │
│  2. 路由匹配                                                      │
│     POST /api/users  →  UserController.create()                  │
│                              │                                    │
│                              ▼                                    │
│  3. 中介軟體處理                                                  │
│     - 日誌記錄                                                    │
│     - 身份驗證(檢查 Token)                                      │
│     - 權限檢查                                                    │
│     - 請求驗證                                                    │
│                              │                                    │
│                              ▼                                    │
│  4. 控制器處理                                                    │
│     - 解析請求資料                                                │
│     - 驗證輸入                                                    │
│     - 呼叫服務層                                                  │
│                              │                                    │
│                              ▼                                    │
│  5. 服務層/業務邏輯                                               │
│     - 執行業務邏輯                                                │
│     - 呼叫資料存取層                                              │
│                              │                                    │
│                              ▼                                    │
│  6. 資料存取層                                                    │
│     - 與資料庫互動                                                │
│     - CRUD 操作                                                   │
│                              │                                    │
│                              ▼                                    │
│  7. 回傳回應                                                      │
│     ┌─────────────────────────────────────────────────────────┐  │
│     │ HTTP/1.1 201 Created                                    │  │
│     │ Content-Type: application/json                          │  │
│     │                                                         │  │
│     │ {"id": 1, "username": "alice", "created_at": "..."}     │  │
│     └─────────────────────────────────────────────────────────┘  │
│                                                                   │
└──────────────────────────────────────────────────────────────────┘

Python Flask 範例

Flask 是 Python 最流行的輕量級 Web 框架。

from flask import Flask, request, jsonify
from functools import wraps

app = Flask(__name__)

# 模擬資料庫
users_db = {}

# ===== 中介軟體範例 =====

def require_auth(f):
    """身份驗證裝飾器"""
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token or not token.startswith('Bearer '):
            return jsonify({"error": "Missing or invalid token"}), 401
        # 實際應用中會驗證 Token
        return f(*args, **kwargs)
    return decorated

# ===== 路由與控制器 =====

@app.route('/')
def home():
    """首頁"""
    return jsonify({"message": "Welcome to the API"})

@app.route('/api/users', methods=['GET'])
def get_users():
    """取得所有使用者"""
    return jsonify(list(users_db.values()))

@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    """取得單一使用者"""
    user = users_db.get(user_id)
    if not user:
        return jsonify({"error": "User not found"}), 404
    return jsonify(user)

@app.route('/api/users', methods=['POST'])
def create_user():
    """建立使用者"""
    data = request.get_json()

    # 輸入驗證
    if not data:
        return jsonify({"error": "No data provided"}), 400

    username = data.get('username')
    email = data.get('email')

    if not username or not email:
        return jsonify({"error": "Username and email are required"}), 400

    # 建立使用者
    user_id = len(users_db) + 1
    user = {
        "id": user_id,
        "username": username,
        "email": email
    }
    users_db[user_id] = user

    return jsonify(user), 201

@app.route('/api/users/<int:user_id>', methods=['PUT'])
@require_auth
def update_user(user_id):
    """更新使用者"""
    if user_id not in users_db:
        return jsonify({"error": "User not found"}), 404

    data = request.get_json()
    users_db[user_id].update(data)

    return jsonify(users_db[user_id])

@app.route('/api/users/<int:user_id>', methods=['DELETE'])
@require_auth
def delete_user(user_id):
    """刪除使用者"""
    if user_id not in users_db:
        return jsonify({"error": "User not found"}), 404

    del users_db[user_id]
    return '', 204

# ===== 錯誤處理 =====

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "Not found"}), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({"error": "Internal server error"}), 500

# ===== 啟動應用 =====

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

Node.js Express 範例

const express = require('express');
const app = express();

// 中介軟體
app.use(express.json());  // 解析 JSON

// 模擬資料庫
let users = [];

// 身份驗證中介軟體
const requireAuth = (req, res, next) => {
    const token = req.headers.authorization;
    if (!token || !token.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Unauthorized' });
    }
    next();
};

// 路由
app.get('/', (req, res) => {
    res.json({ message: 'Welcome to the API' });
});

app.get('/api/users', (req, res) => {
    res.json(users);
});

app.get('/api/users/:id', (req, res) => {
    const user = users.find(u => u.id === parseInt(req.params.id));
    if (!user) {
        return res.status(404).json({ error: 'User not found' });
    }
    res.json(user);
});

app.post('/api/users', (req, res) => {
    const { username, email } = req.body;

    if (!username || !email) {
        return res.status(400).json({ error: 'Username and email required' });
    }

    const user = {
        id: users.length + 1,
        username,
        email
    };
    users.push(user);

    res.status(201).json(user);
});

app.delete('/api/users/:id', requireAuth, (req, res) => {
    const index = users.findIndex(u => u.id === parseInt(req.params.id));
    if (index === -1) {
        return res.status(404).json({ error: 'User not found' });
    }
    users.splice(index, 1);
    res.status(204).send();
});

// 啟動伺服器
app.listen(3000, () => {
    console.log('Server running on port 3000');
});

PHP 範例

<?php
// 簡單的 PHP API 範例

header('Content-Type: application/json');

// 模擬資料庫
users = [];

// 取得請求方法和路徑method = _SERVER['REQUEST_METHOD'];path = parse_url(_SERVER['REQUEST_URI'], PHP_URL_PATH);

// 路由處理
switch (path) {
    case '/':
        echo json_encode(['message' => 'Welcome to the API']);
        break;

    case '/api/users':
        if (method === 'GET') {
            echo json_encode(users);
        } elseif (method === 'POST') {data = json_decode(file_get_contents('php://input'), true);

            if (empty(data['username']) || empty(data['email'])) {
                http_response_code(400);
                echo json_encode(['error' => 'Username and email required']);
                exit;
            }

            user = [
                'id' => count(users) + 1,
                'username' => data['username'],
                'email' =>data['email']
            ];
            users[] =user;

            http_response_code(201);
            echo json_encode($user);
        }
        break;

    default:
        http_response_code(404);
        echo json_encode(['error' => 'Not found']);
}
?>

資料庫互動

後端程式通常需要與資料庫互動來儲存和讀取資料。

SQL 基礎

-- 建立資料表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    is_admin BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 新增資料 (INSERT)
INSERT INTO users (username, email, password_hash)
VALUES ('alice', '[email protected]', 'hashed_password');

-- 查詢資料 (SELECT)
SELECT * FROM users;
SELECT id, username, email FROM users WHERE is_admin = TRUE;
SELECT * FROM users WHERE username = 'alice';
SELECT * FROM users WHERE created_at > '2025-01-01' ORDER BY created_at DESC;
SELECT COUNT(*) FROM users;

-- 更新資料 (UPDATE)
UPDATE users SET email = '[email protected]' WHERE id = 1;
UPDATE users SET is_admin = TRUE WHERE username = 'alice';

-- 刪除資料 (DELETE)
DELETE FROM users WHERE id = 1;
DELETE FROM users WHERE is_admin = FALSE;

-- 聯結查詢 (JOIN)
SELECT users.username, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE orders.total > 100;

Python 資料庫操作

import sqlite3
import mysql.connector
from contextlib import contextmanager

# ===== SQLite 範例 =====

# 連接資料庫
conn = sqlite3.connect('database.db')
cursor = conn.cursor()

# 建立資料表
cursor.execute('''
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT NOT NULL UNIQUE,
        email TEXT NOT NULL,
        password_hash TEXT NOT NULL
    )
''')

# ❌ 不安全:SQL Injection 漏洞!
username = "alice' OR '1'='1"
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")

# ✓ 安全:使用參數化查詢
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))

# 新增資料
cursor.execute(
    "INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)",
    ("alice", "[email protected]", "hashed_password")
)
conn.commit()

# 查詢資料
cursor.execute("SELECT * FROM users WHERE id = ?", (1,))
user = cursor.fetchone()  # 取得一筆
# 或
users = cursor.fetchall()  # 取得所有

# 關閉連接
cursor.close()
conn.close()

# ===== MySQL 範例 =====

# 連接設定
config = {
    'host': 'localhost',
    'user': 'root',
    'password': 'password',
    'database': 'myapp'
}

conn = mysql.connector.connect(**config)
cursor = conn.cursor(dictionary=True)  # 回傳字典格式

# 參數化查詢
cursor.execute(
    "SELECT * FROM users WHERE username = %s AND is_active = %s",
    (username, True)
)
user = cursor.fetchone()

# ===== 使用 ORM(SQLAlchemy)=====

from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    username = Column(String(50), unique=True, nullable=False)
    email = Column(String(100), nullable=False)
    password_hash = Column(String(255), nullable=False)
    is_admin = Column(Boolean, default=False)

# 建立引擎和 Session
engine = create_engine('sqlite:///database.db')
Session = sessionmaker(bind=engine)
session = Session()

# 使用 ORM 查詢(自動防止 SQL Injection)
user = session.query(User).filter_by(username='alice').first()
admins = session.query(User).filter(User.is_admin == True).all()

# 新增資料
new_user = User(username='bob', email='[email protected]', password_hash='xxx')
session.add(new_user)
session.commit()

常見後端安全漏洞

這是資安人員最需要關注的部分。

SQL Injection

SQL Injection 是最經典也最危險的漏洞之一。

# ❌ 有漏洞的程式碼
def get_user(username):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    cursor.execute(query)
    return cursor.fetchone()

# 攻擊者輸入:alice' OR '1'='1
# 實際執行:SELECT * FROM users WHERE username = 'alice' OR '1'='1'
# 結果:回傳所有使用者!

# 攻擊者輸入:alice'; DROP TABLE users; --
# 實際執行:SELECT * FROM users WHERE username = 'alice'; DROP TABLE users; --'
# 結果:刪除整個資料表!

# 攻擊者輸入:' UNION SELECT username, password_hash, null FROM users --
# 結果:洩露所有密碼!

# ✓ 安全的做法:參數化查詢
def get_user_safe(username):
    cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
    return cursor.fetchone()
<?php
// ❌ 有漏洞
username =_GET['username'];
query = "SELECT * FROM users WHERE username = 'username'";
result = mysqli_query(conn, query);

// ✓ 安全:使用 Prepared Statementstmt = conn->prepare("SELECT * FROM users WHERE username = ?");stmt->bind_param("s", username);stmt->execute();
?>

Command Injection

當程式執行系統命令並包含使用者輸入時,可能發生命令注入。

import os
import subprocess

# ❌ 有漏洞的程式碼
def ping_host(host):
    os.system(f"ping -c 1 {host}")

# 攻擊者輸入:8.8.8.8; cat /etc/passwd
# 實際執行:ping -c 1 8.8.8.8; cat /etc/passwd
# 結果:洩露系統檔案!

# 攻擊者輸入:8.8.8.8; rm -rf /
# 後果不堪設想!

# ✓ 安全的做法
def ping_host_safe(host):
    # 1. 輸入驗證
    import re
    if not re.match(r'^[\d.]+$', host):  # 只允許數字和點
        raise ValueError("Invalid host")

    # 2. 使用 subprocess 並避免 shell=True
    result = subprocess.run(
        ['ping', '-c', '1', host],
        capture_output=True,
        text=True,
        timeout=10
    )
    return result.stdout
<?php
// ❌ 有漏洞
filename =_GET['filename'];
system("cat " . filename);

// 攻擊:?filename=;cat /etc/passwd

// ✓ 安全:使用 escapeshellargfilename = escapeshellarg(_GET['filename']);
system("cat " .filename);

// 更好:完全避免使用 shell
content = file_get_contents(validated_filename);
?>

Path Traversal(路徑穿越)

# ❌ 有漏洞的程式碼
def read_file(filename):
    with open(f"/var/www/files/{filename}", 'r') as f:
        return f.read()

# 攻擊者輸入:../../../etc/passwd
# 實際路徑:/var/www/files/../../../etc/passwd = /etc/passwd
# 結果:讀取到系統敏感檔案!

# ✓ 安全的做法
import os

def read_file_safe(filename):
    base_dir = "/var/www/files"

    # 解析絕對路徑
    full_path = os.path.abspath(os.path.join(base_dir, filename))

    # 確保路徑在允許的目錄內
    if not full_path.startswith(base_dir):
        raise ValueError("Invalid path")

    # 確保檔案存在
    if not os.path.isfile(full_path):
        raise FileNotFoundError("File not found")

    with open(full_path, 'r') as f:
        return f.read()

Insecure Deserialization(不安全的反序列化)

import pickle
import yaml

# ❌ 危險:使用 pickle 反序列化不信任的資料
data = pickle.loads(user_input)  # 可以執行任意程式碼!

# 攻擊者可以構造惡意 pickle 資料來執行系統命令
# 例如:os.system('rm -rf /')

# ❌ 危險:不安全的 YAML 載入
data = yaml.load(user_input)  # 可以執行任意程式碼!

# ✓ 安全:使用安全的載入方式
data = yaml.safe_load(user_input)

# ✓ 安全:使用 JSON(不能執行程式碼)
import json
data = json.loads(user_input)

Server-Side Request Forgery (SSRF)

import requests

# ❌ 有漏洞的程式碼
def fetch_url(url):
    response = requests.get(url)
    return response.text

# 攻擊者輸入:http://localhost:6379/  (存取內部 Redis)
# 攻擊者輸入:http://169.254.169.254/  (AWS metadata,可能洩露憑證)
# 攻擊者輸入:file:///etc/passwd      (讀取本地檔案)

# ✓ 安全的做法
from urllib.parse import urlparse
import ipaddress

def fetch_url_safe(url):
    parsed = urlparse(url)

    # 只允許 http 和 https
    if parsed.scheme not in ['http', 'https']:
        raise ValueError("Invalid scheme")

    # 解析主機 IP
    try:
        ip = ipaddress.ip_address(parsed.hostname)
        # 禁止內部 IP
        if ip.is_private or ip.is_loopback or ip.is_reserved:
            raise ValueError("Internal IP not allowed")
    except ValueError:
        # 如果不是 IP,可能是域名,需要進一步驗證
        pass

    # 白名單驗證
    allowed_domains = ['example.com', 'api.example.com']
    if parsed.hostname not in allowed_domains:
        raise ValueError("Domain not allowed")

    response = requests.get(url, timeout=10)
    return response.text

Authentication Bypass(認證繞過)

# ❌ 有漏洞:不安全的密碼比較
def login(username, password):
    user = get_user(username)
    if user and user.password == password:  # 明文比較!
        return True
    return False

# 問題:
# 1. 密碼應該儲存雜湊值,不是明文
# 2. 字串比較可能受到 Timing Attack

# ✓ 安全的做法
import hashlib
import hmac

def login_safe(username, password):
    user = get_user(username)
    if not user:
        return False

    # 使用安全的雜湊比較
    password_hash = hashlib.pbkdf2_hmac(
        'sha256',
        password.encode(),
        user.salt.encode(),
        100000
    )

    # 使用常數時間比較,防止 Timing Attack
    return hmac.compare_digest(password_hash, user.password_hash)

# ❌ 有漏洞:JWT 驗證不當
import jwt

def verify_token(token):
    # 沒有驗證簽章!
    payload = jwt.decode(token, options={"verify_signature": False})
    return payload

# 攻擊者可以偽造 Token 內容

# ✓ 安全的做法
def verify_token_safe(token):
    try:
        payload = jwt.decode(
            token,
            SECRET_KEY,
            algorithms=['HS256']  # 明確指定演算法
        )
        return payload
    except jwt.InvalidTokenError:
        return None

Mass Assignment(批量賦值)

# ❌ 有漏洞的程式碼
@app.route('/api/users', methods=['POST'])
def create_user():
    data = request.get_json()
    user = User(**data)  # 直接使用所有傳入的資料
    db.session.add(user)
    db.session.commit()
    return jsonify(user.to_dict())

# 攻擊者發送:{"username": "alice", "email": "...", "is_admin": true}
# 結果:普通使用者變成管理員!

# ✓ 安全的做法
@app.route('/api/users', methods=['POST'])
def create_user_safe():
    data = request.get_json()

    # 只接受允許的欄位
    allowed_fields = ['username', 'email', 'password']
    user_data = {k: v for k, v in data.items() if k in allowed_fields}

    user = User(**user_data)
    db.session.add(user)
    db.session.commit()
    return jsonify(user.to_dict())

安全編碼實踐

輸入驗證

import re
from email_validator import validate_email

def validate_user_input(data):
    errors = []

    # 驗證使用者名稱
    username = data.get('username', '')
    if not username:
        errors.append("Username is required")
    elif len(username) < 3 or len(username) > 20:
        errors.append("Username must be 3-20 characters")
    elif not re.match(r'^[a-zA-Z0-9_]+$', username):
        errors.append("Username can only contain letters, numbers, and underscores")

    # 驗證電子郵件
    email = data.get('email', '')
    try:
        validate_email(email)
    except:
        errors.append("Invalid email address")

    # 驗證密碼
    password = data.get('password', '')
    if len(password) < 8:
        errors.append("Password must be at least 8 characters")
    if not re.search(r'[A-Z]', password):
        errors.append("Password must contain uppercase letter")
    if not re.search(r'[a-z]', password):
        errors.append("Password must contain lowercase letter")
    if not re.search(r'\d', password):
        errors.append("Password must contain digit")

    return errors if errors else None

輸出編碼

from markupsafe import escape
import html

def render_user_content(content):
    # HTML 編碼防止 XSS
    safe_content = html.escape(content)
    return f"<div>{safe_content}</div>"

# Flask 的 Jinja2 模板會自動編碼
# {{ user_input }}  會自動編碼
# {{ user_input|safe }}  不會編碼(危險!)

密碼處理

import bcrypt
import secrets

def hash_password(password):
    """安全地雜湊密碼"""
    salt = bcrypt.gensalt()
    return bcrypt.hashpw(password.encode(), salt)

def verify_password(password, hashed):
    """驗證密碼"""
    return bcrypt.checkpw(password.encode(), hashed)

def generate_token():
    """產生安全的隨機 Token"""
    return secrets.token_urlsafe(32)

def generate_reset_token():
    """產生密碼重設 Token"""
    return secrets.token_hex(32)

日誌記錄

import logging

# 設定日誌
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('app.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

def login(username, password):
    user = get_user(username)

    if not user:
        # 記錄失敗的登入嘗試
        logger.warning(f"Failed login attempt for non-existent user: {username}")
        return False

    if not verify_password(password, user.password_hash):
        logger.warning(f"Failed login attempt for user: {username}")
        return False

    logger.info(f"Successful login for user: {username}")
    return True

# ❌ 不要記錄敏感資訊
logger.info(f"User {username} logged in with password {password}")  # 危險!

# ✓ 安全的日誌
logger.info(f"User {username} logged in successfully")

實戰練習

搭建簡單的練習環境

# vulnerable_app.py - 有意設計漏洞的練習應用

from flask import Flask, request, render_template_string
import sqlite3
import os

app = Flask(__name__)

# 初始化資料庫
def init_db():
    conn = sqlite3.connect('vulnerable.db')
    c = conn.cursor()
    c.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY,
            username TEXT,
            password TEXT,
            email TEXT
        )
    ''')
    c.execute("INSERT OR IGNORE INTO users VALUES (1, 'admin', 'admin123', '[email protected]')")
    c.execute("INSERT OR IGNORE INTO users VALUES (2, 'user', 'user123', '[email protected]')")
    conn.commit()
    conn.close()

init_db()

# SQL Injection 漏洞
@app.route('/search')
def search():
    username = request.args.get('username', '')
    conn = sqlite3.connect('vulnerable.db')
    c = conn.cursor()
    # 有漏洞的查詢
    query = f"SELECT * FROM users WHERE username = '{username}'"
    c.execute(query)
    results = c.fetchall()
    conn.close()
    return str(results)

# XSS 漏洞
@app.route('/hello')
def hello():
    name = request.args.get('name', 'Guest')
    # 有漏洞的模板
    template = f"<h1>Hello, {name}!</h1>"
    return render_template_string(template)

# Command Injection 漏洞
@app.route('/ping')
def ping():
    host = request.args.get('host', '127.0.0.1')
    # 有漏洞的命令執行
    output = os.popen(f"ping -c 1 {host}").read()
    return f"<pre>{output}</pre>"

# Path Traversal 漏洞
@app.route('/read')
def read_file():
    filename = request.args.get('file', 'readme.txt')
    try:
        # 有漏洞的檔案讀取
        with open(f"./files/{filename}", 'r') as f:
            content = f.read()
        return f"<pre>{content}</pre>"
    except:
        return "File not found"

if __name__ == '__main__':
    app.run(debug=True, port=5000)

練習任務

  1. SQL Injection
    • 訪問 /search?username=admin
    • 嘗試:/search?username=' OR '1'='1
    • 嘗試提取所有密碼
  2. XSS
    • 訪問 /hello?name=Alice
    • 嘗試:/hello?name=<script>alert('XSS')</script>
  3. Command Injection
    • 訪問 /ping?host=127.0.0.1
    • 嘗試:/ping?host=127.0.0.1;whoami
  4. Path Traversal
    • 訪問 /read?file=readme.txt
    • 嘗試:/read?file=../../../etc/passwd

安全工具推薦

靜態分析工具
Bandit(Python):pip install bandit && bandit -r your_code/
ESLint(JavaScript):安全規則檢查
SonarQube:多語言支援

動態測試工具
OWASP ZAP:Web 應用掃描
Burp Suite:滲透測試必備
SQLMap:自動化 SQL Injection

延伸學習

練習平台

  • OWASP WebGoat:學習 Web 安全的教學平台
  • DVWA(Damn Vulnerable Web Application):有意設計漏洞的 PHP 應用
  • HackTheBox:真實環境滲透測試
  • PortSwigger Web Security Academy:免費的 Web 安全課程
  • PentesterLab:滲透測試練習

推薦資源

書籍
– 《The Web Application Hacker’s Handbook》
– 《Black Hat Python》
– 《Violent Python》

線上資源
– OWASP Testing Guide
– OWASP Cheat Sheet Series
– Python 官方文件
– Flask/Django 安全指南

常用框架安全文件

  • Flask Security:https://flask.palletsprojects.com/en/2.0.x/security/
  • Django Security:https://docs.djangoproject.com/en/4.0/topics/security/
  • Express Security:https://expressjs.com/en/advanced/best-practice-security.html
  • Laravel Security:https://laravel.com/docs/security

總結

這篇文章涵蓋了後端程式語言的基礎知識:

程式基礎
– 變數與資料型態
– 控制流程(條件、迴圈)
– 函數與類別
– 例外處理
– 模組與套件

Web 後端開發
– HTTP 請求處理流程
– Web 框架使用(Flask、Express、PHP)
– 路由與中介軟體
– RESTful API 設計

資料庫互動
– SQL 基礎語法
– 參數化查詢
– ORM 使用

安全漏洞
– SQL Injection
– Command Injection
– Path Traversal
– SSRF
– 認證繞過
– Mass Assignment

安全實踐
– 輸入驗證
– 輸出編碼
– 密碼處理
– 日誌記錄

對資安人員來說,理解後端程式是進行安全測試的基礎。無論是:
– 白箱滲透測試(程式碼審計)
– 黑箱測試(理解後端行為)
– 漏洞研究(分析根本原因)
– 撰寫 Exploit(利用漏洞)
– 與開發團隊溝通(提出修復建議)

都需要扎實的後端程式知識。

下一步,你可以:
– 搭建練習環境,實際測試各種漏洞
– 學習使用 Burp Suite 進行 Web 測試
– 嘗試 OWASP WebGoat 或 DVWA
– 深入學習一個框架(如 Flask 或 Django)
– 練習程式碼審計

飛飛
飛飛

講師學歷:臺科資工所、逢甲資工系畢業。
技術專長:OSINT、滲透測試、網站開發、專業易懂教育訓練。
證照書籍:OSCP、OSCE³、著《資安這條路:領航新手的 Web Security 指南》。
教學經驗:60+ 企業教學經驗、指導過上百位學員。
教學特色:新手友善、耐心指導、擅長圖解(流程圖、心智圖)引導學習。
社群經驗:目前經營全臺資安社群 CURA,曾任臺科資安社社長、逢甲黑客社社長。
社群交流:LINE 社群《飛飛的資安大圈圈》,即時分享經驗、鼓勵交流。
社群分享:FB 粉專《資安這條路,飛飛來領路》,分享文章與圖卡整理。
個人網站:feifei.tw 分享資安技術文章;pbtw.tw 分享 AI 相關應用;ssdlc.feifei.tw 分享軟體安全開發流程文章。