DEV Community

loading...
Cover image for My App was Under Attack!

My App was Under Attack!

2spacemilk profile image Mark Harless ・2 min read

I'm fully aware my title sounds like a clickbait video from YouTube but I assure you it kind of does sound like that, huh?

A couple of weeks ago I created an app for Nintendo Switch owners to easily share their friend code with their friends. It's called Ninny Code!.

I received an email from Heroku, where I deployed the app, saying that I've exceeded the total amount of rows I'm allowed on the free tier.

The database DATABASE_URL on Heroku app ninnycode-b has exceeded its allocated storage capacity. Immediate action is required:

"The database contains 22,360 rows, exceeding the Hobby-dev plan limit of 10,000. INSERT privileges to the database will be automatically revoked in 7 days. This will cause service failures in most applications dependent on this database.

To avoid a disruption to your service, migrate the database to a Hobby Basic ($9/month) or higher database plan:

https://hello.heroku.com/upgrade-postgres-c#upgrading-with-pg-copy

If you are unable to upgrade the database, you should reduce the number of records stored in it."

In a panic, I quickly deleted all of the records. Yes, you read that right. I delete everything on complete accident. Ugh! That lesson was quickly learned the wrong way.

With my records at 0, a few hours later it was attacked again. And again, and again, and again. All day by somebody bored at home during the quarantine.

I was forced to shut the app down which is unfortunate because I use the app to show future employers. After a bit of research, I found an easy way to stop this mad man from messing with my app!

Enter Flask Limiter. This Python package ensures that an API endpoint can only be called up to a specified limit from the same IP address. So in my POST endpoint I have set a limit of 5 per hour and I'm happy to say it has stopped!

Here is a very quick implementation to show you how easy it is to get set up in your Flask projects:

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

@app.route('/api', methods=['POST'])
@limiter.limit("3 per hour")
def post_user():
    # create user

# custom 429 HTTP status
@app.errorhandler(429)
def ratelimit_handler(e):
    return {"Error": "Too many requests. Please try again later."}, 429
Enter fullscreen mode Exit fullscreen mode

It's that easy! You'll see at the bottom I created a custom 429 status code because I'm using React to connect to my backend. Normally, Flask Limiter will create a bare-bones page with an h1 and p tag telling you you've reached the limit. If you try to create more than 5 users per hour on Ninny Code!, you'll see my solution is a bit more elegant!

Discussion

pic
Editor guide
Collapse
rubenwap profile image
Ruben Sanchez

So if I understand, this is not a case of your app having some kind of vulnerability, but the attack was to bomb it with legitimate requests? I am curious to know what the attacker's rows looked like, were they garbage data?

Collapse
2spacemilk profile image
Mark Harless Author

Yep, they were just random alphanumeric characters that passed my POST validations.

screenshot of tables

Collapse
binotaliu profile image
ビノタ

Using user input as actual filenames is still a terrible idea. Consider to use an internal key or hashed string as filenames.

Collapse
devarjunan profile image
dev-arjunan

Thanks for sharing. I learned new thing today. Keep sharing

Collapse
hyaovi profile image
Yaovi

I second you

Collapse
patarapolw profile image
Pacharapol Withayasakpunt

Nice.

Actually, I remembered 429 from some websites I have scraped.