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

Rate Limiting Additions #47

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
142 changes: 138 additions & 4 deletions modules/3-ssdlc.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,35 @@ Additionally, thought can be put into how you architect data flows with regards

### Description

Rate limiting restricts the number of requests that can be allowed in a certain time frame on a specific resource. Rate limiting can be implemented to protect against a variety of attacks or abuses:
While performing their designed operations, applications send traffic across networks, make client requests, generate server responses, and consume processing and memory capacity from the computing machines on which they are hosted. They often have components, like APIs that interact with data sources and services. Simply put - modern applications have a lot of things happening!

* Preventing Denial Of Service attacks (limiting the number of calls to expensive endpoints)
When building an application, it is necessary to consider approaches to manage the stress on all internal and external resources involved within the context of the application. This will help ensure the continued availability of the application and its functionality for all legitimate users and entities. This is particularly important for applications that run critical infrastructure and other key services organizations rely on for business operations.

When resources are not well managed, applications become vulnerable to negative impacts in performance, service outages, and denial of service attacks, in which a malicious actor takes advantage of resource limitations to intentionally overwhelm and crash a system.

Uncontrolled Resource Consumption is ranked 23rd on the [list of most dangerous software weaknesses](https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html). Implementing rate limiting is one security best practice that can help mitigate the likelihood of an application inadvertently or being deliberately triggered into consuming vital resources.

### Implementation Approaches
There are multiple approaches to implementing rate limiting within an application and in general the approach is based on anticipating at least one of the following problematic scenarios:

* Malicious actor-generating abusive traffic designed to force the application into a compromised state
* Malicious or legitimate users perform large load activities that require significant processing power
* Malicious or legitimate users sending a large number of queries, often repeatedly to the service
* Malicious or legitimate users sending a large number of queries in a narrow timeframe

Rate limiting can be implemented to protect against a variety of attacks or abuses by:

* Preventing Denial Of Service attacks (limiting the number of calls to resource expensive endpoints)
* Limiting brute force attempts (such as on one time codes and passwords)
* Programmatic abuse of services
* Addressing programmatic abuse of services

### Prevention
There are generally two approaches to rate limiting: that which occurs at the application level, and that which occurs at the network level.

### Application Level Rate Limiting

Application rate limiting involves limiting interactions with functionality within the application. While developing an application, it is important to implement constraints around how often users can perform certain actions.

For instance, an application that allows users to submit documents will want to limit file size and the number of repeated upload attempts. Users who upload gigantic files or who try to send too many files back-to-back could case a crash. Implementing rate limiting around the file upload functionality will help ensure that components of the application that respond to unpredictable or intentionally malicious user input, will not exceed the application's server processing capacity or available storage space.

When rate limiting a new action, begin by asking these questions:

Expand All @@ -75,6 +97,118 @@ If the answer to one or more of those questions is yes, consider putting a limit

More often than not, rate limiting should be as specific as possible. For instance, it is better to add rate limiting on a single GraphQL type than to add a generic limit to the entire /GraphQL endpoint.

### <span style="color:blue;">Example</span>

When building Elixir applications, we recommend using the rate limiting library [Hammer](https://hexdocs.pm/hammer/frontpage.html). Let's take a look at how one would use it!

Let's say there is an application that assigns users a userID and allows users to upload photos. Developers could decide to apply limits based on userId as in the example below.

Note, the check_rate function below takes 3 arguments:

1. The first is what we've decided apply the limit to - in this case a unique identifier from the `userID`.
2. The second argument (`60_000`) represents the number of milliseconds in 1 minute
3. The last argument represents the number of times the identifier (our first argument) can appear to have done a particular action before we stop them. In this case, 5 times.

Ultimately we've created a case statement where the user is able to perform 5 upload photos actions within a minute before being blocked or stopped.

```elixir
defmodule PhotoStorage do
def upload(userID, _file) do
case Hammer.check_rate("action:#{userId}", 60_000, 5) do
{:allow, _count} ->
# let them do it
{:deny, _limit} ->
# nope too many
end
end
end
```

### Network Level Rate Limiting
Network level rate limiting involves limiting the internet traffic that is destined for the application's host, web server, or web services. In web applications, when a client makes a request for an application's resources by internet address (via DNS <-> URL domain), that request is made using the HTTP/HTTPS protocol across the internet. Then the service responds using an HTTP response status code indicating if the resource was successfully returned, if there has been an error, or other message as appropriate.

At the network level, a firewall can be configured to sit between the internet and your application servers/services to control traffic entering and leaving your hosting environment. Firewalls, are traditionally a network device that filters traffic based on a configured access control list that defines good traffic and, depending on the firewall captures/analyzes network packets, and blocks bad traffic.

Web Application Firewalls (WAF) operate in a similar fashion to protect applications from incoming network traffic. They look at web traffic over HTTP/HTTPS (ports 80 and 443) respectively for malicious incoming traffic with your application as it's destination and can be configured to allow only known good requests. They can also restrict the number of repeated bad attempts from a single source ip address. Third party hosted WAFs have the ability to monitor for and mitigate large distributed denial of service attacks.

### <span style="color:blue;">Example</span>

**If your public facing URI/URL for your application is hxxps://yourelixirapplication.org/login:**

*The following is an example of requests that can come from across the internet targeting your application
from the same source IP 555.555.555.555 for instance:*

```
hxxps://yourelixirapplication.org/secret
hxxps://yourelixirapplication.org/admin
hxxps://yourelixirapplication.org/login?user=test
hxxps://yourelixirapplication.org/login?user=test&pass=test
hxxps://yourelixirapplication.org/login?user=';&pass=/www/app/...
.....

```
Your site will handle these incoming requests either by returning 404 responses, or other, depending on your configuration.

Imagine receiving thousands of these requests. Automated tools are freely available to malicious users who may attempt a variety of attacks, like triggering a denial of service. If while building the server/service this scenario was not anticipated, the server could crash and your legitimate users won't be able to login to your application.

A WAF can be configured to limit the number of tries from the same IP address. In the above example, if the same IP is the source for more than 100 of these attempts within 5 seconds, the WAF can prevent those requests from reach your application.

When implementing rate limiting, consider limits on the following (please see Rate Limiting References below):

* Execution timeouts
* Max allocable memory
* Number of file descriptors
* Number of processes
* Request payload size (e.g., uploads)
* Number of requests per client/resource/source ip address
* Number of records per page to return in a single request response

### <span style="color:red">QUIZ</span>

***Using the Hammer library, protect the provided function with these specifications: limit each user to 3 login attempts in 1 minute***

```elixir
defmodule LoginAttempt do
def login(user, attempt) do
# code below to be added here
end
end
end
```

*Uncomment the line (1-3) that indicates the code block that goes into the funtion above*

```
# answer = :1
# case Hammer.check_rate("login_try:#{user}", 60_000, 3) do
# {:allow, _count} ->
# # try again
# {:deny, _limit} ->
# # lock out for 10 min
# ------------------------------------------------------
# answer = :2
# case Hammer("login_try:#{user}", 60_000, 3) do
# {:allow, _count} ->
# # try again
# {:deny, _limit} ->
# # lock out for 30 min
# ------------------------------------------------------
# answer = :3
# case Hammer.check_rate("login_try:#{user}", 30_000, 100) do
# {:allow, _count} ->
# # try again
# {:deny, _limit} ->
# # lock out for 5 min

IO.puts(answer)
```


### Resources
1. https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html#rate-limiting
2. https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa4-lack-of-resources-and-rate-limiting.md
3. https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html

## Principle of Least Privilege

Sometimes known as the Principle of Minimal Privilege or the Principle of Least Authority, the Principle of Least Privilege (PoLP) means that every entity* is only strictly given the essential privileges needed to perform its requirement.
Expand Down