How To Prevent AWS Lambda Abuse by 3rd-party apps

When you deploy an endpoint that is open to the world, you're opening it to be used, but also to be abused.

AWS provides services to avoid common abuse methods, such as AWS Shield, which mitigates against DDoS, etc., however, they do not know what is or is not abuse of your Lambda function, as you are asking.

If your Lambda function is private, then you should use one of the API gateway security mechanisms to prevent abuse:

  • IAM security
  • API key security
  • Custom security authorization

With one of these in place, your Lambda function can only by called by authorized users. Without one of these in place, there is no way to prevent the type of abuse you're concerned about.


Unlimited access to your public Lambda functions - either by bad actors, or by bad software developed by legitimate 3rd parties, can result in unwanted usage of billable corporate resources, and can degrade application performance. It is important to you consider ways of limiting and restricting access to your Lambda clients as part of your systems security design, to prevent runaway function invocations and uncontrolled costs.

Consider using the following approach to preventing execution "abuse" of your Lambda endpoint by 3rd party apps:

One factor you want to control is concurrency, or number of concurrent requests that are supported per account and per function. You are billed per request plus total memory allocation per request, so this is the unit you want to control. To prevent run away costs, you prevent run away executions - either by bad actors, or by bad software cause by legitimate 3rd parties.

From Managing Concurrency

The unit of scale for AWS Lambda is a concurrent execution (see Understanding Scaling Behavior for more details). However, scaling indefinitely is not desirable in all scenarios. For example, you may want to control your concurrency for cost reasons, or to regulate how long it takes you to process a batch of events, or to simply match it with a downstream resource. To assist with this, Lambda provides a concurrent execution limit control at both the account level and the function level.

In addition to per account and per Lambda invocation limits, you can also control Lambda exposure by wrapping Lambda calls in an AWS API Gateway, and Create and Use API Gateway Usage Plans:

After you create, test, and deploy your APIs, you can use API Gateway usage plans to extend them as product offerings for your customers. You can provide usage plans to allow specified customers to access selected APIs at agreed-upon request rates and quotas that can meet their business requirements and budget constraints.

What Is a Usage Plan? A usage plan prescribes who can access one or more deployed API stages— and also how much and how fast the caller can access the APIs. The plan uses an API key to identify an API client and meters access to an API stage with the configurable throttling and quota limits that are enforced on individual client API keys.

The throttling prescribes the request rate limits that are applied to each API key. The quotas are the maximum number of requests with a given API key submitted within a specified time interval. You can configure individual API methods to require API key authorization based on usage plan configuration. An API stage is identified by an API identifier and a stage name.

Using API Gateway Limits to create Gateway Usage Plans per customer, you can control API and Lambda access prevent uncontrolled account billing.


@Matt answer is correct, yet incomplete.

Adding a security layer is a necessary step towards security, but doesn't protect you from authenticated callers, as @Rodrigo's answer states.

I actually just encountered - and solved - this issue on one of my lambda, thanks to this article: https://itnext.io/the-everything-guide-to-lambda-throttling-reserved-concurrency-and-execution-limits-d64f144129e5

Basically, I added a single line on my serverless.yml file, in my function that gets called by the said authirized 3rd party:

reservedConcurrency: 1

And here goes the whole function:

refresh-cache:
    handler: src/functions/refresh-cache.refreshCache
    # XXX Ensures the lambda always has one slot available, and never use more than one lambda instance at once.
    #  Avoids GraphCMS webhooks to abuse our lambda (GCMS will trigger the webhook once per create/update/delete operation)
    #  This makes sure only one instance of that lambda can run at once, to avoid refreshing the cache with parallel runs
    #  Avoid spawning tons of API calls (most of them would timeout anyway, around 80%)
    #  See https://itnext.io/the-everything-guide-to-lambda-throttling-reserved-concurrency-and-execution-limits-d64f144129e5
    reservedConcurrency: 1
    events:
      - http:
          method: POST
          path: /refresh-cache
          cors: true

The refresh-cache lambda was invoked by a webhook triggered by a third party service when any data change. When importing a dataset, it would for instance trigger as much as 100 calls to refresh-cache. This behaviour was completely spamming my API, which in turn was running requests to other services in order to perform a cache invalidation.

Adding this single line improved the situation a lot, because only one instance of the lambda was running at once (no concurrent run), the number of calls was divided by ~10, instead of 50 calls to refresh-cache, it only triggered 3-4, and all those call worked (200 instead of 500 due to timeout issue).

Overall, pretty good. Not yet perfect for my workflow, but a step forward.


Not related, but I used https://epsagon.com/ which tremendously helped me figuring out what was happening on AWS Lambda. Here is what I got:

Before applying reservedConcurrency limit to the lambda: enter image description here

You can see that most calls fail with timeout (30000ms), only the few first succeed because the lambda isn't overloaded yet.

After applying reservedConcurrency limit to the lambda: enter image description here

You can see that all calls succeed, and they are much faster. No timeout. Saves both money, and time.


Using reservedConcurrency is not the only way to deal with this issue, there are many other, as @Rodrigo stated in his answer. But it's a working one, that may fit in your workflow. It's applied on the Lambda level, not on API Gateway (if I understand the docs correctly).