Kem Intensif Pembangunan Bahagian Belakang FastAPI · Pelajaran

Mengurangkan 10 Ancaman Keselamatan API Teratas OWASP

Petakan ancaman API lazim kepada pertahanan FastAPI khusus untuk pengesahan rosak, BOLA dan tugasan besar-besaran.

Pelajaran 1 daripada 413 langkah

Mengurangkan 10 Ancaman Keselamatan API Teratas OWASP ialah pelajaran Kem Intensif Pembangunan Bahagian Belakang FastAPI percuma di CoddyKit. Ini ialah pelajaran 1 daripada 4. Sebanyak 3 pelajaran dalam laluan pembelajaran ini boleh dibaca sepenuhnya secara percuma — selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan praktikal dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Kem Intensif Pembangunan Bahagian Belakang FastAPI, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Kem Intensif Pembangunan Bahagian Belakang FastAPI merangkumi sejumlah 4 pelajaran.

Mengapa OWASP API Security Top 10 Penting

OWASP API Security Top 10 ialah senarai terpilih risiko keselamatan paling kritikal yang dihadapi oleh API moden. Berbeza daripada OWASP Top 10 klasik untuk aplikasi web, senarai ini memberi tumpuan khusus kepada permukaan serangan API — termasuk penyalahgunaan pengesahan, pendedahan data berlebihan dan kelemahan penetapan massa.

FastAPI ialah rangka kerja yang berkuasa, tetapi ia tidak menjadikan API anda selamat secara lalai. Anda mesti menerapkan pertahanan dengan sengaja pada setiap lapisan: penghalaan, pengesahan, pengesahan identiti dan pensirian.

Dalam pelajaran ini, kita memberi tumpuan kepada tiga kategori berimpak tinggi:

  • API1 — Broken Object Level Authorization (BOLA)
  • API2 — Broken Authentication
  • API6 — Mass Assignment

Setiap satunya mempunyai corak serangan yang tersendiri dan pengurangan risiko FastAPI yang nyata serta boleh anda gunakan hari ini.

Pengesahan Rosak: Permukaan Serangan

Broken Authentication (API2) berlaku apabila API gagal mengesahkan dengan betul bahawa pemanggil ialah orang yang didakwanya. Kegagalan yang biasa termasuk:

  • Menerima JWT yang telah tamat tempoh atau diubah suai tanpa pengesahan tandatangan
  • Menggunakan rahsia yang lemah atau boleh dijangka untuk menandatangani token
  • Tidak menguatkuasakan tempoh tamat token (tuntutan exp)
  • Membenarkan percubaan log masuk tanpa had (tiada had kadar)

Dalam FastAPI, corak yang paling boleh dipercayai ialah mengesahkan JWT dengan pustaka seperti python-jose atau PyJWT pada setiap laluan terlindung — menggunakan kebergantungan yang disuntik melalui Depends().

Pendekatan kebergantungan memusatkan logik pengesahan supaya anda tidak terlupa menerapkannya secara tidak sengaja pada laluan baharu.

Melaksanakan Kebergantungan Pengesahan JWT

Di bawah ialah kebergantungan JWT bergaya pengeluaran untuk FastAPI. Ia mengesahkan tandatangan token, menyemak tuntutan exp dan mencetuskan 401 bagi sebarang kegagalan. Suntikkannya ke dalam setiap laluan yang memerlukan pengesahan identiti.

Titik utama untuk memperkukuh keselamatan:

  • algorithms=[ALGORITHM] — benarkan secara jelas hanya HS256 (atau RS256); jangan sekali-kali hantar algorithms=None
  • Rahsia mesti datang daripada pemboleh ubah persekitaran, bukan dikodkan secara keras
  • Tuntutan sub yang tiada atau tidak sah dianggap sebagai token tidak sah
import os
from datetime import datetime, timezone
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt

SECRET_KEY = os.environ["JWT_SECRET_KEY"]  # never hardcode
ALGORITHM = "HS256"

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")


def get_current_user_id(token: str = Depends(oauth2_scheme)) -> int:
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        user_id: str = payload.get("sub")
        if user_id is None:
            raise credentials_exception
        return int(user_id)
    except JWTError:
        raise credentials_exception

BOLA: Keizinan Tahap Objek Rosak

BOLA (API1) ialah kelemahan API nombor satu di seluruh dunia. Ia berlaku apabila pemanggil boleh mengakses mana-mana objek hanya dengan meneka ID-nya — kerana pelayan tidak pernah menyemak pemilikan.

Corak klasik yang terdedah:

GET /orders/9871

Jika pelayan mendapatkan pesanan hanya berdasarkan order_id lalu mengembalikannya, mana-mana pengguna yang disahkan boleh membaca mana-mana pesanan dalam pangkalan data hanya dengan menambah nilai ID.

Penyelesaiannya sentiasa sama: selepas mendapatkan sumber, bandingkan pemilik sumber dengan pemanggil yang disahkan. Jika kedua-duanya tidak sepadan, pulangkan 403 Forbidden — bukannya 404, yang dalam sesetengah konteks boleh membocorkan maklumat tentang kewujudan sumber.

Membetulkan BOLA dalam Laluan FastAPI

Coraknya: dapatkan objek, kemudian sahkan pemilikan. Jangan sekali-kali melangkau semakan pemilikan kerana anda menganggap ID sukar diteka — UUID bukan kawalan keselamatan.

Perhatikan bahawa get_current_user_id daripada babak sebelumnya disuntik melalui Depends(). Pengesahan pemilikan ialah satu pernyataan if, tetapi ia merupakan baris paling penting dalam fungsi tersebut.

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.auth import get_current_user_id
from app.db import get_db
from app.models import Order

router = APIRouter()


@router.get("/orders/{order_id}")
async def get_order(
    order_id: int,
    current_user_id: int = Depends(get_current_user_id),
    db: AsyncSession = Depends(get_db),
):
    order = await db.get(Order, order_id)
    if order is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not found")

    # BOLA fix: verify the caller owns this resource
    if order.user_id != current_user_id:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied")

    return order

Menulis Pengawal BOLA yang Boleh Digunakan Semula

Apabila API anda mempunyai berpuluh-puluh titik akhir sumber, mengulangi semakan pemilikan secara sebaris mudah menyebabkan ralat. Ekstrakkannya ke dalam pembantu yang boleh digunakan semula dan mencetuskan pengecualian secara automatik. Ini mengurangkan kemungkinan pembangun terlupa melakukan semakan pada laluan baharu.

Pembantu ini cukup umum untuk berfungsi dengan mana-mana model SQLAlchemy yang mempunyai atribut user_id. Untuk peraturan pemilikan yang lebih kompleks (contohnya, akses berasaskan pasukan), luaskan fungsi ini dengan pertanyaan peranan/keizinan.

from fastapi import HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession


async def get_owned_resource(model_class, resource_id: int, user_id: int, db: AsyncSession):
    """
    Fetch a resource and assert ownership in one call.
    Raises 404 if not found, 403 if the caller does not own it.
    """
    resource = await db.get(model_class, resource_id)
    if resource is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"{model_class.__name__} not found",
        )
    if resource.user_id != user_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You do not have permission to access this resource",
        )
    return resource

Penetapan Massa: Serangan

Penetapan Pukal (API6) berlaku apabila API memetakan medan badan permintaan kepada model pangkalan data secara membuta tuli, lalu membolehkan penyerang menetapkan medan yang tidak pernah dimaksudkan untuk dikawal oleh pengguna.

Contoh klasik: pengguna mengemas kini profil mereka dan memasukkan "is_admin": true atau "balance": 99999 dalam badan JSON. Jika pelayan melaksanakan user.__dict__.update(request_data) tanpa penapisan, medan tersebut akan ditulis ke pangkalan data.

Dalam FastAPI, skema masukan Pydantic ialah pertahanan utama Anda. Skema ini bertindak sebagai senarai benarkan yang jelas: hanya medan yang diisytiharkan dalam skema boleh diterima daripada klien. Sebarang medan tambahan akan diabaikan secara senyap (atau ditolak, bergantung pada konfigurasi).

Memisahkan Skema Masukan dan Keluaran

Pola paling penting untuk mencegah penetapan pukal ialah menggunakan model Pydantic yang berasingan untuk masukan dan keluaran:

  • Skema masukan — hanya medan yang dibenarkan untuk ditetapkan oleh pengguna
  • Skema keluaran — semua medan yang selamat untuk dikembalikan kepada pemanggil
  • Model pangkalan data — rekod penuh, termasuk medan sensitif seperti is_admin, hashed_password dan sebagainya.

Jangan gunakan skema yang sama untuk masukan dan keluaran jika kedua-duanya mempunyai keperluan keselamatan yang berbeza.

from pydantic import BaseModel, EmailStr
from typing import Optional


# What the client is ALLOWED to send when updating a profile
class UserUpdateInput(BaseModel):
    display_name: Optional[str] = None
    bio: Optional[str] = None
    email: Optional[EmailStr] = None
    # NOTE: is_admin, balance, role, hashed_password are NOT here


# What we return to the client (read-only fields visible but not settable)
class UserPublicOutput(BaseModel):
    id: int
    display_name: str
    email: EmailStr
    is_admin: bool

    model_config = {"from_attributes": True}

Menggunakan Skema Masukan dalam Laluan Kemas Kini

Dengan skema berasingan tersedia, laluan kemas kini menggunakan model_dump(exclude_unset=True) untuk mendapatkan hanya medan yang benar-benar dihantar oleh klien. Medan ini kemudiannya digunakan pada model pangkalan data satu demi satu — jangan sekali-kali melalui kemas kini pukal __dict__.

exclude_unset=True amat penting untuk semantik PATCH: ia memastikan medan yang tiada bermaksud "jangan ubah" dan bukannya "tetapkan kepada null".

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.auth import get_current_user_id
from app.db import get_db
from app.models import User
from app.schemas import UserUpdateInput, UserPublicOutput

router = APIRouter()


@router.patch("/users/me", response_model=UserPublicOutput)
async def update_profile(
    payload: UserUpdateInput,
    current_user_id: int = Depends(get_current_user_id),
    db: AsyncSession = Depends(get_db),
):
    user = await db.get(User, current_user_id)
    if user is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")

    # Only apply fields the client actually provided
    update_data = payload.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(user, field, value)

    await db.commit()
    await db.refresh(user)
    return user

Menguatkuasakan Penolakan Medan Tambahan

Secara lalai, Pydantic v2 mengabaikan medan yang tidak diketahui secara senyap. Untuk skema yang sensitif dari segi keselamatan, Anda boleh mengkonfigurasi model supaya menimbulkan ralat pengesahan jika medan yang tidak dijangka dihantar. Ini menjadikan percubaan penetapan pukal kelihatan dalam log dan mencegah kehilangan data secara senyap akibat kesilapan taip.

Tetapkan model_config = ConfigDict(extra='forbid') pada skema masukan Anda. API Anda akan mengembalikan 422 Unprocessable Entity jika klien menghantar sebarang medan yang tidak diisytiharkan dalam skema.

from pydantic import BaseModel, EmailStr, ConfigDict
from typing import Optional


class StrictUserUpdateInput(BaseModel):
    model_config = ConfigDict(extra="forbid")  # reject unknown fields

    display_name: Optional[str] = None
    bio: Optional[str] = None
    email: Optional[EmailStr] = None


# Demonstration (runs standalone)
if __name__ == "__main__":
    import json
    from pydantic import ValidationError

    # Valid input
    valid = StrictUserUpdateInput(display_name="Alice")
    print("Valid:", valid.model_dump(exclude_unset=True))

    # Attacker tries to escalate privileges
    try:
        evil = StrictUserUpdateInput(display_name="Alice", is_admin=True)
    except ValidationError as e:
        errors = json.loads(e.json())
        print("Blocked:", errors[0]["type"], "-", errors[0]["loc"])

Menggabungkan Ketiga-tiga Pertahanan dalam Satu Aliran

Ketiga-tiga pertahanan ini berfungsi bersama sebagai lapisan. Begini cara ketiga-tiganya digabungkan dalam satu titik akhir kemas kini dokumen:

  1. Kebergantungan pengesahan — mengesahkan JWT dan mengekstrak current_user_id (menangani Pengesahan Rosak)
  2. Pemeriksaan BOLA — mendapatkan dokumen dan menegaskan doc.owner_id == current_user_id (menangani BOLA)
  3. Skema masukan dengan extra='forbid' — hanya membenarkan title dan content ditetapkan (menangani Penetapan Pukal)

Setiap lapisan adalah bebas. Jika Anda membuang mana-mana satu daripadanya, dua lapisan yang lain masih memberikan perlindungan separa — tetapi ketiga-tiganya diperlukan untuk liputan penuh.

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, ConfigDict
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession
from app.auth import get_current_user_id
from app.db import get_db
from app.models import Document

router = APIRouter()


class DocumentUpdateInput(BaseModel):
    model_config = ConfigDict(extra="forbid")
    title: Optional[str] = None
    content: Optional[str] = None


@router.patch("/documents/{doc_id}")
async def update_document(
    doc_id: int,
    payload: DocumentUpdateInput,                          # Layer 3: mass assignment guard
    current_user_id: int = Depends(get_current_user_id),  # Layer 1: auth
    db: AsyncSession = Depends(get_db),
):
    doc = await db.get(Document, doc_id)
    if doc is None:
        raise HTTPException(status_code=404, detail="Document not found")

    if doc.owner_id != current_user_id:                   # Layer 2: BOLA guard
        raise HTTPException(status_code=403, detail="Access denied")

    for field, value in payload.model_dump(exclude_unset=True).items():
        setattr(doc, field, value)

    await db.commit()
    await db.refresh(doc)
    return doc

Semakan Pengetahuan: Mencegah BOLA

Titik akhir FastAPI mendapatkan invois berdasarkan ID-nya dan mengembalikannya kepada pemanggil. Titik akhir itu sudah memerlukan JWT yang sah. Langkah tambahan manakah yang penting untuk mencegah serangan BOLA (Kebenaran Tahap Objek yang Rosak)?

Ulang Kaji Pelajaran: Tiga Pertahanan, Satu API Selamat

Dalam pelajaran ini, Anda memadankan tiga ancaman OWASP API Security Top 10 dengan pertahanan FastAPI yang nyata:

  • Pengesahan Rosak (API2) — pusatkan pengesahan JWT dalam kebergantungan Depends(); senaraikan putih algoritma tandatangan; muatkan rahsia daripada pemboleh ubah persekitaran.
  • BOLA (API1) — selepas mendapatkan sebarang sumber, sentiasa tegaskan resource.owner_id == current_user_id. Kembalikan 403 apabila tidak sepadan. Ekstrak pemeriksaan itu ke dalam pembantu yang boleh digunakan semula bagi mencegah keciciran.
  • Penetapan Pukal (API6) — gunakan skema Pydantic masukan dan keluaran yang berasingan; tetapkan extra='forbid' pada skema masukan; gunakan kemas kini medan demi medan dengan exclude_unset=True.

Ketiga-tiga pola ini bebas dan boleh digabungkan. Apabila digunakan bersama melalui sistem suntikan kebergantungan FastAPI, pola ini menghapuskan vektor serangan API yang paling lazim pada peringkat rangka kerja — sebelum satu baris logik perniagaan pun dijalankan.

Percuma untuk bermula

Pelajari Kem Intensif Pembangunan Bahagian Belakang FastAPI dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
21
Pelajaran
84

Soalan Lazim

Adakah pelajaran “Mengurangkan 10 Ancaman Keselamatan API Teratas OWASP” percuma?

Ya — sebanyak 3 pelajaran dalam laluan pembelajaran Kem Intensif Pembangunan Bahagian Belakang FastAPI, termasuk “Mengurangkan 10 Ancaman Keselamatan API Teratas OWASP”, boleh dibaca sepenuhnya secara percuma di web ini. Selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan interaktif dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Kursus Kem Intensif Pembangunan Bahagian Belakang FastAPI merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Mengurangkan 10 Ancaman Keselamatan API Teratas OWASP”?

Petakan ancaman API lazim kepada pertahanan FastAPI khusus untuk pengesahan rosak, BOLA dan tugasan besar-besaran. Anda berlatih Kem Intensif Pembangunan Bahagian Belakang FastAPI menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan Kem Intensif Pembangunan Bahagian Belakang FastAPI?

Tiada pengalaman terdahulu diperlukan. Pembelajaran Kem Intensif Pembangunan Bahagian Belakang FastAPI di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 1 daripada 4.

Berapa lamakah pelajaran “Mengurangkan 10 Ancaman Keselamatan API Teratas OWASP” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran Kem Intensif Pembangunan Bahagian Belakang FastAPI ini?

Ya. Setiap pelajaran Kem Intensif Pembangunan Bahagian Belakang FastAPI menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Mengurangkan 10 Ancaman Keselamatan API Teratas OWASP
  2. Pengehadan Kadar dan Perlindungan Penyalahgunaan Bot
  3. Pengurusan Rahsia dan Putaran Kunci
  4. CORS, CSP dan Dasar Pengepala Selamat
← Kembali ke Kem Intensif Pembangunan Bahagian Belakang FastAPI