惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
Recent Announcements
Recent Announcements
雷峰网
雷峰网
The GitHub Blog
The GitHub Blog
罗磊的独立博客
月光博客
月光博客
J
Java Code Geeks
A
About on SuperTechFans
Microsoft Security Blog
Microsoft Security Blog
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
U
Unit 42
C
Check Point Blog
Martin Fowler
Martin Fowler
有赞技术团队
有赞技术团队
博客园 - 叶小钗
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志
MyScale Blog
MyScale Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I scanned FastAPI's tutorial examples. Here's what I found.
Moon sehwan · 2026-06-23 · via DEV Community

Moon sehwan

FastAPI's official docs are beautiful. I love them.

So I scanned them through AINAScan.

Here's what I found.


The Setup

FastAPI's tutorial examples are designed to teach. They're intentionally simplified. That's not a criticism — it's a design choice.

But I wanted to know: when someone copies those examples directly into a production app (which happens constantly), what's the actual risk profile?

I ran the examples through AINAScan, which tracks taint across variable assignments and detects 48 patterns across 9 languages. Here are the results.


Finding 1: The Classic SQL Injection Teaching Example

# From FastAPI's SQL tutorial (simplified)
from fastapi import FastAPI
import sqlite3

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: str):
    conn = sqlite3.connect("sql_app.db")
    user = conn.execute(
        f"SELECT * FROM users WHERE id = '{user_id}'"
    ).fetchone()
    return {"user": user}

AINAScan result:

BLOCK: SQL_INJECTION_RISK  L5  →  f-string in execute()
       taint: user_id (path param) → SQL query string
       Score deduction: -28 pts

The tutorial goes on to show SQLAlchemy (the right way), but the raw sqlite3 example is what gets copied first. The f-string SQL stays in the codebase. The SQLAlchemy refactor gets marked as "TODO."

Fix:

user = conn.execute(
    "SELECT * FROM users WHERE id = ?", (user_id,)
).fetchone()


Finding 2: The async def Trap

FastAPI makes async look easy. Which causes this:

@app.post("/process")
async def process_file(file: UploadFile):
    content = file.read()           # blocks
    result = heavy_computation(content)  # blocks
    db.save(result)                 # blocks
    return {"status": "done"}

AINAScan result:

WARN: FAKE_ASYNC  L2  →  async def with no await
      All calls are synchronous — blocks the event loop
      Score deduction: -6 pts

The function is async in name only. Under load, this serializes every request. FastAPI even documents this — use def for blocking operations, async def only when you actually await. But the template makes everything async by default.

Fix:

@app.post("/process")
def process_file(file: UploadFile):  # regular def = FastAPI runs in threadpool
    content = file.read()
    result = heavy_computation(content)
    db.save(result)
    return {"status": "done"}


Finding 3: The Save That Saves Nothing

This one shows up in almost every vibe-coded FastAPI app:

@app.post("/users/")
async def create_user(user: UserCreate):
    # "Save" user
    new_user = {
        "id": generate_id(),
        "name": user.name,
        "email": user.email
    }
    return new_user  # returns the dict but never stores it

AINAScan result:

BLOCK: MISSING_WRITE  L8  →  create_user() has no DB write
       Function name implies persistence, no INSERT/save found
       Score deduction: -10 pts

The function looks complete. It takes a UserCreate model, generates an ID, returns a response. But nothing was saved anywhere. The next request has no memory of this user.

This is the defining vibe-coding bug: it looks like it works because it returns a 200 with data. It only fails when you try to retrieve the user later.


Finding 4: Hardcoded Development Credentials

Found across multiple tutorial snippets and community examples:

DATABASE_URL = "postgresql://postgres:admin123@localhost/myapp"
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"

AINAScan result:

BLOCK: HARDCODED_SECRET  L1,L2,L3
       Variables: DATABASE_URL, SECRET_KEY
       Score deduction: -22 pts (first), -13.2 pts (second)

The tutorial context is clear: these are examples. But SECRET_KEY = "09d25e094..." from the FastAPI JWT tutorial is one of the most Googled strings in Python. It's in production codebases right now.

Fix:

import os
DATABASE_URL = os.environ["DATABASE_URL"]
SECRET_KEY = os.environ["SECRET_KEY"]


The Score

If I assembled these four patterns into a single file and scanned it:

HARDCODED_SECRET (2x)   → -22 + -13.2 = -35.2
SQL_INJECTION_RISK       → -28.0
MISSING_WRITE            → -10.0
FAKE_ASYNC               →  -6.0

Starting score: 100
Final score:     20.8 → Grade D 😱

A D. Built entirely from official tutorial copy-paste.


Why This Happens

FastAPI tutorials optimize for teaching concepts, not production safety. That's correct — teaching should minimize noise.

The problem is the copy-paste gap. Between "this is for illustration" and "this code runs on my server" there's no friction. The tutorial doesn't stop you.

Three things that would help:

  1. Security comments in examples# NEVER USE F-STRINGS HERE — use parameterized queries
  2. Pre-commit hooks — catch these before they hit main
  3. Automated scanningAINAScan runs in under 3 seconds

Try It

Paste your FastAPI routes at AINAScan. Free, no signup.

Or curl it:

curl -X POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan \
  -H 'X-API-Key: vg_free_test' \
  -F 'file=@main.py'

What's your FastAPI app's score? Drop it in the comments.


AINAScan: 48 patterns · 9 languages · github.com/moonsehwan/aina-scan