I have an API endpoint that returns an AWS S3 upload presigned URL.
Only authenticated users in my app can call this endpoint to upload music files.
However, nothing prevents a "malicious user" from creating an account and calling this endpoint an unlimited amount of times to upload an unlimited amount of files.
How can I even prevent this issue? It seems like all apps that use presigned URLs must have this issue, yet I see no questions about this, here or elsewhere 🤔
Your application (API endpoint) will be responsible for limiting the number of calls that your users can make to that endpoint. You could set rate limiting (where the API returns an error if called too often), or return the same presigned URL until a certain amount of time passes (e.g. return only 1 URL per every X minutes per user). If you choose rate limiting, I would suggest returning a 429 error code with a Retry-After
header to tell your caller how long to wait until the next try.
If you want to limit protect the presigned URL from misuse, I suggest you set an expiration on the URL that is limited to the typical time period that is practical for your use case. A presigned URL should be protected just like other credentials/secrets, similar to short-lived credentials from AWS STS.
If your concern is related to validating the content stored in S3 (as opposed to the API usage rate), you might consider attaching a Lambda function to the PutObject event on your S3 bucket. This Lambda could be used to perform any post-processing, triggering virus scans, file compression, etc.
If you want to optimize your storage costs in S3, consider using Intelligent Tiering on your bucket or applying a Lifecycle Policy according to your app's retention needs (move to IA after X days, Glacier after Y days, delete after Z days).