phase 5 part 5: cs algorithms, network-os, web-security topics with tested examples; all 43 stacks pass --strict

This commit is contained in:
Shikui Wang committed 2026-10-23 16:47:11 +08:00
1 parent f695bbd7cc
commit 691f738f5f
22 files changed
+1955 -37

No files matched your search

@@ -0,0 +1,126 @@
# requires: fastapi, httpx
"""Web 安全示例:SQL 注入、XSS、CSRF、CORS、SSRF(Agent 的“抓取网页”工具最容易出这个问题)。
每类问题都给出“错误写法”和“正确写法”,test_secure_app.py 用测试证明错误写法可被利用、正确写法可以防住。
运行:pytest -q test_secure_app.py;直接 `python secure_app.py` 只做一次自检。
"""
from __future__ import annotations
import html
import ipaddress
import secrets
import socket
import sqlite3
from urllib.parse import urlsplit
from fastapi import Cookie, FastAPI, Form, Header, HTTPException, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
# ---------- SQL 注入 ----------
def make_db() -> sqlite3.Connection:
db = sqlite3.connect(":memory:", check_same_thread=False)
db.executescript("""
CREATE TABLE orders (id TEXT, tenant TEXT, amount INTEGER);
INSERT INTO orders VALUES ('A001', 't1', 100), ('A002', 't1', 200), ('B001', 't2', 999);
""")
return db
def find_orders_unsafe(db: sqlite3.Connection, tenant: str, keyword: str) -> list[tuple]:
# 错误:字符串拼接。keyword = "x' OR 1=1 --" 时会查出其他租户的数据
return db.execute(f"SELECT id, tenant FROM orders WHERE tenant = '{tenant}' AND id LIKE '%{keyword}%'").fetchall()
def find_orders_safe(db: sqlite3.Connection, tenant: str, keyword: str) -> list[tuple]:
# 正确:参数化查询,数据永远不会被当成 SQL 执行
return db.execute("SELECT id, tenant FROM orders WHERE tenant = ? AND id LIKE ?", (tenant, f"%{keyword}%")).fetchall()
# ---------- XSS ----------
def render_answer_unsafe(answer: str) -> str:
return f"<div class='answer'>{answer}</div>" # 错误:模型输出 / 用户输入直接拼进 HTML
def render_answer_safe(answer: str) -> str:
return f"<div class='answer'>{html.escape(answer)}</div>" # 正确:输出到 HTML 时转义
# ---------- SSRF ----------
ALLOWED_SCHEMES = {"http", "https"}
def check_fetch_url(url: str, resolve=socket.getaddrinfo) -> str:
"""Agent 抓取网页工具的 URL 校验:只允许 http(s),解析后的所有 IP 都必须是公网地址。"""
parts = urlsplit(url)
if parts.scheme not in ALLOWED_SCHEMES or not parts.hostname:
raise ValueError("只允许 http / https 地址")
if parts.username or parts.password:
raise ValueError("不允许在 URL 中携带凭证")
infos = resolve(parts.hostname, parts.port or (443 if parts.scheme == "https" else 80))
for info in infos:
ip = ipaddress.ip_address(info[4][0])
if not ip.is_global: # 拒绝内网、回环、链路本地(含云元数据地址 169.254.169.254)
raise ValueError(f"禁止访问内网地址 {ip}")
# 注意:真正发请求时还要禁止自动跟随重定向(或对每一跳重新校验),并固定使用这里解析出的 IP,防止 DNS 重绑定
return url
# ---------- 应用:CORS、CSRF、安全响应头 ----------
TRUSTED_ORIGINS = ["https://app.example.com"]
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=TRUSTED_ORIGINS, # 白名单;带凭证时绝不能用 "*"
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["Content-Type", "X-CSRF-Token"],
)
DB = make_db()
@app.middleware("http")
async def security_headers(request, call_next):
resp = await call_next(request)
resp.headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self'; object-src 'none'" # XSS 的第二道防线
resp.headers["X-Content-Type-Options"] = "nosniff"
return resp
@app.post("/login")
def login(response: Response) -> dict:
csrf = secrets.token_urlsafe(16)
# 会话 Cookie:HttpOnly(脚本读不到)、Secure、SameSite=Lax(跨站的 POST 不会带上)
response.set_cookie("session", "user-1", httponly=True, secure=True, samesite="lax")
# CSRF Token(双重提交):前端读取后放在请求头中,跨站页面拿不到这个值
response.set_cookie("csrf_token", csrf, secure=True, samesite="lax")
return {"ok": True}
@app.post("/transfer")
def transfer(amount: int = Form(...), session: str | None = Cookie(None), csrf_cookie: str | None = Cookie(None, alias="csrf_token"),
x_csrf_token: str | None = Header(None)) -> dict:
if not session:
raise HTTPException(401, "未登录")
if not csrf_cookie or not x_csrf_token or not secrets.compare_digest(csrf_cookie, x_csrf_token):
raise HTTPException(403, "CSRF 校验失败")
return {"ok": True, "amount": amount}
@app.get("/orders")
def orders(keyword: str = "") -> list[list[str]]:
return [list(r) for r in find_orders_safe(DB, "t1", keyword)]
@app.get("/answer", response_class=HTMLResponse)
def answer(q: str = "") -> str:
return render_answer_safe(f"你问的是:{q}")
if __name__ == "__main__":
payload = "x' OR 1=1 --"
assert {r[1] for r in find_orders_unsafe(DB, "t1", payload)} == {"t1", "t2"}
assert find_orders_safe(DB, "t1", payload) == []
assert "<script>" not in render_answer_safe("<script>alert(1)</script>")
print("自检通过:拼接 SQL 被注入读到了 t2 的数据,参数化查询没有;XSS 输出已转义")
@@ -0,0 +1,71 @@
# requires: fastapi, httpx
import pytest
from fastapi.testclient import TestClient
from secure_app import (DB, app, check_fetch_url, find_orders_safe, find_orders_unsafe, render_answer_safe,
render_answer_unsafe)
client = TestClient(app, base_url="https://testserver")
def test_sql_injection_unsafe_leaks_other_tenant():
rows = find_orders_unsafe(DB, "t1", "x' OR 1=1 --")
assert ("B001", "t2") in rows # 证明漏洞真实存在
def test_sql_injection_safe():
assert find_orders_safe(DB, "t1", "x' OR 1=1 --") == []
assert find_orders_safe(DB, "t1", "A00") == [("A001", "t1"), ("A002", "t1")]
def test_xss_escaped():
evil = '<img src=x onerror="alert(document.cookie)">'
assert "<img" in render_answer_unsafe(evil)
assert "<img" not in render_answer_safe(evil) and "&lt;img" in render_answer_safe(evil)
r = client.get("/answer", params={"q": "<script>alert(1)</script>"})
assert "<script>" not in r.text and "script-src 'self'" in r.headers["content-security-policy"]
def test_csrf():
c = TestClient(app, base_url="https://testserver")
c.post("/login")
# 模拟跨站伪造:浏览器会自动带上 Cookie,但攻击页面读不到 csrf_token,无法设置请求头
assert c.post("/transfer", data={"amount": 100}).status_code == 403
token = c.cookies.get("csrf_token")
r = c.post("/transfer", data={"amount": 100}, headers={"X-CSRF-Token": token})
assert r.status_code == 200
def test_cors_allowlist():
ok = client.get("/orders", headers={"Origin": "https://app.example.com"})
assert ok.headers.get("access-control-allow-origin") == "https://app.example.com"
evil = client.get("/orders", headers={"Origin": "https://evil.example"})
assert "access-control-allow-origin" not in evil.headers
pre = client.options("/orders", headers={"Origin": "https://evil.example", "Access-Control-Request-Method": "GET"})
assert pre.status_code == 400
def fake_resolve(mapping):
return lambda host, port: [(None, None, None, "", (mapping[host], port))]
@pytest.mark.parametrize("url,ip", [
("http://169.254.169.254/latest/meta-data/", "169.254.169.254"), # 云元数据
("http://localhost:6379/", "127.0.0.1"),
("http://intranet.corp/", "10.0.0.8"),
("http://rebind.example/", "192.168.1.10"), # 公网域名解析到内网
])
def test_ssrf_blocks_internal(url, ip):
host = url.split("/")[2].split(":")[0]
with pytest.raises(ValueError):
check_fetch_url(url, resolve=fake_resolve({host: ip}))
@pytest.mark.parametrize("url", ["file:///etc/passwd", "gopher://x/", "http://user:pw@example.com/"])
def test_ssrf_blocks_schemes_and_credentials(url):
with pytest.raises(ValueError):
check_fetch_url(url, resolve=fake_resolve({"example.com": "93.184.215.14", "x": "93.184.215.14"}))
def test_ssrf_allows_public():
assert check_fetch_url("https://example.com/a", resolve=fake_resolve({"example.com": "93.184.215.14"}))