ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FastAPI实战指南:从零搭建安全可靠的Python Web后端

FastAPI实战指南:从零搭建安全可靠的Python Web后端 1. 项目全景与整体设计去年下半年我接到一个内部业务系统的重构任务要求把原来堆在单体PHP里的功能拆出来用Python重写后端。项目不大不小大概二十来个接口包含用户认证、订单管理、文件上传、操作日志外加一套给前端同事用的管理后台API。我在技术选型上花了两天最终确定用FastAPI做核心框架配合SQLAlchemy 2.0操作PostgreSQL部署走Nginx Uvicorn。整个项目从零搭建到通过安全测试前后差不多六周。这篇文章就是把这次实战中踩过的坑、验证过有效的方案、以及我认为值得记录的基础要点一次性写清楚。如果你是一个刚接触Python Web后端的开发者或者正在准备做一个前后端分离的小型系统这篇文章能帮你少走很多弯路。文章涉及的内容包括环境搭建、项目结构设计、核心接口实现、认证鉴权、常见攻击防护、安全测试以及部署上线覆盖了一个真实Web后端从开发到上线的完整链路。我会把每一步“为什么这么做”也讲清楚而不是只丢给你一堆代码。1.1 为什么用FastAPI而不是Django或Flask先说选型。这个项目是前后端分离架构前端独立部署后端只提供JSON接口。Flask足够轻量但很多东西要自己搭JWT、参数校验、接口文档、异步支持都得靠第三方库拼凑后期维护成本不低。Django功能全家桶很全自带Admin和ORM但对于纯API项目来说偏重而且我团队里几个同事对Django的MTV模式并不熟悉学习曲线反而更陡。FastAPI的优势在于三个点第一原生异步支持面对IO密集型的数据库查询和外部API调用表现更好第二基于Pydantic的请求参数校验声明好类型就能自动完成校验和错误提示省掉大量手写判断第三自动生成Swagger文档前后端联调时直接拿浏览器打开/docs接口参数、返回结构一目了然节省了大量沟通成本。选型确实需要结合团队情况。如果你的团队擅长Django或者项目需要Admin后台、复杂ORM关系映射Django依然是很好的选择。但就我这个项目而言FastAPI的性价比最高。1.2 整体架构与模块划分项目规模不大但我还是按标准的分层架构来组织代码避免后期膨胀难维护。简单画一下结构app/ ├── api/ # 路由层接收请求、返回响应 │ ├── v1/ # 版本控制后续可扩展 v2 │ │ ├── auth.py # 登录、刷新Token │ │ ├── users.py # 用户管理 │ │ ├── orders.py # 订单相关 │ │ └── files.py # 文件上传下载 ├── core/ # 配置、安全、依赖 │ ├── config.py # 环境变量读取 │ ├── security.py # JWT、密码哈希 │ └── deps.py # 依赖注入如当前用户 ├── models/ # SQLAlchemy ORM模型 ├── schemas/ # Pydantic模型请求/响应结构 ├── services/ # 业务逻辑层 ├── utils/ # 通用工具 └── main.py # 应用入口这个结构的核心思想是路由层只负责HTTP层面的参数接收和响应返回业务逻辑放在service层数据操作集中在model层谁也不越界。搜索引擎、中间件、日志这些横切关注点再单独放core或utils。实际开发中最大的好处是出了问题能快速定位改起来也放心。1.3 前端联动方式RESTful API 设计约定前后端分离项目最怕接口设计混乱。我们定了几条简单约定执行下来效果不错资源用名词复数不用动词例如POST /orders创建订单GET /orders/{order_id}查询订单状态码严格区分200表示成功400表示参数错误401表示未认证403表示无权限404表示资源不存在500表示服务端错误返回体统一包裹一层{code: 0, message: success, data: {...}}成功时code为0失败时为错误码分页统一使用page和page_size参数返回{items: [...], total: 100}规则不多但前后端联调时几乎没吵过架。前端拿到任何响应先看code再取data错误处理逻辑高度统一。2. 环境准备与工程搭建2.1 Python环境安装与虚拟环境隔离这个项目我用的Python 3.11。如果你的机器上还没有Python环境建议直接去官网下载最新稳定版安装时记得勾选“Add Python to PATH”。Windows用户尤其注意安装完打开命令行执行python --version如果能正常输出版本号说明环境变量没问题。强烈建议每个项目创建独立的虚拟环境不要图省事直接用全局Python。一个真实教训我同事一开始图省事全局环境里装了几十个包后来升级依赖直接把另一个项目搞崩了排查了一天才发现是版本冲突。用虚拟环境隔离每个项目各装各的依赖互不干扰。创建虚拟环境很简单# Windows python -m venv venv venv\Scripts\activate # Linux / macOS python3 -m venv venv source venv/bin/activate激活后命令行前缀会出现(venv)此时pip install装的所有包都只属于当前环境。依赖管理我用requirements.txt装完新包顺手执行pip freeze requirements.txt导出换机器部署时直接pip install -r requirements.txt一键还原。等包数量到30个以上可以考虑用Poetry管理项目初期requirements.txt完全够用。2.2 项目初始化与关键依赖清单我用pip install安装以下核心依赖pip install fastapi uvicorn[standard] sqlalchemy psycopg2-binary pydantic-settings python-jose[cryptography] passlib[bcrypt] python-multipart alembic逐个说明作用fastapiWeb框架本身uvicorn[standard]ASGI服务器负责运行FastAPI应用sqlalchemyORM操作数据库psycopg2-binaryPostgreSQL驱动pydantic-settings读取环境变量和配置python-joseJWT生成和校验里面的cryptography扩展用于签名算法passlib[bcrypt]密码哈希python-multipart处理文件上传和表单数据alembic数据库迁移工具框架装好后创建一个最简单的入口文件验证环境是否正常from fastapi import FastAPI app FastAPI(titleMy API) app.get(/health) def health_check(): return {status: ok}在项目根目录执行uvicorn main:app --reload浏览器访问http://127.0.0.1:8000/health看到{status:ok}说明环境跑通了。2.3 配置管理环境变量与配置分离配置管理是很多人容易忽略的环节。所谓配置就是数据库连接串、密钥、Token有效期、允许的域名列表等这些因环境而异的参数。这些信息绝对不能硬编码在代码里否则换环境部署时改代码极其痛苦而且敏感信息容易泄露。我用.env文件管理环境变量并在代码里用Pydantic的BaseSettings读取from pydantic_settings import BaseSettings class Settings(BaseSettings): app_name: str My API database_url: str postgresql://user:passlocalhost/mydb jwt_secret_key: str change-me jwt_algorithm: str HS256 jwt_expire_minutes: int 30 cors_origins: list[str] [http://localhost:5173] class Config: env_file .env settings Settings().env文件内容示例DATABASE_URLpostgresql://myuser:mysecretlocalhost:3306/mydb JWT_SECRET_KEYyour-long-random-string CORS_ORIGINS[http://localhost:5173,http://example.com].env文件要加入.gitignore绝不能提交到代码仓库。配置里最不能省的就是jwt_secret_key这个是Token签名的密钥泄露了等于任何人都能伪造登录状态。生产环境的密钥需要随机生成长度至少32位我习惯用secrets.token_hex(32)来产生。3. 核心功能实现从路由到业务闭环3.1 数据库建模与迁移数据库表设计遵循“先建模再迁移”的流程。我用SQLAlchemy定义ORM模型然后通过Alembic生成迁移脚本。以订单表为例from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Numeric, Enum from sqlalchemy.orm import declarative_base, relationship from datetime import datetime Base declarative_base() class Order(Base): __tablename__ orders id Column(Integer, primary_keyTrue, indexTrue) order_no Column(String(32), uniqueTrue, indexTrue, nullableFalse) user_id Column(Integer, ForeignKey(users.id), nullableFalse) amount Column(Numeric(10, 2), nullableFalse) status Column(Enum(pending, paid, cancelled, nameorder_status), defaultpending) created_at Column(DateTime, defaultdatetime.now) updated_at Column(DateTime, defaultdatetime.now, onupdatedatetime.now) user relationship(User, back_populatesorders)有几个设计细节值得讲。order_no加唯一索引业务上订单号必须全局唯一防止并发重复创建。amount用Numeric(10, 2)而不是浮点数金额精度问题在电商系统里出过事故浮点计算误差会导致对账不平必须用定点数。created_at和updated_at是审计字段几乎每张业务表都该有排查问题时能提供关键时间线索。写完模型后执行alembic init migrations alembic revision --autogenerate -m create orders table alembic upgrade headAlembic的自动生成基于模型和数据库的差异对比但不完全可靠复杂迁移比如改字段类型、删除字段生成后要人工审查一遍。我踩过的坑是--autogenerate生成了一些预期外的索引变更如果直接跑上线大数据量下会锁表所以迁移脚本一定要逐行检查。3.2 参数校验与序列化Pydantic的威力FastAPI的请求参数校验依赖Pydantic模型这也是我选这个框架的重要原因。举个例子创建订单接口的请求体定义from pydantic import BaseModel, Field class OrderCreate(BaseModel): product_id: int Field(..., gt0) quantity: int Field(..., gt0, le999) address: str Field(..., min_length5, max_length200) class OrderResponse(BaseModel): id: int order_no: str amount: float status: str前端传过来的JSON会先经过Pydantic校验。比如quantity传了0框架会自动返回422错误并指明字段和失败原因不需要在路由函数里写任何if not quantity之类的判断。这样不仅代码更干净接口文档也会自动生成每种参数的类型和约束。Pydantic模型同时兼任响应模型。路由函数标注返回类型为OrderResponse后FastAPI会对返回数据做序列化和过滤多余字段不会暴露给前端。特别是ORM查询出来的对象里可能带着关联关系、内部状态如果不做这层过滤很容易把敏感信息泄漏出去。我见过有项目直接把SQLAlchemy对象返回给前端结果用户密码哈希都跟着序列化出去了非常可怕。3.3 JWT认证流程实现用户认证我采用JWT方案。原理简单说就是用户登录成功后服务端签发一个加密的Token给客户端客户端后续请求带上这个Token服务端验签通过就认为是已认证用户。Token本身不存储在服务端天然适合无状态的水平扩展。登录接口核心代码from datetime import datetime, timedelta from jose import jwt def create_access_token(user_id: int, username: str) - str: expire datetime.now() timedelta(minutessettings.jwt_expire_minutes) payload {sub: str(user_id), username: username, exp: expire} return jwt.encode(payload, settings.jwt_secret_key, algorithmsettings.jwt_algorithm)这里sub是JWT标准里的主体字段我存用户IDexp是过期时间。注意exp必须用UTC时间计算我一开始用本地时间部署到服务器后时区不一致Token总是提前或延迟过期排查了半天才发现问题。密码存储使用passlib库的bcrypt算法from passlib.context import CryptContext pwd_context CryptContext(schemes[bcrypt], deprecatedauto) def hash_password(password: str) - str: return pwd_context.hash(password) def verify_password(plain_password: str, hashed_password: str) - bool: return pwd_context.verify(plain_password, hashed_password)这里要强调数据库里存的永远是密码的哈希值不是明文。bcrypt每次哈希会随机加盐所以同一个密码两次哈希的结果不同但验证函数仍能正确判断。明文存密码的项目一旦数据库泄露就是全量撞库事故这个底线不能碰。依赖注入在FastAPI里用起来很顺手。受保护的接口只需要在参数里声明一个current_user依赖框架会先执行依赖函数完成Token校验再进入路由逻辑from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials security HTTPBearer() def get_current_user(credentials: HTTPAuthorizationCredentials Depends(security)): token credentials.credentials try: payload jwt.decode(token, settings.jwt_secret_key, algorithms[settings.jwt_algorithm]) user_id int(payload.get(sub)) except Exception: raise HTTPException(status_codestatus.HTTP_401_UNAUTHORIZED, detail无效或过期的Token) return get_user_by_id(user_id)路由中使用app.get(/users/me) def get_my_profile(current_user: User Depends(get_current_user)): return current_user整体认证链路的颗粒度可以画成一句话登录拿Token请求带Token依赖验Token业务拿用户。四个环节互相独立后面做权限控制、Token刷新都围绕这条链路扩展。3.4 文件上传与安全校验项目中有一个证书附件上传的需求。文件上传类接口有两个安全重点文件类型校验和路径穿越防护。import os from fastapi import UploadFile UPLOAD_DIR uploads ALLOWED_EXTENSIONS {.pdf, .jpg, .png} async def save_upload_file(file: UploadFile): ext os.path.splitext(file.filename)[1].lower() if ext not in ALLOWED_EXTENSIONS: raise HTTPException(status_code400, detail不支持的文件类型) # 用uuid重命名避免使用用户原始文件名 import uuid new_filename f{uuid.uuid4().hex}{ext} file_path os.path.join(UPLOAD_DIR, new_filename) # 校验最终路径确实在UPLOAD_DIR内防止路径穿越 real_path os.path.realpath(file_path) if not real_path.startswith(os.path.realpath(UPLOAD_DIR)): raise HTTPException(status_code400, detail非法路径) contents await file.read() with open(real_path, wb) as f: f.write(contents) return {filename: new_filename}文件上传的典型攻击有两个。一个是超长文件名或恶意构造的路径如../../etc/passwd导致路径穿越服务器上任意文件被覆盖。解决核心就是重命名和路径二次校验不信用户的任何输入。另一个是可执行文件上传比如上传.php、.jsp后配合Web容器解析漏洞直接getshell。所以上传目录不能放在Web根目录下且通过Nginx禁止执行上传目录里的脚本文件。3.5 跨域配置与前后端联调前后端分离开发阶段的头号问题是跨域。前端跑在5173端口后端跑在8000端口浏览器会拦截跨源请求。FastAPI通过CORS中间件解决from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_originssettings.cors_origins, allow_credentialsTrue, allow_methods[*], allow_headers[*], )allow_origins必须明确写允许的前端地址清单不要直接写[*]。如果你用了allow_credentialsTrue即允许携带Cookie通配符*是不被允许的浏览器会直接拒绝。生产环境最好由Nginx统一配置响应头应用层只负责业务。联调过程中我还处理过一个容易忽略的问题前端发来的是OPTIONS预检请求有些兼容性差的浏览器或代理可能在预检阶段就丢掉了。排查思路是先确认服务器是否返回了正确的Access-Control-Allow-Origin响应头再确认预检请求的响应码是200而不是500。4. 安全体系建设认证、防护与测试4.1 常见Web攻击与应对方案Web后端开发绕不开安全这个话题尤其是暴露在公网的服务。我按OWASP Top 10的思路把常见攻击类型逐一列出来结合本次项目的防护实践说明。SQL注入危害最大的漏洞之一。攻击者在输入参数中拼接SQL片段尝试操纵数据库查询。比如登录接口如果直接拼接字符串# 错误写法严禁使用 sql fSELECT * FROM users WHERE username {username} AND password {password}攻击者输入admin --就可能绕过密码验证。防护方案有两个一是使用ORM或参数化查询SQLAlchemy的select()构造会自动参数化二是严格限制数据库账号权限应用账号只授INSERT、UPDATE、DELETE、SELECT权限不要用超级管理员连接数据库。XSS跨站脚本攻击攻击者在输入中注入恶意脚本在用户浏览器上执行。FastAPI返回JSON数据一般不会直接触发XSS但前端如果使用v-html渲染后端返回的内容就会中招。防护核心是前端输出编码后端建议配置CSP内容安全策略响应头Content-Security-Policy: default-src self; script-src self; object-src noneCSRF跨站请求伪造攻击者诱导已登录用户访问恶意网站伪造请求到目标站点。前后端分离架构使用Token认证的话CSRF风险相对较低因为Token放在请求头而不是自动携带的Cookie里。如果使用Cookie做会话必须在服务端验证Origin或Referer头并使用CSRF Token。权限绕过这是我在安全测试中重点检查的环节。常见问题包括用户A能访问用户B的订单详情、未登录用户可以调用管理接口。解决方案是层层校验接口层判断是否登录业务层判断数据归属权数据层用多租户过滤器限制查询结果。比如订单详情接口查询时强制加user_idcurrent_user.id条件而不是查出订单后对比order db.query(Order).filter(Order.id order_id, Order.user_id current_user.id).first()如果查不到就直接404攻击者无法靠遍历ID探测他人数据。暴力破解与接口滥用登录接口最容易被拿来撞库、爆破。我在登录接口上加了简单的限流同一IP每分钟最多尝试5次超过后返回429并锁定一段时间。FastAPI生态中可以用slowapi也可以自己在Redis里维护计数。没有Redis的小项目用进程内字典也能挡掉大部分脚本攻击。4.2 密码存储与敏感数据保护前面提到密码用bcrypt哈希存储这里说几个实践细节。bcrypt设计上自带工作因子推荐值为12到14。工作因子越高哈希计算耗时越长攻击者暴力破解的成本也越高但用户登录体验会变差。我的经验是12比较均衡单次哈希约0.2秒。生产环境想提高安全性就逐步调因子但要注意新旧哈希格式的兼容性passlib会根据哈希字符串自动判断版本所以升级不会导致老用户登录失败。Token的存放也有讲究。前端不能把JWT放进localStorage因为任何XSS漏洞都能偷走它。更安全的方式是放在内存变量里配合刷新Token轮换。小项目图省事可以把Token放内存刷新接口不强制持久化刷新页面后用刷新Token换取新Token这样Token不会出现在任何持久化存储中。敏感数据还要区分“存储加密”和“传输加密”。传输加密指HTTPS存储加密指数据库中的敏感字段加密。本次项目中用户的手机号、身份证号属于敏感信息我用AES对称加密后存储接口返回时按需解密。虽然多了一个加解密步骤但数据库文件被拖走时攻击者拿不到真实数据这一步对用户隐私配合平台风险控制很有价值。4.3 HTTPS证书配置与常见问题部署阶段我踩过不少证书相关的坑。HTTPS是现代Web应用的基本配置浏览器已经对非HTTPS网站打上“不安全”标签同时很多浏览器能力如API、传感器、语音识别强制要求安全上下文。这套环境的证书是用内网证书颁发机构申请的中间经历了一次“证书链不完整”的坑。部署Nginx时配置证书就三个关键指令ssl_certificate /etc/nginx/certs/server.crt; ssl_certificate_key /etc/nginx/certs/server.key; ssl_protocols TLSv1.2 TLSv1.3;配置完成后用在线检测工具或OpenSSL验证curl -v https://api.example.com/health如果浏览器报“此网站无法提供安全连接发送的响应无效”多数情况是证书链不完整。服务端发了叶子证书但没有带上中间证书浏览器无法构建信任链。解决方法是把中间证书和叶子证书串在同一个文件里顺序为叶子证书在前中间证书在后。如果Nginx配置了ssl_trusted_certificate指令确保指向正确的CA证书包。处理完证书链别忘了配置HSTS响应头强制浏览器后续都走HTTPSadd_header Strict-Transport-Security max-age31536000; includeSubDomains always;4.4 安全测试方法与漏洞排查我的习惯是上线前做一轮完整的黑盒安全测试重点不放在用什么商业扫描器上而是人工围绕业务逻辑做测试。登录和注册是重灾区我会逐一验证弱密码策略是否生效长度、复杂度多次错误密码是否触发锁定或限流登录成功和失败是否能被日志记录登录接口是否泄露“用户不存在”这种信息攻击者可以用来枚举账号业务接口方面越权测试修改请求中的ID看能否访问不属于自己的资源重复提交测试快速点击多次下单按钮看是否产生多条订单参数边界测试提交超大数值、超长字符串、负数数量看后端是否有约束我还习惯抓包检查一下敏感字段是否出现在请求日志里。有一次发现登录接口打日志时把完整的手机号和密码哈希都打印出来了这是隐患日志文件一旦被读取等于密码哈希直接泄露。后来统一加了一层脱敏工具所有日志输出经过脱敏后再落盘用户密码类字段完全不记录。5. 常见问题排查与优化建议5.1 CORS跨域报错的快速定位开发中最常见的报错之一就是浏览器控制台提示Blocked by CORS policy。按照以下顺序排查基本都能解决确认后端是否配置了allow_origins且包含前端请求的完整源协议域名端口确认后端返回的响应头里确实有Access-Control-Allow-Origin字段可以在Chrome DevTools的Network面板查看确认预检请求OPTIONS成功返回2xx如果使用了allow_credentialsTrue检查allow_origins是否用了通配符*两者不能共存个别情况是浏览器缓存了旧的CORS策略无痕模式试一下就能排除。5.2 500错误与日志排查后端返回500是最着急的因为什么都没返回。我的排查流程是先看日志再复现请求最后定位代码行。项目里我配置了结构化日志每条请求记录包括方法、路径、状态码、耗时、客户端IP、用户ID。线上出问题时直接按时间窗和用户ID过滤日志能快速还原现场。还有一个被很多人忽略的操作——给FastAPI配置exception handler把未捕获的异常统一转成JSON响应同时打印完整堆栈app.exception_handler(Exception) async def unhandled_exception_handler(request, exc): logger.exception(Unhandled exception, exc_infoexc) return JSONResponse(status_code500, content{detail: 内部错误})这样好处是前端不会莫名其妙收到HTML错误页服务端也能保留完整的堆栈信息。注意生产环境不要把堆栈发给客户端防止暴露代码结构。5.3 接口性能优化缓存与异步系统上线后遇到一个性能瓶颈订单列表页的统计接口要扫全表前端每次加载都要等两三秒。优化方向有两个对于读多写少的数据用缓存对于耗时计算用异步。我用Redis做了热点数据的缓存订单列表的基础信息缓存30秒缓存命中时直接返回数据库压力骤降。实现初版很简单import redis r redis.Redis(hostlocalhost, port6379, decode_responsesTrue) def get_orders_with_cache(user_id: int, page: int, page_size: int): cache_key forders:{user_id}:{page}:{page_size} cached r.get(cache_key) if cached: return json.loads(cached) data query_orders_from_db(user_id, page, page_size) r.setex(cache_key, 30, json.dumps(data)) return data缓存要特别注意失效策略如果业务上有用户修改订单后立刻要看最新状态30秒的缓存会导致数据不一致。针对这种场景我改成了“先更新数据库再删除缓存”下次请求重新回源查询这是缓存中最常用也相对可靠的模式。5.4 安全加固响应头、限流与日志审计上线前的最后一道工序是加固响应头。我在Nginx层统一配置了几个安全响应头浏览器拿到这些头会开启额外的安全策略add_header X-Frame-Options SAMEORIGIN always; add_header X-Content-Type-Options nosniff always; add_header Referrer-Policy no-referrer always;X-Frame-Options防点击劫持禁止页面被嵌套到恶意网站iframe中。X-Content-Type-Options防止浏览器对响应内容做MIME嗅探。Referrer-Policy控制跳转时是否携带来源地址。日志审计方面所有涉及认证和敏感操作的接口都记录操作日志包括用户ID、操作时间、请求参数、结果状态。这些日志保留90天既能做安全回溯也方便业务上追责。日志级别要控制好不要把密码、Token、完整手机号写进去否则等于给攻击者送弹药。5.5 部署上线与后续维护部署我用的是一台Linux服务器架构是Nginx反向代理到Uvicorn进程。Uvicorn处理高并发的能力不如Gunicorn多进程模式所以生产环境用Gunicorn加Uvicorn workergunicorn main:app \ -w 4 \ -k uvicorn.workers.UvicornWorker \ --bind 127.0.0.1:8000 \ --timeout 60 \ --access-logfile /var/log/api/access.log \ --error-logfile /var/log/api/error.log-w 4表示4个worker进程我根据服务器CPU核数定的一般取2n1n为核数。--timeout 60防止慢接口长时间占用worker但具体数值要看业务如果接口本身处理时间超过60秒需要调大。Nginx反向代理配置server { listen 443 ssl; server_name api.example.com; ssl_certificate /etc/nginx/certs/api.example.com.crt; ssl_certificate_key /etc/nginx/certs/api.example.com.key; ssl_protocols TLSv1.2 TLSv1.3; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }部署完成后我用Supervisor做进程守护进程挂掉自动拉起同时每天备份数据库。上线一个月内我基本每周抽查一次错误日志和安全日志前两周确实发现了几次异常登录尝试配合限流措施都拦住了。后续我又加了一个简单的IP白名单功能内网IP访问后台管理接口不受限流影响来自外部IP的管理类请求直接拦截并告警。写在后面的一些实操体会做这个项目的过程中我最有感触的一点是安全不能靠“添加功能”来实现而要从设计阶段就嵌入每一个环节。JWT放在请求头而不是Cookie里、文件上传重命名、日志脱敏、数据库账号最小权限这些决策都是在动手写代码之前就已经确定的。等到上线前再补救安全漏洞付出的成本会是指数级的。对刚开始接触Python Web后端的开发者我的建议是先从小项目完整走一遍这个流程搭环境、建表、写接口、做认证、配部署。走完一遍之后再回过头来研究为什么会踩这些坑比如为什么CSRF在Token方案下不那么致命为什么bcrypt的哈希体验比MD5好那么多。理解背后的原理比记住一堆“最佳实践”清单更有用。最后再分享一个小技巧接口开发时把debugTrue打开FastAPI报错页面会把完整的调用栈展示出来开发期定位问题非常方便。但上线前一定要关掉否则服务器路径、代码逻辑、环境变量都可能暴露给访问者。我的做法是把配置里的debug项用一个环境变量控制本地开发设True生产环境设False同时加一段进程启动时的断言如果生产环境开着debug就拒绝启动。这个“保险丝”帮我避免过好几次低级事故。
返回列表