Find the security problems in a FastAPI reports endpoint

Backend Live Coding November 13, 2026 python

(1) SQL injection: an f-string inside text().

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# ======================================================================
# Q: Find the security problems in this code.
#
# (1) SQL injection: an f-string inside text(). For values I use bind parameters
# (:x). For column names, which cannot be bound, I use an allowlist. (2) SSRF:
# the user can pass http://169.254.169.254/ and get AWS credentials from the
# metadata service. It needs a domain allowlist, blocking of private IP ranges
# after DNS resolution, a timeout and a size limit. There is also no timeout at
# all. (3) The token ends up in the logs. (4) There is no auth and no tenant
# filter: it returns all reports of all customers, and SELECT * may expose
# internal fields.
# ======================================================================

@app.get('/v1/reports')
def reports(sort: str, url: str, session=Depends(get_session)):
    rows = session.execute(text(f'SELECT * FROM reports ORDER BY {sort}'))   # (1)
    preview = requests.get(url).text                                         # (2)
    logger.info('user token %s', request.headers['Authorization'])           # (3)
    return {'rows': [dict(r._mapping) for r in rows], 'preview': preview}   # (4)

SORTABLE = {'created': Report.created_at, 'name': Report.name}

@app.get('/v1/reports', response_model=list[ReportOut])
def reports(sort: Literal['created', 'name'] = 'created',
            user: User = Depends(require_permission('reports:read')),
            session: Session = Depends(get_session)):
    stmt = (select(Report).where(Report.tenant_id == user.tenant_id)
            .order_by(SORTABLE[sort].desc()).limit(100))
    return session.scalars(stmt).all()