Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DDoS prevention #119

Open
adriangb opened this issue Jan 24, 2023 · 1 comment
Open

DDoS prevention #119

adriangb opened this issue Jan 24, 2023 · 1 comment

Comments

@adriangb
Copy link
Owner

adriangb commented Jan 24, 2023

I think most web frameworks should, by default, prevent DDoS and Slow Loris attacks. There's a bunch of places this needs to happen, here are some ideas:

  • A Slowloris middleware that kills the connection if you spend more than X seconds waiting on the client.
  • A middleware that limits total payload size for uploads.
  • Stricter form parsing

With regards to form parsing, we currently rely entirely on Starlette and blindly call Request.form(), which is not great given we have a ton of metadata about what we expect in the form. Inspired by multer I would like something like this:

from typing import Annotated, List

import annotated_types as at
from xpresso.bodies import FormField, FormFile, Multipart

MB = 2**20

# Define a model, this restricts the fields that can be included
# unless extra = "allow" is set on the Pydantic model itself
# This protects against attacks that spam a lot of fields
class MyForm(BaseModel):
    # Restrict the age field to 4 bytes
    # If the client sends more than this we'll error immediately
    # We also use Pydantic to automatically parse the string into an int
    age: Annotated[int, FormField(), at.Len(max_length=10)]
    # Images is a list of bytes
    # We first declare that the list itself cannot have more than 10 items
    # Xpresso would have to check if the type is a List and if so check if `max_items` is set on the field
    # as a special case since we can use this to error before reading more fields
    # (I think re-using Pydantic's metadata makes sense when possible)
    # We also limit each image to 10 MB and will immediately stop parsing and error
    # if it is larger than this
    images: Annotated[List[Annotated[bytes, FormFile(), at.Len(max_length=10*MB], at.Len(max_length=10)]
    extra_data: Annotated[bytes, FormFile()]  # just to demonstrate max_length below

# restrict the size (in bytes) of the entire body to 100MB
async def endpoint(form: Annotated[MyForm, Multipart(max_length=100*MB)]):
    ...

This is a super explicit and perhaps overkill example, but it gets the idea across.

With JSON or raw bytes we can at least limit the total size of the request body:

async def endpoint(form: Annotated[SomeModel, Json(max_length=1*MB)]):
    ...
@adriangb
Copy link
Owner Author

And the idea would be that the web framework itself imposes some reasonable defaults.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant