
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Sun, 05 Apr 2026 19:47:41 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Cloudflare incident on June 20, 2024]]></title>
            <link>https://blog.cloudflare.com/cloudflare-incident-on-june-20-2024/</link>
            <pubDate>Wed, 26 Jun 2024 13:00:22 GMT</pubDate>
            <description><![CDATA[ A new DDoS rule resulted in an increase in error responses and latency for Cloudflare customers. Here’s how it went wrong, and what we’ve learned ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/18peGsB3IdmJIUOoqJalSs/b69735394144edb57ba12ff8e8516518/2459.png" />
            
            </figure><p>On Thursday, June 20, 2024, two independent events caused an increase in latency and error rates for Internet properties and Cloudflare services that lasted 114 minutes. During the 30-minute peak of the impact, we saw that 1.4 - 2.1% of HTTP requests to our CDN received a generic error page, and observed a 3x increase for the 99th percentile Time To First Byte (TTFB) latency.</p><p>These events occurred because:</p><ol><li><p><a href="https://www.cloudflare.com/network-services/solutions/network-monitoring-tools/">Automated network monitoring</a> detected performance degradation, re-routing traffic suboptimally and <a href="https://www.cloudflarestatus.com/incidents/k7d4c79c63lq">causing backbone congestion between 17:33 and 17:50 UTC</a></p></li><li><p>A new Distributed Denial-of-Service (DDoS) mitigation mechanism deployed between 14:14 and 17:06 UTC triggered a latent bug in our rate limiting system that allowed a specific form of HTTP request to cause a process handling it to enter an infinite loop <a href="https://www.cloudflarestatus.com/incidents/p7l6rrbhysck">between 17:47 and 19:27 UTC</a></p></li></ol><p>Impact from these events were observed in many Cloudflare data centers around the world.</p><p>With respect to the backbone congestion event, we were already working on expanding backbone capacity in the affected data centers, and improving our network mitigations to use more information about the available capacity on alternative network paths when taking action. In the remainder of this blog post, we will go into more detail on the second and more impactful of these events.</p><p>As part of routine updates to our protection mechanisms, we created a new DDoS rule to prevent a specific type of abuse that we observed on our infrastructure. This DDoS rule worked as expected, however in a specific suspect traffic case it exposed a latent bug in our existing rate-limiting component. To be absolutely clear, we have no reason to believe this suspect traffic was intentionally exploiting this bug, and there is no evidence of a breach of any kind.</p><p>We are sorry for the impact and have already made changes to help prevent these problems from occurring again.</p>
    <div>
      <h2>Background</h2>
      <a href="#background">
        
      </a>
    </div>
    
    <div>
      <h3>Rate-limiting suspicious traffic</h3>
      <a href="#rate-limiting-suspicious-traffic">
        
      </a>
    </div>
    <p>Depending on the profile of an HTTP request and the configuration of the requested Internet property, Cloudflare may protect our network and our customer’s origins by applying a limit to the number of requests a visitor can make within a certain time window. These <a href="https://www.cloudflare.com/en-gb/learning/bots/what-is-rate-limiting/">rate limits</a> can activate through customer configuration or in response to DDoS rules detecting suspicious activity.</p><p>Usually, these rate limits will be applied based on the IP address of the visitor. As many institutions and Internet Service Providers (ISPs) can have <a href="https://en.wikipedia.org/wiki/Carrier-grade_NAT">many devices and individual users behind a single IP address</a>, rate limiting based on the IP address is a broad brush that can unintentionally block legitimate traffic.</p>
    <div>
      <h3>Balancing traffic across our network</h3>
      <a href="#balancing-traffic-across-our-network">
        
      </a>
    </div>
    <p>Cloudflare has several systems that together provide continuous real-time capacity monitoring and rebalancing to ensure we serve as much traffic as we can as quickly and efficiently as we can.</p><p>The first of these is <a href="/unimog-cloudflares-edge-load-balancer">Unimog, Cloudflare’s edge load balancer</a>. Every packet that reaches our anycast network passes through Unimog, which delivers it to an appropriate server to process that packet. That server may be in a different location from where the packet originally arrived into our network, depending on the availability of compute capacity. Within each data center, Unimog aims to keep the CPU load uniform across all active servers.</p><p>For a global view of our network, we rely on <a href="/meet-traffic-manager">Traffic Manager</a>. Across all of our data center locations, it takes in a variety of signals, such as overall CPU utilization, HTTP request latency, and bandwidth utilization to instruct rebalancing decisions. It has built-in safety limits to prevent causing outsized traffic shifts, and also considers the expected resulting load in destination locations when making any decisions.</p>
    <div>
      <h2>Incident timeline and impact</h2>
      <a href="#incident-timeline-and-impact">
        
      </a>
    </div>
    <p>All timestamps are UTC on 2024-06-20.</p><ul><li><p>14:14 DDoS rule gradual deployment starts</p></li><li><p>17:06 DDoS rule deployed globally</p></li><li><p>17:47 First HTTP request handling process is poisoned</p></li><li><p>18:04 Incident declared automatically based on detected high CPU load</p></li><li><p>18:34 Service restart shown to recover on a server, full restart tested in one data center</p></li><li><p>18:44 CPU load normalized in data center after service restart</p></li><li><p>18:51 Continual global reloads of all servers with many stuck processes begin</p></li><li><p>19:05 Global eyeball HTTP error rate peaks at 2.1% service unavailable / 3.45% total</p></li><li><p>19:05 First Traffic Manager actions recovering service</p></li><li><p>19:11 Global eyeball HTTP error rate halved to 1% service unavailable / 1.96% total</p></li><li><p>19:27 Global eyeball HTTP error rate reduced to baseline levels</p></li><li><p>19:29 DDoS rule deployment identified as likely cause of process poisoning</p></li><li><p>19:34 DDoS rule is fully disabled</p></li><li><p>19:43 Engineers stop routine restarts of services on servers with many stuck processes</p></li><li><p>20:16 Incident response stood down</p></li></ul><p>Below, we provide a view of the impact from some of Cloudflare’s internal metrics. The first graph illustrates the percentage of all eyeball (inbound from external devices) HTTP requests that were served an error response because the service suffering poisoning could not be reached. We saw an initial increase to 0.5% of requests, and then later a larger one reaching as much as 2.1% before recovery started due to our service reloads.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1EoCaNBt1Bss8RhwKO5s5B/155382a7f8eba337c39f48c803c9979b/image7-3.png" />
            
            </figure><p>For a broader view of errors, we can see all 5xx responses our network returned to eyeballs during the same window, including those from origin servers. These peaked at 3.45%, and you can more clearly see the gradual recovery between 19:25 and 20:00 UTC as Traffic Manager finished its re-routing activities. The dip at 19:25 UTC aligns with the last large reload, with the error increase afterwards primarily consisting of upstream DNS timeouts and connection limits which are consistent with high and unbalanced load.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7G1ZnlpW9Qbw8DgW5MfsLv/9758cb94e7d6be79dcfac41e482114ea/image4-6.png" />
            
            </figure><p>And here’s what our TTFB measurements looked like at the 50th, 90th and 99th percentiles, showing an almost 3x increase in latency at p99:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/16tMNhSZdhkV3wH6MtAWd6/44a638b8f639d39026112c32cb80dc2e/image2-10.png" />
            
            </figure>
    <div>
      <h2>Technical description of the error and how it happened</h2>
      <a href="#technical-description-of-the-error-and-how-it-happened">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/MQYpmg1O82vEURJNsl2VU/da0b8e5c1afd70998bc928a533ee4160/image5-5.png" />
            
            </figure><p><i>Global percentage of HTTP Request handling processes that were using excessive CPU during the event</i></p><p>Earlier on June 20, between 14:14 - 17:06 UTC, we gradually activated a new DDoS rule on our network. Cloudflare has recently been building a new way of mitigating HTTP DDoS attacks. This method is using a combination of rate-limits and cookies in order to allow legitimate clients that were falsely identified as being part of an attack to proceed anyway.</p><p>With this new method, an HTTP request that is considered suspicious runs through these key steps:</p><ol><li><p>Check for the presence of a valid cookie, otherwise block the request</p></li><li><p>If a valid cookie is found, add a rate-limit rule based on the cookie value to be evaluated at a later point</p></li><li><p>Once all the currently applied DDoS mitigation are run, apply rate-limit rules</p></li></ol><p>We use this "asynchronous" workflow because it is more efficient to block a request without a rate-limit rule, so it gives a chance for other rule types to be applied.</p><p>So overall, the flow can be summarized with this pseudocode:</p>
            <pre><code>for (rule in active_mitigations) {
   // ... (ignore other rule types)
   if (rule.match_current_request()) {
       if (!has_valid_cookie()) {
           // no cookie: serve error page
           return serve_error_page();
       } else {
           // add a rate-limit rule to be evaluated later
           add_rate_limit_rule(rule);
       }
   }
}


evaluate_rate_limit_rules();</code></pre>
            <p>When evaluating rate-limit rules, we need to make a <i>key</i> for each client that is used to look up the correct counter and compare it with the target rate. Typically, this key is the client IP address, but other options are available, such as the value of a cookie as used here. We actually reused an existing portion of the rate-limit logic to achieve this. In pseudocode, it looks like:</p>
            <pre><code>function get_cookie_key() {
   // Validate that the cookie is valid before taking its value.
   // Here the cookie has been checked before already, but this code is
   // also used for "standalone" rate-limit rules.
   if (!has_valid_cookie_broken()) { // more on the "broken" part later
       return cookie_value;
   } else {
       return parent_key_generator();
   }
}</code></pre>
            <p>This simple <i>key</i> generation function had two issues that, combined with a specific form of client request, caused an infinite loop in the process handling the HTTP request:</p><ol><li><p>The rate-limit rules generated by the DDoS logic are using internal APIs in ways that haven't been anticipated. This caused the <code>parent_key_generator</code> in the pseudocode above to point to the <code>get_cookie_key</code> function itself, meaning that if that code path was taken, the function would call itself indefinitely</p></li><li><p>As these rate-limit rules are added only after validating the cookie, validating it a second time should give the same result. The problem is that the <code>has_valid_cookie_broken</code> function used here is actually different and both can disagree if the client sends multiple cookies where some are valid but not others</p></li></ol><p>So, combining these two issues: the broken validation function tells <code>get_cookie_key</code> that the cookie is invalid, causing the <code>else</code> branch to be taken and calling the same function over and over.</p><p>A protection many programming languages have in place to help prevent loops like this is a run-time protection limit on how deep the stack of function calls can get. An attempt to call a function once already at this limit will result in a runtime error. When reading the logic above, an initial analysis might suggest we were reaching the limit in this case, and so requests eventually resulted in an error, with a stack containing those same function calls over and over.</p><p>However, this isn’t the case here. Some languages, including Lua, in which this logic is written, also implement an optimization called proper tail calls. A tail call is when the final action a function takes is to execute another function. Instead of adding that function as another layer in the stack, as we know for sure that we will not be returning execution context to the parent function afterwards, nor using any of its local variables, we can replace the top frame in the stack with this function call instead.</p><p>The end result is a loop in the request processing logic which never increases the size of the stack. Instead, it simply consumes 100% of available CPU resources, and never terminates. Once a process handling HTTP requests receives a single request on which the action should be applied and has a mixture of valid and invalid cookies, that process is poisoned and is never able to process any further requests.</p><p>Every Cloudflare server has dozens of such processes, so a single poisoned process does not have much of an impact. However, then some other things start happening:</p><ol><li><p>The increase in CPU utilization for the server causes Unimog to lower the amount of new traffic that server receives, moving traffic to other servers, so at a certain point, more new connections are directed away from servers with a subset of their processes poisoned to those with fewer or no poisoned processes, and therefore lower CPU utilization.</p></li><li><p>The gradual increase in CPU utilization in the data center starts to cause Traffic Manager to redirect traffic to other data centers. As this movement does not fix the poisoned processes, CPU utilization remains high, and so Traffic Manager continues to redirect more and more traffic away.</p></li><li><p>The redirected traffic in both cases includes the requests that are poisoning processes, causing the servers and data centers to which this redirected traffic was sent to start failing in the same way.</p></li></ol><p>Within a few minutes, multiple data centers had many poisoned processes, and Traffic Manager had redirected as much traffic away from them as possible, but was restricted from doing more. This was partly due to its built-in automation safety limits, but also because it was becoming more difficult to find a data center with sufficient available capacity to use as a target.</p><p>The first case of a poisoned process was at 17:47 UTC, and by 18:09 UTC – five minutes after the incident was declared – Traffic Manager was re-routing a lot of traffic out of Europe:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2AVE8fJsh1ODNjAoAtduon/6d77ac4385ba78ddd251c887ef02b74a/image3-14.png" />
            
            </figure><p><i>A summary map of Traffic Manager capacity actions as of 18:09 UTC. Each circle represents a data center that traffic is being re-routed towards or away from. The color of the circle indicates the CPU load of that data center. The orange ribbons between them show how much traffic is re-routed, and where from/to.</i></p><p>It’s obvious to see why, if we look at the percentage of the HTTP request service’s processes that were saturating their CPUs. 10% of our capacity in Western Europe was already gone, and 4% in Eastern Europe, during peak traffic time for those timezones:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7A71NY1brim2rjGowoewXo/a1c7f316d465b7dfb6c844b14c6408c3/image1-21.png" />
            
            </figure><p><i>Percentage of all the HTTP request handling processes saturating their CPU, by geographic region</i></p><p>Partially poisoned servers in many locations struggled with the request load, and the remaining processes could not keep up, resulting in Cloudflare returning minimal HTTP error responses.</p><p>Cloudflare engineers were automatically notified at 18:04 UTC, once our global CPU utilization reached a certain sustained level, and started to investigate. Many of our on-duty incident responders were already working on the open incident caused by backbone network congestion, and in the early minutes we looked into likely correlation with the network congestion events. It took some time for us to realize that locations where the CPU was highest is where traffic was the lowest, drawing the investigation away from a network event being the trigger. At this point, the focus moved to two main streams:</p><ol><li><p>Evaluating if restarting poisoned processes allowed them to recover, and if so, instigating mass-restarts of the service on affected servers</p></li><li><p>Identifying the trigger of processes entering this CPU saturation state</p></li></ol><p>It was 25 minutes after the initial incident was declared when we validated that restarts helped on one sample server. Five minutes after this, we started executing wider restarts – initially to entire data centers at once, and then as the identification method was refined, on servers with a large number of poisoned processes. Some engineers continued regular routine restarts of the affected service on impacted servers, whilst others moved to join the ongoing parallel effort to identify the trigger. At 19:36 UTC, the new DDoS rule was disabled globally, and the incident was declared resolved after executing one more round of mass restarts and monitoring.</p><p>At the same time, conditions presented by the incident triggered a latent bug in Traffic Manager. When triggered, the system would attempt to recover from the exception by initiating a graceful restart, halting its activity. The bug was first triggered at 18:17 UTC, then numerous times between 18:35 and 18:57 UTC. During two periods in this window (18:35-18:52 UTC and 18:56-19:05 UTC) the system did not issue any new traffic routing actions. This meant whilst we had recovered service in the most affected data centers, almost all traffic was still being re-routed away from them. Alerting notified on-call engineers of the issue at 18:34 UTC. By 19:05 UTC the Traffic team had written, tested, and deployed a fix. The first actions following restoration showed a positive impact on restoring service.</p>
    <div>
      <h2>Remediation and follow-up steps</h2>
      <a href="#remediation-and-follow-up-steps">
        
      </a>
    </div>
    <p>To resolve the immediate impact to our network from the request poisoning, Cloudflare instigated mass rolling restarts of the affected service until the change that triggered the condition was identified and rolled back. The change, which was the activation of a new type of DDoS rule, remains fully rolled back, and the rule will not be reactivated until we have fixed the broken cookie validation check and are fully confident this situation cannot recur.</p><p>We take these incidents very seriously, and recognize the magnitude of impact they had. We have identified several steps we can take to address these specific situations, and the risk of these sorts of problems from recurring in the future.</p><ul><li><p><b>Design:</b> The rate limiting implementation in use for our DDoS module is a legacy component, and rate limiting rules customers configure for their Internet properties use a newer engine with more modern technologies and protections.</p></li><li><p><b>Design:</b> We are exploring options within and around the service which experienced process poisoning to limit the ability to loop forever through tail calls. Longer term, Cloudflare is entering the early implementation stages of replacing this service entirely. The design of this replacement service will allow us to apply limits on the non-interrupted and total execution time of a single request.</p></li><li><p><b>Process:</b> The activation of the new rule for the first time was staged in a handful of production data centers for validation, and then to all data centers a few hours later. We will continue to enhance our staging and rollout procedures to minimize the potential change-related blast radius.</p></li></ul>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Cloudflare experienced two back-to-back incidents that affected a significant set of customers using our CDN and network services. The first was network backbone congestion that our systems automatically remediated. We mitigated the second by regularly restarting the faulty service whilst we identified and deactivated the DDoS rule that was triggering the fault. We are sorry for any disruption this caused our customers and to end users trying to access services.</p><p>The conditions necessary to activate the latent bug in the faulty service are no longer possible in our production environment, and we are putting further fixes and detections in place as soon as possible.</p> ]]></content:encoded>
            <category><![CDATA[Post Mortem]]></category>
            <category><![CDATA[Outage]]></category>
            <guid isPermaLink="false">1J7iIhkUBWQNj3JpbBTVEU</guid>
            <dc:creator>Lloyd Wallis</dc:creator>
            <dc:creator>Julien Desgats</dc:creator>
            <dc:creator>Manish Arora</dc:creator>
        </item>
        <item>
            <title><![CDATA[HTTP/2 Rapid Reset: deconstructing the record-breaking attack]]></title>
            <link>https://blog.cloudflare.com/technical-breakdown-http2-rapid-reset-ddos-attack/</link>
            <pubDate>Tue, 10 Oct 2023 12:02:28 GMT</pubDate>
            <description><![CDATA[ This post dives into the details of the HTTP/2 protocol, the feature that attackers exploited to generate the massive Rapid Reset attacks ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Starting on Aug 25, 2023, we started to notice some unusually big HTTP attacks hitting many of our customers. These attacks were detected and mitigated by our automated DDoS system. It was not long however, before they started to reach record-breaking sizes — and eventually peaked just above 201 million requests per second. This was nearly 3x bigger than our <a href="/cloudflare-mitigates-record-breaking-71-million-request-per-second-ddos-attack/">previous biggest attack on record</a>.</p><em><small>Under attack or need additional protection? <a href="https://www.cloudflare.com/h2/">Click here to get help</a>.</small></em><br /><p>Concerning is the fact that the attacker was able to generate such an attack with a botnet of merely 20,000 machines. There are botnets today that are made up of hundreds of thousands or millions of machines. Given that the entire web typically sees only between 1–3 billion requests per second, it's not inconceivable that using this method could focus an entire web’s worth of requests on a small number of targets.</p>
    <div>
      <h2>Detecting and Mitigating</h2>
      <a href="#detecting-and-mitigating">
        
      </a>
    </div>
    <p>This was a novel attack vector at an unprecedented scale, but Cloudflare's existing protections were largely able to absorb the brunt of the attacks. While initially we saw some impact to customer traffic — affecting roughly 1% of requests during the initial wave of attacks — today we’ve been able to refine our mitigation methods to stop the attack for any Cloudflare customer without it impacting our systems.</p><p>We noticed these attacks at the same time two other major industry players — Google and AWS — were seeing the same. We worked to harden Cloudflare’s systems to ensure that, today, all our customers are protected from this new DDoS attack method without any customer impact. We’ve also participated with Google and AWS in a coordinated disclosure of the attack to impacted vendors and critical infrastructure providers.</p><p>This attack was made possible by abusing some features of the HTTP/2 protocol and server implementation details (see  <a href="https://www.cve.org/CVERecord?id=CVE-2023-44487">CVE-2023-44487</a> for details). Because the attack abuses an underlying weakness in the HTTP/2 protocol, we believe any vendor that has implemented HTTP/2 will be subject to the attack. This included every modern web server. We, along with Google and AWS, have disclosed the attack method to web server vendors who we expect will implement patches. In the meantime, the best defense is using a DDoS mitigation service like Cloudflare’s in front of any web-facing web or API server.</p><p>This post dives into the details of the HTTP/2 protocol, the feature that attackers exploited to generate these massive attacks, and the mitigation strategies we took to ensure all our customers are protected. Our hope is that by publishing these details other impacted web servers and services will have the information they need to implement mitigation strategies. And, moreover, the HTTP/2 protocol standards team, as well as teams working on future web standards, can better design them to <a href="https://www.cloudflare.com/learning/ddos/how-to-prevent-ddos-attacks/">prevent such attacks</a>.</p>
    <div>
      <h2>RST attack details</h2>
      <a href="#rst-attack-details">
        
      </a>
    </div>
    <p>HTTP is the application protocol that powers the Web. <a href="https://www.rfc-editor.org/rfc/rfc9110.html">HTTP Semantics</a> are common to all versions of HTTP — the overall architecture, terminology, and protocol aspects such as request and response messages, methods, status codes, header and trailer fields, message content, and much more. Each individual HTTP version defines how semantics are transformed into a "wire format" for exchange over the Internet. For example, a client has to serialize a request message into binary data and send it, then the server parses that back into a message it can process.</p><p><a href="https://www.rfc-editor.org/rfc/rfc9112.html">HTTP/1.1</a> uses a textual form of serialization. Request and response messages are exchanged as a stream of ASCII characters, sent over a reliable transport layer like TCP, using the following <a href="https://www.rfc-editor.org/rfc/rfc9112.html#section-2.1">format</a> (where CRLF means carriage-return and linefeed):</p>
            <pre><code> HTTP-message   = start-line CRLF
                   *( field-line CRLF )
                   CRLF
                   [ message-body ]</code></pre>
            <p>For example, a very simple GET request for <code>https://blog.cloudflare.com/</code> would look like this on the wire:</p><p><code>GET / HTTP/1.1 CRLFHost: blog.cloudflare.comCRLFCRLF</code></p><p>And the response would look like:</p><p><code>HTTP/1.1 200 OK CRLFServer: cloudflareCRLFContent-Length: 100CRLFtext/html; charset=UTF-8CRLFCRLF&lt;100 bytes of data&gt;</code></p><p>This format <b>frames</b> messages on the wire, meaning that it is possible to use a single TCP connection to exchange multiple requests and responses. However, the format requires that each message is sent whole. Furthermore, in order to correctly correlate requests with responses, strict ordering is required; meaning that messages are exchanged serially and can not be multiplexed. Two GET requests, for <code>https://blog.cloudflare.com/</code> and <code>https://blog.cloudflare.com/page/2/</code>, would be:</p><p><code>GET / HTTP/1.1 CRLFHost: blog.cloudflare.comCRLFCRLFGET /page/2/ HTTP/1.1 CRLFHost: blog.cloudflare.comCRLFCRLF</code></p><p>With the responses:</p><p><code>HTTP/1.1 200 OK CRLFServer: cloudflareCRLFContent-Length: 100CRLFtext/html; charset=UTF-8CRLFCRLF&lt;100 bytes of data&gt;CRLFHTTP/1.1 200 OK CRLFServer: cloudflareCRLFContent-Length: 100CRLFtext/html; charset=UTF-8CRLFCRLF&lt;100 bytes of data&gt;</code></p><p>Web pages require more complicated HTTP interactions than these examples. When visiting the Cloudflare blog, your browser will load multiple scripts, styles and media assets. If you visit the front page using HTTP/1.1 and decide quickly to navigate to page 2, your browser can pick from two options. Either wait for all of the queued up responses for the page that you no longer want before page 2 can even start, or cancel in-flight requests by closing the TCP connection and opening a new connection. Neither of these is very practical. Browsers tend to work around these limitations by managing a pool of TCP connections (up to 6 per host) and implementing complex request dispatch logic over the pool.</p><p><a href="https://www.rfc-editor.org/rfc/rfc9113">HTTP/2</a> addresses many of the issues with HTTP/1.1. Each HTTP message is serialized into a set of <b>HTTP/2 frames</b> that have type, length, flags, stream identifier (ID) and payload. The stream ID makes it clear which bytes on the wire apply to which message, allowing safe multiplexing and concurrency. Streams are bidirectional. Clients send frames and servers reply with frames using the same ID.</p><p>In HTTP/2 our GET request for <code>https://blog.cloudflare.com</code> would be exchanged across stream ID 1, with the client sending one <a href="https://www.rfc-editor.org/rfc/rfc9113#name-headers">HEADERS</a> frame, and the server responding with one HEADERS frame, followed by one or more <a href="https://www.rfc-editor.org/rfc/rfc9113#name-data">DATA</a> frames. Client requests always use odd-numbered stream IDs, so subsequent requests would use stream ID 3, 5, and so on. Responses can be served in any order, and frames from different streams can be interleaved.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/UgsMj35BXBaxK2dKVIvC2/8b6db7d94e03f2a0f9f0f9a2c2a55df6/Screenshot-2023-10-09-at-2.13.29-PM.png" />
            
            </figure><p>Stream multiplexing and concurrency are powerful features of HTTP/2. They enable more efficient usage of a single TCP connection. HTTP/2 optimizes resources fetching especially when coupled with <a href="/better-http-2-prioritization-for-a-faster-web/">prioritization</a>. On the flip side, making it easy for clients to launch large amounts of parallel work can increase the peak demand for server resources when compared to HTTP/1.1. This is an obvious vector for denial-of-service.</p><p>In order to provide some guardrails, HTTP/2 provides a notion of maximum active <a href="https://www.rfc-editor.org/rfc/rfc9113#section-5.1.2">concurrent streams</a>. The <a href="https://www.rfc-editor.org/rfc/rfc9113#SETTINGS_MAX_FRAME_SIZE">SETTINGS_MAX_CONCURRENT_STREAMS</a> parameter allows a server to advertise its limit of concurrency. For example, if the server states a limit of 100, then only 100 requests can be active at any time. If a client attempts to open a stream above this limit, it must be rejected by the server using a <a href="https://www.rfc-editor.org/rfc/rfc9113#section-6.4">RST_STREAM</a> frame. Stream rejection does not affect the other in-flight streams on the connection.</p><p>The true story is a little more complicated. Streams have a <a href="https://www.rfc-editor.org/rfc/rfc9113#section-5.1">lifecycle</a>. Below is a diagram of the HTTP/2 stream state machine. Client and server manage their own views of the state of a stream. HEADERS, DATA and RST_STREAM frames trigger transitions when they are sent or received. Although the views of the stream state are independent, they are synchronized.</p><p>HEADERS and DATA frames include an END_STREAM flag, that when set to the value 1 (true), can trigger a state transition.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2iRGsrf6eBGkrJ0rpqZtSx/a4c47fc7f29ec562660aa75c3e26e13c/Request-stream-states.png" />
            
            </figure><p>Let's work through this with an example of a GET request that has no message content. The client sends the request as a HEADERS frame with the END_STREAM flag set to 1. The client first transitions the stream from <b>idle</b> to <b>open</b> state, then immediately transitions into <b>half-closed</b> state. The client half-closed state means that it can no longer send HEADERS or DATA, only <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-6.9">WINDOW_UPDATE</a>, <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-6.3">PRIORITY</a> or RST_STREAM frames. It can receive any frame however.</p><p>Once the server receives and parses the HEADERS frame, it transitions the stream state from idle to open and then half-closed, so it matches the client. The server half-closed state means it can send any frame but receive only WINDOW_UPDATE, PRIORITY or RST_STREAM frames.</p><p>The response to the GET contains message content, so the server sends HEADERS with END_STREAM flag set to 0, then DATA with END_STREAM flag set to 1. The DATA frame triggers the transition of the stream from <b>half-closed</b> to <b>closed</b> on the server. When the client receives it, it also transitions to closed. Once a stream is closed, no frames can be sent or received.</p><p>Applying this lifecycle back into the context of concurrency, HTTP/2 <a href="https://www.rfc-editor.org/rfc/rfc9113#section-5.1.2-2">states</a>:</p><p><i>Streams that are in the "open" state or in either of the "half-closed" states count toward the maximum number of streams that an endpoint is permitted to open. Streams in any of these three states count toward the limit advertised in the</i> <a href="https://www.rfc-editor.org/rfc/rfc9113#SETTINGS_MAX_CONCURRENT_STREAMS"><i>SETTINGS_MAX_CONCURRENT_STREAMS</i></a> <i>setting.</i></p><p>In theory, the concurrency limit is useful. However, there are practical factors that hamper its effectiveness— which we will cover later in the blog.</p>
    <div>
      <h3>HTTP/2 request cancellation</h3>
      <a href="#http-2-request-cancellation">
        
      </a>
    </div>
    <p>Earlier, we talked about client cancellation of in-flight requests. HTTP/2 supports this in a much more efficient way than HTTP/1.1. Rather than needing to tear down the whole connection, a client can send a RST_STREAM frame for a single stream. This instructs the server to stop processing the request and to abort the response, which frees up server resources and avoids wasting bandwidth.</p><p>Let's consider our previous example of 3 requests. This time the client cancels the request on stream 1 after all of the HEADERS have been sent. The server parses this RST_STREAM frame before it is ready to serve the response and instead only responds to stream 3 and 5:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7dnYO0saZeIgGlocFRxKLb/45b3c1197559ee9f5547efad0a88b00a/Screenshot-2023-10-09-at-2.12.04-PM.png" />
            
            </figure><p>Request cancellation is a useful feature. For example, when scrolling a webpage with multiple images, a web browser can cancel images that fall outside the viewport, meaning that images entering it can load faster. HTTP/2 makes this behaviour a lot more efficient compared to HTTP/1.1.</p><p>A request stream that is canceled, rapidly transitions through the stream lifecycle. The client's HEADERS with END_STREAM flag set to 1 transitions the state from <b>idle</b> to <b>open</b> to <b>half-closed</b>, then RST_STREAM immediately causes a transition from <b>half-closed</b> to <b>closed.</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4XHuuWwKaQkVyDclt2ktaT/983c5d5531c2987a90382548e8618f50/Request-stream-states-reset.png" />
            
            </figure><p>Recall that only streams that are in the open or half-closed state contribute to the stream concurrency limit. When a client cancels a stream, it instantly gets the ability to open another stream in its place and can send another request immediately. This is the crux of what makes <a href="https://www.cve.org/CVERecord?id=CVE-2023-44487">CVE-2023-44487</a> work.</p>
    <div>
      <h3>Rapid resets leading to denial of service</h3>
      <a href="#rapid-resets-leading-to-denial-of-service">
        
      </a>
    </div>
    <p>HTTP/2 request cancellation can be abused to rapidly reset an unbounded number of streams. When an HTTP/2 server is able to process client-sent RST_STREAM frames and tear down state quickly enough, such rapid resets do not cause a problem. Where issues start to crop up is when there is any kind of delay or lag in tidying up. The client can churn through so many requests that a backlog of work accumulates, resulting in excess consumption of resources on the server.</p><p>A common HTTP deployment architecture is to run an HTTP/2 proxy or load-balancer in front of other components. When a client request arrives it is quickly dispatched and the actual work is done as an asynchronous activity somewhere else. This allows the proxy to handle client traffic very efficiently. However, this separation of concerns can make it hard for the proxy to tidy up the in-process jobs. Therefore, these deployments are more likely to encounter issues from rapid resets.</p><p>When Cloudflare's <a href="https://www.rfc-editor.org/rfc/rfc9110#section-3.7-6">reverse proxies</a> process incoming HTTP/2 client traffic, they copy the data from the connection’s socket into a buffer and process that buffered data in order. As each request is read (HEADERS and DATA frames) it is dispatched to an upstream service. When RST_STREAM frames are read, the local state for the request is torn down and the upstream is notified that the request has been canceled. Rinse and repeat until the entire buffer is consumed. However, this logic can be abused: when a malicious client started sending an enormous chain of requests and resets at the start of a connection, our servers would eagerly read them all and create stress on the upstream servers to the point of being unable to process any new incoming request.</p><p>Something that is important to highlight is that stream concurrency on its own cannot mitigate rapid reset. The client can churn requests to create high request rates no matter the server's chosen value of <a href="https://www.rfc-editor.org/rfc/rfc9113#SETTINGS_MAX_CONCURRENT_STREAMS">SETTINGS_MAX_CONCURRENT_STREAMS</a>.</p>
    <div>
      <h3>Rapid Reset dissected</h3>
      <a href="#rapid-reset-dissected">
        
      </a>
    </div>
    Here's an example of rapid reset reproduced using a proof-of-concept client attempting to make a total of 1000 requests. I've used an off-the-shelf server without any mitigations; listening on port 443 in a test environment. The traffic is dissected using Wireshark and filtered to show only HTTP/2 traffic for clarity. <a href="http://staging.blog.mrk.cfdata.org/content/images/rapidreset.pcapng">Download the pcap</a> to follow along.
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3wv6U0bsO7wQ2Ofw0qp5f9/9d974117608d62c9eef6e276890f336b/Untitled--2-.png" />
            
            </figure><p>It's a bit difficult to see, because there are a lot of frames. We can get a quick summary via Wireshark's Statistics &gt; HTTP2 tool:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1STsop3XklVVe7phIL1mg0/69bb1269474936146e529c969aaecd18/Screenshot-2023-10-09-at-10.50.42-PM.png" />
            
            </figure><p>The first frame in this trace, in packet 14, is the server's SETTINGS frame, which advertises a maximum stream concurrency of 100. In packet 15, the client sends a few control frames and then starts making requests that are rapidly reset. The first HEADERS frame is 26 bytes long, all subsequent HEADERS are only 9 bytes. This size difference is due to a compression technology called <a href="/hpack-the-silent-killer-feature-of-http-2/">HPACK</a>. In total, packet 15 contains 525 requests, going up to stream 1051.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/JkezErQ90qEV2L0JuKNuK/c75695ef3a2b192eb1cb7a93a546cb7f/Untitled--3-.png" />
            
            </figure><p>Interestingly, the RST_STREAM for stream 1051 doesn't fit in packet 15, so in packet 16 we see the server respond with a 404 response.  Then in packet 17 the client does send the RST_STREAM, before moving on to sending the remaining 475 requests.</p><p>Note that although the server advertised 100 concurrent streams, both packets sent by the client sent a lot more HEADERS frames than that. The client did not have to wait for any return traffic from the server, it was only limited by the size of the packets it could send. No server RST_STREAM frames are seen in this trace, indicating that the server did not observe a concurrent stream violation.</p>
    <div>
      <h2>Impact on customers</h2>
      <a href="#impact-on-customers">
        
      </a>
    </div>
    <p>As mentioned above, as requests are canceled, upstream services are notified and can abort requests before wasting too many resources on it. This was the case with this attack, where most malicious requests were never forwarded to the origin servers. However, the sheer size of these attacks did cause some impact.</p><p>First, as the rate of incoming requests reached peaks never seen before, we had reports of increased levels of 502 errors seen by clients. This happened on our most impacted data centers as they were struggling to process all the requests. While our network is meant to deal with large attacks, this particular vulnerability exposed a weakness in our infrastructure. Let's dig a little deeper into the details, focusing on how incoming requests are handled when they hit one of our data centers:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2FNw47et7A6A8yQ8FTJWeB/4a244ae8faed2fca1afc45de7ccb2600/Untitled-2023-10-04-1953.png" />
            
            </figure><p>We can see that our infrastructure is composed of a chain of different proxy servers with different responsibilities. In particular, when a client connects to Cloudflare to send HTTPS traffic, it first hits our TLS decryption proxy: it decrypts TLS traffic, processes HTTP 1, 2 or 3 traffic, then forwards it to our "business logic" proxy. This one is responsible for loading all the settings for each customer, then routing the requests correctly to other upstream services — and more importantly in our case, <b>it is also responsible for security features</b>. This is where L7 attack mitigation is processed.</p><p>The problem with this attack vector is that it manages to send a lot of requests very quickly in every single connection. Each of them had to be forwarded to the business logic proxy before we had a chance to block it. As the request throughput became higher than our proxy capacity, the pipe connecting these two services reached its saturation level in some of our servers.</p><p>When this happens, the TLS proxy cannot connect anymore to its upstream proxy, this is why some clients saw a bare "502 Bad Gateway" error during the most serious attacks. It is important to note that, as of today, the logs used to create HTTP analytics are also emitted by our business logic proxy. The consequence of that is that these errors are not visible in the Cloudflare dashboard. Our internal dashboards show that about 1% of requests were impacted during the initial wave of attacks (before we implemented mitigations), with peaks at around 12% for a few seconds during the most serious one on August 29th. The following graph shows the ratio of these errors over a two hours while this was happening:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/lNZyUoAWzVfyQ66xIPJ9i/d9907d299155b8b6e80c9de3f4a4f032/imageLikeEmbed.png" />
            
            </figure><p>We worked to reduce this number dramatically in the following days, as detailed later on in this post. Both thanks to changes in our stack and to our mitigation that reduce the size of these attacks considerably, this number today is effectively zero.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6PDIKg6O07UYWjQk30YkIH/e542a06392b680737ec2256c0919a33e/imageLikeEmbed--2-.png" />
            
            </figure>
    <div>
      <h3>499 errors and the challenges for HTTP/2 stream concurrency</h3>
      <a href="#499-errors-and-the-challenges-for-http-2-stream-concurrency">
        
      </a>
    </div>
    <p>Another symptom reported by some customers is an increase in 499 errors. The reason for this is a bit different and is related to the maximum stream concurrency in a HTTP/2 connection detailed earlier in this post.</p><p>HTTP/2 settings are exchanged at the start of a connection using SETTINGS frames. In the absence of receiving an explicit parameter, default values apply. Once a client establishes an HTTP/2 connection, it can wait for a server's SETTINGS (slow) or it can assume the default values and start making requests (fast). For SETTINGS_MAX_CONCURRENT_STREAMS, the default is effectively unlimited (stream IDs use a 31-bit number space, and requests use odd numbers, so the actual limit is 1073741824). The specification recommends that a server offer no fewer than 100 streams. Clients are generally biased towards speed, so don't tend to wait for server settings, which creates a bit of a race condition. Clients are taking a gamble on what limit the server might pick; if they pick wrong the request will be rejected and will have to be retried. Gambling on 1073741824 streams is a bit silly. Instead, a lot of clients decide to limit themselves to issuing 100 concurrent streams, with the hope that servers followed the specification recommendation. Where servers pick something below 100, this client gamble fails and streams are reset.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ZdAxnNVz1PsuG1BCE3wsT/d4b02d4d1b8f6cf8ecebdddbed194c74/Untitled-2023-10-04-1953--1-.png" />
            
            </figure><p>There are many reasons a server might reset a stream beyond concurrency limit overstepping. HTTP/2 is strict and requires a stream to be closed when there are parsing or logic errors. In 2019, Cloudflare developed several mitigations in response to <a href="/on-the-recent-http-2-dos-attacks/">HTTP/2 DoS vulnerabilities</a>. Several of those vulnerabilities were caused by a client misbehaving, leading the server to reset a stream. A very effective strategy to clamp down on such clients is to count the number of server resets during a connection, and when that exceeds some threshold value, close the connection with a <a href="https://www.rfc-editor.org/rfc/rfc9113#section-6.8">GOAWAY</a> frame. Legitimate clients might make one or two mistakes in a connection and that is acceptable. A client that makes too many mistakes is probably either broken or malicious and closing the connection addresses both cases.</p><p>While responding to DoS attacks enabled by <a href="https://www.cve.org/CVERecord?id=CVE-2023-44487">CVE-2023-44487</a>, Cloudflare reduced maximum stream concurrency to 64. Before making this change, we were unaware that clients don't wait for SETTINGS and instead assume a concurrency of 100. Some web pages, such as an image gallery, do indeed cause a browser to send 100 requests immediately at the start of a connection. Unfortunately, the 36 streams above our limit all needed to be reset, which triggered our counting mitigations. This meant that we closed connections on legitimate clients, leading to a complete page load failure. As soon as we realized this interoperability issue, we changed the maximum stream concurrency to 100.</p>
    <div>
      <h2>Actions from the Cloudflare side</h2>
      <a href="#actions-from-the-cloudflare-side">
        
      </a>
    </div>
    <p>In 2019 several <a href="/on-the-recent-http-2-dos-attacks/">DoS vulnerabilities</a> were uncovered related to implementations of HTTP/2. Cloudflare developed and deployed a series of detections and mitigations in response.  <a href="https://www.cve.org/CVERecord?id=CVE-2023-44487">CVE-2023-44487</a> is a different manifestation of HTTP/2 vulnerability. However, to mitigate it we were able to extend the existing protections to monitor client-sent RST_STREAM frames and close connections when they are being used for abuse. Legitimate client uses for RST_STREAM are unaffected.</p><p>In addition to a direct fix, we have implemented several improvements to the server's HTTP/2 frame processing and request dispatch code. Furthermore, the business logic server has received improvements to queuing and scheduling that reduce unnecessary work and improve cancellation responsiveness. Together these lessen the impact of various potential abuse patterns as well as giving more room to the server to process requests before saturating.</p>
    <div>
      <h3>Mitigate attacks earlier</h3>
      <a href="#mitigate-attacks-earlier">
        
      </a>
    </div>
    <p>Cloudflare already had systems in place to efficiently mitigate very large attacks with less expensive methods. One of them is named "IP Jail". For hyper volumetric attacks, this system collects the client IPs participating in the attack and stops them from connecting to the attacked property, either at the IP level, or in our TLS proxy. This system however needs a few seconds to be fully effective; during these precious seconds, the origins are already protected but our infrastructure still needs to absorb all HTTP requests. As this new botnet has effectively no ramp-up period, we need to be able to neutralize attacks before they can become a problem.</p><p>To achieve this we expanded the IP Jail system to protect our entire infrastructure: once an IP is "jailed", not only it is blocked from connecting to the attacked property, we also forbid the corresponding IPs from using HTTP/2 to any other domain on Cloudflare for some time. As such protocol abuses are not possible using HTTP/1.x, this limits the attacker's ability to run large attacks, while any legitimate client sharing the same IP would only see a very small performance decrease during that time. IP based mitigations are a very blunt tool — this is why we have to be extremely careful when using them at that scale and seek to avoid false positives as much as possible. Moreover, the lifespan of a given IP in a botnet is usually short so any long term mitigation is likely to do more harm than good. The following graph shows the churn of IPs in the attacks we witnessed:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3tsViCTkDispBbBmIY57vy/3c49903f3c4a1ac60f98efae6c1e3fb4/ip-churn.png" />
            
            </figure><p>As we can see, many new IPs spotted on a given day disappear very quickly afterwards.</p><p>As all these actions happen in our TLS proxy at the beginning of our HTTPS pipeline, this saves considerable resources compared to our regular L7 mitigation system. This allowed us to weather these attacks much more smoothly and now the number of random 502 errors caused by these botnets is down to zero.</p>
    <div>
      <h3>Observability improvements</h3>
      <a href="#observability-improvements">
        
      </a>
    </div>
    <p>Another front on which we are making change is <a href="https://www.cloudflare.com/learning/performance/what-is-observability/">observability</a>. Returning errors to clients without being visible in customer analytics is unsatisfactory. Fortunately, a project has been underway to overhaul these systems since long before the recent attacks. It will eventually allow each service within our infrastructure to log its own data, instead of relying on our business logic proxy to consolidate and emit log data. This incident underscored the importance of this work, and we are redoubling our efforts.</p><p>We are also working on better connection-level logging, allowing us to spot such protocol abuses much more quickly to improve our DDoS mitigation capabilities.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>While this was the latest record-breaking attack, we know it won’t be the last. As attacks continue to become more sophisticated, Cloudflare works relentlessly to proactively identify new threats — deploying countermeasures to our global network so that our millions of customers are immediately and automatically protected.</p><p>Cloudflare has provided free, unmetered and unlimited DDoS protection to all of our customers since 2017. In addition, we offer a range of additional security features to suit the needs of organizations of all sizes. <a href="https://www.cloudflare.com/h2">Contact us</a> if you’re unsure whether you’re protected or want to understand how you can be.</p> ]]></content:encoded>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[Vulnerabilities]]></category>
            <category><![CDATA[Trends]]></category>
            <category><![CDATA[Attacks]]></category>
            <category><![CDATA[Security]]></category>
            <guid isPermaLink="false">3WjbDYiA84ghLhFzgsMfYp</guid>
            <dc:creator>Lucas Pardue</dc:creator>
            <dc:creator>Julien Desgats</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare mitigates record-breaking 71 million request-per-second DDoS attack]]></title>
            <link>https://blog.cloudflare.com/cloudflare-mitigates-record-breaking-71-million-request-per-second-ddos-attack/</link>
            <pubDate>Mon, 13 Feb 2023 18:37:51 GMT</pubDate>
            <description><![CDATA[ This was a weekend of record-breaking DDoS attacks. Over the weekend, Cloudflare detected and mitigated dozens of hyper-volumetric DDoS attacks. The majority of attacks peaked in the ballpark of 50-70 million requests per second (rps) with the largest exceeding 71 million rps ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4zzsCqug2fzQ1LZ9cDhPPc/f41f616c5972ce12c9dedbb8844a2147/DDoS-protection-1.png" />
            
            </figure><p>This was a weekend of record-breaking <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS attacks</a>. Over the weekend, Cloudflare detected and mitigated dozens of <i>hyper-volumetric</i> DDoS attacks. The majority of attacks peaked in the ballpark of 50-70 million requests per second (rps) with the largest exceeding 71 million rps. This is the largest reported HTTP DDoS attack on record, more than 54% higher than the previous reported record of 46M rps in June 2022.</p><p>The attacks were HTTP/2-based and targeted websites protected by Cloudflare. They originated from over 30,000 IP addresses. Some of the attacked websites included a popular gaming provider, cryptocurrency companies, hosting providers, and cloud computing platforms. The attacks originated from numerous cloud providers, and we have been working with them to crack down on the botnet.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/TDtAM4ae2w8i3WkfQqd08/737282a05169368edb3dfaaa54b7afaa/1711-1.png" />
            
            </figure><p>Record breaking attack: DDoS attack exceeding 71 million requests per second</p><p>Over the past year, we’ve seen more attacks originate from cloud computing providers. For this reason, we will be providing service providers that own their own autonomous system a <a href="/botnet-threat-feed-for-isp/">free Botnet threat feed</a>. The feed will provide service providers threat intelligence about their own IP space; attacks originating from within their autonomous system. Service providers that operate their own IP space can now <a href="https://www.cloudflare.com/lp/botnet-threat-feed/">sign up</a> to the early access waiting list.</p>
    <div>
      <h3>Is this related to the Super Bowl or Killnet?</h3>
      <a href="#is-this-related-to-the-super-bowl-or-killnet">
        
      </a>
    </div>
    <p>No. This campaign of attacks arrives less than two weeks after the <a href="/uptick-in-healthcare-organizations-experiencing-targeted-ddos-attacks/">Killnet DDoS campaign that targeted healthcare websites</a>. Based on the methods and targets, we do not believe that these recent attacks are related to the healthcare campaign. Furthermore, yesterday was the US Super Bowl, and we also do not believe that this attack campaign is related to the game event.</p>
    <div>
      <h3>What are DDoS attacks?</h3>
      <a href="#what-are-ddos-attacks">
        
      </a>
    </div>
    <p>Distributed Denial of Service attacks are cyber attacks that aim to take down Internet properties and make them unavailable for users. These types of cyberattacks can be very efficient against unprotected websites and they can be very inexpensive for the attackers to execute.</p><p>An HTTP DDoS attack usually involves a <a href="https://www.cloudflare.com/learning/ddos/http-flood-ddos-attack/">flood of HTTP requests</a> towards the target website. The attacker’s objective is to bombard the website with more requests than it can handle. Given a sufficiently high amount of requests, the website’s server will not be able to process all of the attack requests along with the <i>legitimate</i> user requests. Users will experience this as website-load delays, timeouts, and eventually not being able to connect to their desired websites at all.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/41VYHEH1KGaJHxqfyuCUX3/62edd1a178f035d50b0ec325fde9d074/1711-2.png" />
            
            </figure><p>Illustration of a DDoS attack</p><p>To make attacks larger and more complicated, attackers usually leverage a network of bots — a <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-botnet/"><i>botnet</i></a>. The attacker will orchestrate the botnet to bombard the victim’s websites with HTTP requests. A sufficiently large and powerful botnet can generate very large attacks as we’ve seen in this case.</p><p>However, building and operating botnets requires a lot of investment and expertise. What is the average Joe to do? Well, an average Joe that wants to launch a DDoS attack against a website doesn’t need to start from scratch. They can hire one of numerous DDoS-as-a-Service platforms for as little as $30 per month. The more you pay, the larger and longer of an attack you’re going to get.</p>
    <div>
      <h3>Why DDoS attacks?</h3>
      <a href="#why-ddos-attacks">
        
      </a>
    </div>
    <p>Over the years, it has become easier, cheaper, and more accessible for attackers and attackers-for-hire to launch DDoS attacks. But as easy as it has become for the attackers, we want to make sure that it is even easier - and free - for defenders of organizations of all sizes to protect themselves against DDoS attacks of all types.</p><p>Unlike <a href="https://www.cloudflare.com/learning/security/ransomware/what-is-ransomware/">Ransomware</a> attacks, <a href="https://www.cloudflare.com/learning/ddos/ransom-ddos-attack/">Ransom DDoS attacks</a> don’t require an actual system intrusion or a foothold within the targeted network. Usually Ransomware attacks start once an employee naively clicks an email link that installs and propagates the malware. There’s no need for that with DDoS attacks. They are more like a hit-and-run attack. All a DDoS attacker needs to know is the website’s address and/or IP address.</p>
    <div>
      <h3>Is there an increase in DDoS attacks?</h3>
      <a href="#is-there-an-increase-in-ddos-attacks">
        
      </a>
    </div>
    <p>Yes. The size, sophistication, and frequency of attacks has been increasing over the past months. In our latest <a href="/ddos-threat-report-2022-q4/">DDoS threat report</a>, we saw that the amount of HTTP DDoS attacks increased by 79% year-over-year. Furthermore, the amount of volumetric attacks exceeding 100 Gbps grew by 67% quarter-over-quarter (QoQ), and the number of attacks lasting more than three hours increased by 87% QoQ.</p><p>But it doesn’t end there. The audacity of attackers has been increasing as well. In our latest DDoS threat report, we saw that Ransom DDoS attacks steadily increased throughout the year. They peaked in November 2022 where one out of every four surveyed customers reported being subject to Ransom DDoS attacks or threats.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1dtJk2dHnMO7XbQ7RQM79W/02faf2f2a4494adf85062060581c1644/1711-3.png" />
            
            </figure><p>Distribution of Ransom DDoS attacks by month</p>
    <div>
      <h3>Should I be worried about DDoS attacks?</h3>
      <a href="#should-i-be-worried-about-ddos-attacks">
        
      </a>
    </div>
    <p>Yes. If your website, server, or networks are not protected against volumetric DDoS attacks using a cloud service that provides automatic detection and mitigation, we really recommend that you consider it.</p><p>Cloudflare customers shouldn’t be worried, but should be aware and prepared. Below is a list of recommended steps to ensure your security posture is optimized.</p>
    <div>
      <h3>What steps should I take to defend against DDoS attacks?</h3>
      <a href="#what-steps-should-i-take-to-defend-against-ddos-attacks">
        
      </a>
    </div>
    <p>Cloudflare’s systems have been automatically detecting and <a href="https://www.cloudflare.com/learning/ddos/ddos-mitigation/">mitigating</a> these DDoS attacks.</p><p>Cloudflare offers many features and capabilities that you may already have access to but may not be using. So as extra precaution, we recommend taking advantage of these capabilities to improve and optimize your security posture:</p><ol><li><p>Ensure all <a href="https://developers.cloudflare.com/ddos-protection/managed-rulesets/">DDoS Managed Rules</a> are set to default settings (High sensitivity level and mitigation actions) for optimal DDoS activation.</p></li><li><p><a href="https://www.cloudflare.com/plans/enterprise/">Cloudflare Enterprise</a> customers that are subscribed to the Advanced DDoS Protection service should consider enabling <a href="https://developers.cloudflare.com/ddos-protection/managed-rulesets/adaptive-protection/">Adaptive DDoS Protection</a>, which mitigates attacks more intelligently based on your unique traffic patterns.</p></li><li><p>Deploy <a href="https://developers.cloudflare.com/firewall/cf-firewall-rules/">firewall rules</a> and <a href="https://developers.cloudflare.com/waf/rate-limiting-rules/">rate limiting rules</a> to enforce a combined positive and negative security model. Reduce the traffic allowed to your website based on your known usage.</p></li><li><p>Ensure your origin is not exposed to the public Internet (i.e., <a href="https://developers.cloudflare.com/fundamentals/get-started/setup/allow-cloudflare-ip-addresses/">only enable access to Cloudflare IP addresses</a>). As an extra security precaution, we recommend contacting your hosting provider and requesting new origin server IPs if they have been targeted directly in the past.</p></li><li><p>Customers with access to <a href="https://developers.cloudflare.com/fundamentals/global-configurations/lists/ip-lists/#managed-ip-lists">Managed IP Lists</a> should consider leveraging those lists in firewall rules. Customers with <a href="https://developers.cloudflare.com/bots/get-started/bm-subscription/">Bot Management</a> should consider leveraging the bot scores within the firewall rules.</p></li><li><p>Enable <a href="https://developers.cloudflare.com/cache/">caching</a> as much as possible to reduce the strain on your origin servers, and when using <a href="https://workers.cloudflare.com/">Workers</a>, avoid overwhelming your origin server with more subrequests than necessary.</p></li><li><p>Enable <a href="https://developers.cloudflare.com/ddos-protection/reference/alerts/">DDoS alerting</a> to improve your response time.</p></li></ol>
    <div>
      <h3>Preparing for the next DDoS wave</h3>
      <a href="#preparing-for-the-next-ddos-wave">
        
      </a>
    </div>
    <p>Defending against DDoS attacks is critical for organizations of all sizes. While attacks may be initiated by humans, they are executed by bots — and to play to win, you must fight bots with bots. Detection and mitigation must be automated as much as possible, because relying solely on humans to mitigate in real time puts defenders at a disadvantage. Cloudflare’s automated systems constantly detect and mitigate DDoS attacks for our customers, so they don’t have to. This automated approach, combined with our wide breadth of security capabilities, lets customers tailor the protection to their needs.</p><p>We've been providing <a href="/unmetered-mitigation/">unmetered and unlimited DDoS protection</a> for free to all of our customers since 2017, when we pioneered the concept. Cloudflare's mission is to help build a better Internet. A better Internet is one that is more secure, faster, and reliable for everyone - even in the face of DDoS attacks.</p> ]]></content:encoded>
            <category><![CDATA[Attacks]]></category>
            <category><![CDATA[Trends]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[Botnet]]></category>
            <guid isPermaLink="false">4Z0r3SJQWb9Ja607hPbCW0</guid>
            <dc:creator>Omer Yoachimik</dc:creator>
            <dc:creator>Julien Desgats</dc:creator>
            <dc:creator>Alex Forster</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Location-Aware DDoS Protection]]></title>
            <link>https://blog.cloudflare.com/location-aware-ddos-protection/</link>
            <pubDate>Mon, 11 Jul 2022 12:57:54 GMT</pubDate>
            <description><![CDATA[ Location-Aware DDoS Protection is now available in beta for Cloudflare Enterprise customers that are subscribed to the Advanced DDoS service ]]></description>
            <content:encoded><![CDATA[ <p></p><p>We’re thrilled to introduce Cloudflare’s Location-Aware DDoS Protection.</p><p><a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">Distributed Denial of Service (DDoS) attacks</a> are cyber attacks that aim to make your Internet property unavailable by flooding it with more traffic than it can handle. For this reason, attackers usually aim to generate as much attack traffic as they can — from as many locations as they can. With Location-Aware DDoS Protection, we take this <i>distributed</i> characteristic of the attack, that is thought of being advantageous for the attacker, and turn it on its back — making it into a disadvantage.</p><p>Location-Aware DDoS Protection is now available in beta for Cloudflare Enterprise customers that are subscribed to the Advanced DDoS service.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ttg8VXMQfmMLeEAiy9pI7/6194e4700ab620c56bea9828865b8168/image4-2.png" />
            
            </figure>
    <div>
      <h3>Distributed attacks lose their edge</h3>
      <a href="#distributed-attacks-lose-their-edge">
        
      </a>
    </div>
    <p>Cloudflare’s Location-Aware DDoS Protection takes the attacker’s advantage and uses it against them. By learning where your traffic comes from, the system becomes location-aware and constantly asks “Does it make sense for <i>your</i> website?” when seeing new traffic.</p><p>For example, if you operate an <a href="https://www.cloudflare.com/ecommerce/">e-commerce website</a> that mostly serves the German consumer, then most of your traffic would most likely originate from within Germany, some from neighboring European countries, and a decreasing amount as we expand globally to other countries and geographies. If sudden spikes of traffic arrive from unexpected locations outside your main geographies, the system will flag and mitigate the unwanted traffic.</p><p>Location-Aware DDoS Protection also leverages Cloudflare’s <a href="https://developers.cloudflare.com/bots/concepts/bot-score/#machine-learning">Machine Learning models</a> to identify traffic that is likely automated. This is used as an additional signal to provide more accurate protection.</p>
    <div>
      <h3>Enabling Location-Aware Protection</h3>
      <a href="#enabling-location-aware-protection">
        
      </a>
    </div>
    <p>Enterprise customers subscribed to the Advanced DDoS service can customize and enable the Location-Aware DDoS Protection system. By default, the system will only show what it thinks is suspicious traffic based on your last 7-day P95 rates, bucketed by client country and region (recalculated every 24 hours).</p><p>Customers can view what the system flagged in the <a href="https://dash.cloudflare.com/?to=/:account/:zone/security"><b>Security Overview</b> dashboard</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Kb6nbcrtf6KOE4Yi7k4jw/07ebb6b1c838be3adbc4ba1e2fbe008b/image1-4.png" />
            
            </figure><p>Location-Aware DDoS Protection is exposed to customers as a new HTTP DDoS Managed rule within the existing ruleset. To enable it, change the action to <i>Managed Challenge</i> or <i>Block</i>. Customers can adjust its sensitivity level to define how much tolerance you permit for traffic that deviates from your observed geographies. The lower the sensitivity, the higher the tolerance.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/yEvUggqYGn2yK8t4QTswP/018b63dfdf8fb95b597a29d499e23661/image5-3.png" />
            
            </figure><p>To learn how to view flagged traffic and how to configure the Location-Aware DDoS Protection, visit our <a href="https://developers.cloudflare.com/ddos-protection/managed-rulesets/http/location-aware-protection/">developer docs site</a>.</p>
    <div>
      <h3>Making the impact of DDoS attacks a thing of the past</h3>
      <a href="#making-the-impact-of-ddos-attacks-a-thing-of-the-past">
        
      </a>
    </div>
    <p>Our mission at Cloudflare is to help build a better Internet. The DDoS Protection team’s vision is derived from this mission: our goal is to make the impact of DDoS attacks a thing of the past. Location-aware protection is only the first step to making Cloudflare’s <a href="https://www.cloudflare.com/ddos/">DDoS protection</a> even more intelligent, sophisticated, and tailored to individual needs.</p><p>Not using Cloudflare yet? <a href="https://dash.cloudflare.com/sign-up">Start now</a> with our Free and Pro <a href="https://www.cloudflare.com/plans/">plans</a> to protect your websites, or <a href="https://www.cloudflare.com/magic-transit/">contact us</a> to learn more about the Enterprise Advanced DDoS Protection package.</p> ]]></content:encoded>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[Attacks]]></category>
            <category><![CDATA[Advanced DDoS]]></category>
            <category><![CDATA[Managed Rules]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">4baPBZiCeKjLcASgOUOAQO</guid>
            <dc:creator>Omer Yoachimik</dc:creator>
            <dc:creator>Julien Desgats</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare blocks 15M rps HTTPS DDoS attack]]></title>
            <link>https://blog.cloudflare.com/15m-rps-ddos-attack/</link>
            <pubDate>Wed, 27 Apr 2022 14:02:36 GMT</pubDate>
            <description><![CDATA[ Earlier this month, Cloudflare’s systems automatically detected and mitigated a 15.3 million request-per-second (rps) DDoS attack — one of the largest HTTPS DDoS attacks on record ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/15pS0SoL6zLgxPdnGw7PmP/f5b2d75a068116b439703a0953c6a332/1109-1.png" />
            
            </figure><p>Earlier this month, Cloudflare’s systems automatically detected and mitigated a 15.3 million request-per-second (rps) <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS attack</a> — one of the largest HTTPS DDoS attacks on record.</p><p>While this isn’t the <a href="/cloudflare-thwarts-17-2m-rps-ddos-attack-the-largest-ever-reported/">largest application-layer attack we’ve seen</a>, it is the largest we’ve seen over HTTP<b>S</b>. <a href="https://www.cloudflare.com/learning/ssl/what-is-https/">HTTPS</a> DDoS attacks are more expensive in terms of required computational resources because of the higher cost of establishing a secure <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/">TLS</a> encrypted connection. Therefore it costs the attacker more to launch the attack, and for the victim to mitigate it. We’ve seen very large attacks in the past over (unencrypted) HTTP, but this attack stands out because of the resources it required at its scale.</p><p>The attack, lasting less than 15 seconds, targeted a Cloudflare customer on the Professional (Pro) plan operating a crypto launchpad. Crypto launchpads are used to surface Decentralized Finance projects to potential investors. The attack was launched by a botnet that we’ve been observing — we’ve already seen large attacks as high as 10M rps matching the same attack fingerprint.</p><p>Cloudflare customers are protected against this botnet and do not need to take any action.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2dKJWfgemmjgOJyK68Z6fL/40701eeb1be466369ab574c19545f676/1109-2.png" />
            
            </figure>
    <div>
      <h3>The attack</h3>
      <a href="#the-attack">
        
      </a>
    </div>
    <p>What’s interesting is that the attack mostly came from data centers. A change from residential network Internet Service Providers (ISPs) to cloud compute ISPs.</p><p>This attack was launched from a botnet of approximately 6,000 unique bots. It originated from 112 countries around the world. Almost 15% of the attack traffic originated from Indonesia, followed by Russia, Brazil, India, Colombia, and the United States.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1qU4zUvk8NNKX4qrug6DGO/4b7e320c480cdcf816e9ee5ce7e42fe5/1109-3.png" />
            
            </figure><p>Within those countries, the attack originated from over 1,300 different networks. The top networks included the German provider Hetzner Online GmbH (Autonomous System Number 24940), Azteca Comunicaciones Colombia (ASN 262186), OVH in France (ASN 16276), as well as other cloud providers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/lnPP8NYqk8QO0OkT9TS9L/2f550be64dc33590aeaeb1bececea348/1109-4.png" />
            
            </figure>
    <div>
      <h3>How this attack was automatically detected and mitigated</h3>
      <a href="#how-this-attack-was-automatically-detected-and-mitigated">
        
      </a>
    </div>
    <p>To defend organizations against DDoS attacks, we built and operate software-defined systems that run autonomously. They automatically detect and mitigate DDoS attacks across our entire network — and just as in this case, the attack was automatically detected and mitigated without any human intervention.</p><p>Our system starts by sampling traffic asynchronously; it then analyzes the samples and applies mitigations when needed.</p>
    <div>
      <h3>Sampling</h3>
      <a href="#sampling">
        
      </a>
    </div>
    <p>Initially, traffic is routed through the Internet via <a href="https://www.cloudflare.com/learning/cdn/glossary/anycast-network/">BGP Anycast</a> to the nearest <a href="https://www.cloudflare.com/network/">Cloudflare data centers</a> that are located in over 270 cities around the world. Once the traffic reaches our data center, our DDoS systems sample it asynchronously allowing for out-of-path analysis of traffic without introducing latency penalties.</p>
    <div>
      <h3>Analysis and mitigation</h3>
      <a href="#analysis-and-mitigation">
        
      </a>
    </div>
    <p>The analysis is done using data streaming algorithms. HTTP request samples are compared to conditional fingerprints, and multiple real-time signatures are created based on dynamic masking of various request fields and metadata. Each time another request matches one of the signatures, a counter is increased. When the activation threshold is reached for a given signature, a mitigation rule is compiled and pushed inline. The mitigation rule includes the real-time signature and the mitigation action, e.g. block.</p><p>Cloudflare customers can also customize the settings of the DDoS protection systems by tweaking the <a href="/http-ddos-managed-rules/">HTTP DDoS Managed Rules</a>.</p><p>You can read more about our autonomous DDoS protection systems and how they work in our <a href="/deep-dive-cloudflare-autonomous-edge-ddos-protection/">deep-dive technical blog post</a>.</p>
    <div>
      <h2>Helping build a better Internet</h2>
      <a href="#helping-build-a-better-internet">
        
      </a>
    </div>
    <p>At Cloudflare, everything we do is guided by our mission to help build a better Internet. The DDoS team’s vision is derived from this mission: our goal is to make the impact of DDoS attacks a thing of the past. The level of protection that we offer is <a href="/unmetered-mitigation/">unmetered and unlimited</a> — It is not bounded by the size of the attack, the number of the attacks, or the duration of the attacks. This is especially important these days because as we’ve recently seen, attacks are getting <a href="/ddos-attack-trends-for-2022-q1/">larger and more frequent</a>.</p><p>Not using Cloudflare yet? <a href="https://dash.cloudflare.com/sign-up">Start now</a> with our <a href="https://www.cloudflare.com/plans/free/">Free</a> and <a href="https://www.cloudflare.com/plans/pro/">Pro</a> plans to protect your websites, or <a href="https://www.cloudflare.com/magic-transit/">contact us</a> for <a href="https://www.cloudflare.com/ddos/">comprehensive DDoS protection</a> for your entire network using Magic Transit.</p> ]]></content:encoded>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Attacks]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">6AVtIudIv8lRRIKgU50Z8Y</guid>
            <dc:creator>Omer Yoachimik</dc:creator>
            <dc:creator>Julien Desgats</dc:creator>
        </item>
        <item>
            <title><![CDATA[The problem with thread^W event loops]]></title>
            <link>https://blog.cloudflare.com/the-problem-with-event-loops/</link>
            <pubDate>Wed, 18 Mar 2020 12:00:00 GMT</pubDate>
            <description><![CDATA[ Back when Cloudflare was created, the dominant HTTP server used to power websites was Apache httpd. However, we decided to build our infrastructure using the then relatively new NGINX server. ]]></description>
            <content:encoded><![CDATA[ <p>Back when Cloudflare was created, over 10 years ago now, the <a href="https://news.netcraft.com/archives/category/web-server-survey/">dominant HTTP server</a> used to power websites was <a href="https://httpd.apache.org/">Apache httpd</a>. However, we decided to build our infrastructure using the then relatively new NGINX server.</p><p>There are many differences between the two, but crucially for us, the event loop architecture of NGINX was the key differentiator. In a nutshell, event loops work around the need to have one thread or process per connection by coalescing many of them in a single process, this reduces the need for expensive context switching from the operating system and also keeps the memory usage predictable. This is done by processing each connection until it wants to do some I/O, at that point, the said connection is queued until the I/O task is complete. During that time the event loop is available to process other in-flight connections, accept new clients, and the like. The loop uses a multiplexing system call like <a href="http://man7.org/linux/man-pages/man7/epoll.7.html">epoll</a> (or kqueue) to be notified whenever an I/O task is complete among all the running connections.</p><p>In this article we will see that despite its advantages, event loop models also have their limits and falling back to good old threaded architecture is sometimes a good move.</p><p><a href="http://staging.blog.mrk.cfdata.org/content/images/2020/03/3649485942_09f777897d_o.jpg"><img src="http://staging.blog.mrk.cfdata.org/content/images/2020/03/3649485942_09f777897d_o.jpg" /></a>
<br /><a href="https://www.flickr.com/photos/mpd01605/3649485942">Photo</a> credit: Flickr CC-BY-SA</p>
    <div>
      <h3>The key assumption of an event loop architecture</h3>
      <a href="#the-key-assumption-of-an-event-loop-architecture">
        
      </a>
    </div>
    <p>For an event loop to work correctly, there is one key requirement that has to be held: <b>every piece of work has to finish quickly</b>. This is because, as with other collaborative multitasking approaches, once a piece of work is started, there is no preemption mechanism.</p><p>For a proxy service like Cloudflare, this assumption works quite well as we spend most of the time waiting for the client to send data, or the origin to answer. This model is also widely successful for web applications that spend most of their time waiting for the database or other kind of RPC.</p><p>Let's take an example at a situation where two requests hit a Cloudflare server at the same time:</p><a href="http://staging.blog.mrk.cfdata.org/content/images/2020/03/evpool-sequence-diagram@2x-inblog.png"><img src="http://staging.blog.mrk.cfdata.org/content/images/2020/03/evpool-sequence-diagram@2x-inblog.png" /></a><p>In this case, requests don't block each other too much… However, if one of the work units takes an unreasonable amount of time, it will start blocking other requests and the whole model will fall apart very quickly.</p><p>Such long operations might be CPU-intensive tasks, or blocking system calls. A common mistake is to call a library that uses blocking system calls internally: in this case an event-based program will perform a lot worse than a threaded one.</p>
    <div>
      <h3>Issues with the Cloudflare workload</h3>
      <a href="#issues-with-the-cloudflare-workload">
        
      </a>
    </div>
    <p>As I said previously, most of the work done by Cloudflare servers to process an HTTP request is quick and fits the event loop model well. However, a few places might require more CPU. Most notably, our <a href="https://www.cloudflare.com/learning/ddos/glossary/web-application-firewall-waf/">Web Application Firewall (WAF)</a> is in charge of inspecting incoming requests to look for potential threats.</p><p>Although this process only takes a few milliseconds in the large majority of cases, a tiny portion of requests might need more time. It might be tempting to say that it is rare enough to be ignored. However, a typical worker process can have hundreds of requests in flight at the same time. This means that halting the event loop could slow down any of these unrelated requests as well. Keep in mind that the median web page requires around <a href="https://httparchive.org/reports/state-of-the-web#reqTotal">70 requests</a> to fully load, and pages well over 100 assets are common. So looking at the average metrics <a href="https://bravenewgeek.com/everything-you-know-about-latency-is-wrong/">is not very useful</a> in this case, the 99th percentile is more relevant as a regular page load will likely hit that case at some point.</p><p>We want to make the web as fast as possible, even in edge cases. So we started to think about solutions to remove or mitigate that delay. There are quite a few options:</p><ul><li><p>Increase the number of worker processes: this merely mitigates the problem, and creates more pressure on the kernel. Worse, spawning more workers does not cause a linear increase in capacity (even with spare CPU cores) because it also means that critical sections of code will have more lock contention.</p></li><li><p>Create a separate service dedicated to the WAF: this is a more realistic solution, it makes more sense to adopt a thread-based model for CPU-intensive tasks. A separate process allows that. However it would make migration from the existing codebase more difficult, and adding more IPC also has costs: serialization/deserialization, latency, more error cases, etc.</p></li><li><p>Offload CPU-intensive tasks to a thread pool: this is a hybrid approach where we could just hand over the WAF processing to a dedicated thread. There are still some costs of doing that, but overall using a thread pool is a lot faster and simpler than calling an external service. Moreover, we keep roughly the same code, we just call it differently.</p></li></ul><p>All of these solutions are valid and this list is far from exhaustive. As in many situations, there is no one right solution: we have to weigh the different tradeoffs. In this case, given that we already have working code in NGINX, and that the process must be as quick as possible, we chose the thread pool approach.</p>
    <div>
      <h3>NGINX already has thread pools!</h3>
      <a href="#nginx-already-has-thread-pools">
        
      </a>
    </div>
    <p>Okay, I omitted one detail: NGINX already takes this hybrid approach for other reasons. Which made our task easier.</p><p>It is not always easy to avoid blocking system calls. The filesystem operations on Linux are <a href="/io_submit-the-epoll-alternative-youve-never-heard-about/">famously known</a> to be tricky in an asynchronous mode: among other limitations, files have to be read using "direct" mode, bypassing the filesystem cache. Quite annoying for a static file server<a href="#foot-iouring"><sup>[1]</sup></a>.</p><p>This is why <a href="http://nginx.org/en/docs/ngx_core_module.html#thread_pool">thread pools</a> were introduced back in 2015. The idea is quite simple: each worker process will spawn a group of threads that will be dedicated to process these synchronous operations. We use (and improved) that feature ourselves with <a href="/how-we-scaled-nginx-and-saved-the-world-54-years-every-day/">great success</a> for our caching layer.</p><p>Whenever the event loop wants to have such an operation performed, it will push it into a queue that gets processed by the threads, and it will be notified with a result when it is done.</p><p><a href="http://staging.blog.mrk.cfdata.org/content/images/2020/03/imageLikeEmbed--1--3.png"><img src="http://staging.blog.mrk.cfdata.org/content/images/2020/03/imageLikeEmbed--1--3.png" /></a></p><p>This approach is not unique to NGINX: <a href="http://docs.libuv.org/en/v1.x/threadpool.html#threadpool">libuv</a> (that powers node.js) also uses thread pools for filesystem operations.</p><p>Can we repurpose this system to offload our CPU-intensive sections too? It turns out we can. The threading model here is quite simple: nearly nothing is shared between the main loop, only a <code>struct</code> describing the operation to perform is sent and a result is sent back to the event loop<a href="#foot-threadbuf"><sup>[2]</sup></a>. This share-nothing approach also has some drawbacks, notably memory usage: in our case, every thread has its own Lua VM to run the WAF code and its own compiled regular expression cache. Some of it can be improved, but as our code was written assuming there were no data races, changing that assumption would require a significant <a href="https://www.cloudflare.com/learning/cloud/how-to-refactor-applications/">refactoring</a>.</p>
    <div>
      <h3>The best of both worlds? Not quite yet.</h3>
      <a href="#the-best-of-both-worlds-not-quite-yet">
        
      </a>
    </div>
    <p>It's not a secret that epoll is very difficult to use correctly, in the past my colleague Marek <a href="https://idea.popcount.org/2017-02-20-epoll-is-fundamentally-broken-12/">wrote</a> <a href="https://idea.popcount.org/2017-03-20-epoll-is-fundamentally-broken-22/">extensively</a> about its challenges. But more importantly in our case, its <a href="/the-sad-state-of-linux-socket-balancing/">load balancing issues</a> were a real problem.</p><p>In a nutshell, when multiple processes listen on the same socket, some processes will be busier than others. This is due to the fact that whenever the event loop sits idle, it is free to accept new connections.</p><p>Offloading the WAF to a thread pool means freeing up time on the event loop, that can accept even more connections for the unlucky processes, which in turn will need the WAF. In this case, making this change would only make a bad situation worse: the WAF tasks would start piling up in the job queue waiting for the threads to process them.</p><p>Fortunately, this problem is quite well known and even if there is no solution yet in the upstream kernel, people have already <a href="https://lkml.org/lkml/2015/2/17/518">attempted to fix it</a>. We applied that patch, adding the <code>EPOLLROUNDROBIN</code> flag to our kernels quite some time ago and it played a crucial role in this case.</p>
    <div>
      <h3>That's a lot of text… Show us the numbers!</h3>
      <a href="#thats-a-lot-of-text-show-us-the-numbers">
        
      </a>
    </div>
    <p>Alright, that was a lot of talking, let's have a look at actual numbers. I will examine how our servers behaved before (baseline) and after offloading our WAF into thread pools<a href="#foot-metrics"><sup>[3]</sup></a>.</p><p>First let's have a look at the NGINX event loop itself. Did our change really improve the situation?</p><p>We have a metric telling us the maximum amount of time a request blocked the event loop during its processing. Let's look at its 99th percentile:</p><p><a href="http://staging.blog.mrk.cfdata.org/content/images/2020/03/pasted-image-0--1--1.png"><img src="http://staging.blog.mrk.cfdata.org/content/images/2020/03/pasted-image-0--1--1.png" /></a></p><p>Using 100% as our baseline, we can see that this metric is 30% to 40% lower for the requests using the WAF when it is offloaded (yellow line). This is quite expected as we just offloaded a fair chunk of processing to a thread. For other requests (green line), the situation seems a tiny bit worse, but can be explained by the fact that the kernel has more threads to care about now, so the main event loop is more likely to be interrupted by the scheduler while it is running.</p><p>This is encouraging (at least for the requests with WAF enabled), but this doesn't really say what is the value for our customers, so let's look at more concrete metrics. First, the Time To First Byte (TTFB), the following graph only takes cache hits into account to reduce the noise due to other moving parts.</p><p><a href="http://staging.blog.mrk.cfdata.org/content/images/2020/03/pasted-image-0--2--1.png"><img src="http://staging.blog.mrk.cfdata.org/content/images/2020/03/pasted-image-0--2--1.png" /></a></p><p>The 99th percentile is significantly reduced for both WAF and non-WAF requests, overall the gains of freeing up time on the event loop dwarfs the slight penalty we saw on the previous graph.</p><p>Let's finish by looking at another metric: the TTFB here starts only when a request has been accepted by the server. But now we can assume that these requests will be accepted faster as the event loop spends more time idle. Is that the case?</p><p><a href="http://staging.blog.mrk.cfdata.org/content/images/2020/03/pasted-image-0--3--1.png"><img src="http://staging.blog.mrk.cfdata.org/content/images/2020/03/pasted-image-0--3--1.png" /></a></p><p>Success! The accept latency is also a lot lower. Not only the requests are faster, but the server is able to start processing them more quickly as well.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Overall, event-loop-based processing makes a lot of sense in many situations, especially in a microservices world. But this model is particularly vulnerable in cases where a lot of CPU time is necessary. There are a few different ways to mitigate this issue and as is often the case, the right answer is "it depends". In our case the thread pool approach was the best tradeoff: not only did it give visible improvements for our customers but it also allowed us to spread the load across more CPUs so we can more efficiently use our hardware.</p><p>But this tradeoff has many variables. Despite being too long to comfortably run on an event loop, the WAF is still very quick. For more complex tasks, having a separate service is usually a better option in my opinion. And there are a ton of other factors to take into account: security, development and deployment processes, etc.</p><p>We also saw that despite looking slightly worse in some micro metrics, the overall performance improved. This is something we all have to keep in mind when working on complex systems.</p><p>Are you interested in debugging and solving problems involving userspace, kernel and hardware? <a href="https://www.cloudflare.com/careers/">We're hiring</a>!</p><hr /><p><sup>[1]</sup> hopefully this pain will go away over time as we now have <a href="https://lwn.net/Articles/776703">io_uring</a></p><p><sup>[2]</sup> we still have to make sure the buffers are safely shared to avoid use-after-free and other data races, copying is the safest approach, but not necessarily the fastest.</p><p><sup>[3]</sup> in every case, the metrics are taken over a period of 24h, exactly one week apart.</p> ]]></content:encoded>
            <category><![CDATA[NGINX]]></category>
            <category><![CDATA[Linux]]></category>
            <guid isPermaLink="false">13h1dXAZxY1KXW19XxG0BR</guid>
            <dc:creator>Julien Desgats</dc:creator>
        </item>
        <item>
            <title><![CDATA[How we built rate limiting capable of scaling to millions of domains]]></title>
            <link>https://blog.cloudflare.com/counting-things-a-lot-of-different-things/</link>
            <pubDate>Wed, 07 Jun 2017 12:47:51 GMT</pubDate>
            <description><![CDATA[ Back in April we announced Rate Limiting of requests for every Cloudflare customer. Being able to rate limit at the edge of the network has many advantages: it’s easier for customers to set up and operate, their origin servers are not bothered by excessive traffic or layer 7 attacks. ]]></description>
            <content:encoded><![CDATA[ <p>Back in April we announced <a href="/rate-limiting/">Rate Limiting</a> of requests for every Cloudflare customer. Being able to rate limit at the edge of the network has many advantages: it’s easier for customers to set up and operate, their origin servers are not bothered by excessive traffic or layer 7 attacks, the performance and memory cost of rate limiting is offloaded to the edge, and more.</p><p>In a nutshell, <a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/">rate limiting</a> works like this:</p><ul><li><p>Customers can define one or more rate limit rules that match particular HTTP requests (failed login attempts, expensive API calls, etc.)</p></li><li><p>Every request that matches the rule is counted per client IP address</p></li><li><p>Once that counter exceeds a threshold, further requests are not allowed to reach the origin server and an error page is returned to the client instead</p></li></ul><p>This is a simple yet effective protection against brute force attacks on login pages and other sorts of abusive traffic like <a href="https://www.cloudflare.com/learning/ddos/application-layer-ddos-attack/">L7 DoS attacks</a>.</p><p>Doing this with possibly millions of domains and even more millions of rules immediately becomes a bit more complicated. This article is a look at how we implemented a rate limiter able to run quickly and accurately at the edge of the network which is able to cope with the colossal volume of traffic we see at Cloudflare.</p>
    <div>
      <h3>Let’s just do this locally!</h3>
      <a href="#lets-just-do-this-locally">
        
      </a>
    </div>
    <p>As the Cloudflare edge servers are running NGINX, let’s first see how the stock <a href="https://nginx.org/en/docs/http/ngx_http_limit_req_module.html">rate limiting</a> module works:</p>
            <pre><code>http {
    limit_req_zone $binary_remote_addr zone=ratelimitzone:10m rate=15r/m;
    ...
    server {
        ...
        location /api/expensive_endpoint {
            limit_req zone=ratelimitzone;
        }
    }
}</code></pre>
            <p>This module works great: it is reasonably simple to use (but requires a config reload for each change), and very efficient. The only problem is that if the incoming requests are spread across a large number of servers, this doesn’t work any more. The obvious alternative is to use some kind of centralized data store. Thanks to NGINX’s Lua scripting module, that we already use extensively, we could easily implement similar logic using any kind of central data backend.</p><p>But then another problem arises: how to make this fast and efficient?</p>
    <div>
      <h3>All roads lead to Rome? Not with anycast!</h3>
      <a href="#all-roads-lead-to-rome-not-with-anycast">
        
      </a>
    </div>
    <p>Since Cloudflare has a vast and diverse network, reporting all counters to a single central point is not a realistic solution as the latency is far too high and guaranteeing the availability of the central service causes more challenges.</p><p>First let’s take a look at how the traffic is routed in the Cloudflare network. All the traffic going to our edge servers is <a href="https://en.wikipedia.org/wiki/Anycast">anycast</a> traffic. This means that we announce the same IP address for a given web application, site or API worldwide, and traffic will be automatically and consistently routed to the closest live data center.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3MXm1hRKiJKtqZuSG8Ie0j/9b1869712dd6b0538cf7e3615fe4136d/world.png" />
            
            </figure><p>This property is extremely valuable: we are sure that, under normal conditions<a href="#fn1">[1]</a>, the traffic from a single IP address will always reach the same PoP. Unfortunately each new TCP connection might hit a different server inside that PoP. But we can still narrow down our problem: we can actually create an isolated counting system inside each PoP. This mostly solves the latency problem and greatly improves the availability as well.</p>
    <div>
      <h3>Storing counters</h3>
      <a href="#storing-counters">
        
      </a>
    </div>
    <p>At Cloudflare, each server in our edge network is as independent as possible to make their administration simple. Unfortunately for rate limiting, we saw that we do need to share data across many different servers.</p><p>We actually had a similar problem in the past with <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security#Session_IDs">SSL session IDs</a>: each server needed to fetch TLS connection data about past connections. To solve that problem we created a <a href="https://github.com/twitter/twemproxy">Twemproxy</a> cluster inside each of our PoPs: this allows us to split a memcache<a href="#fn2">[2]</a> database across many servers. A <a href="https://en.wikipedia.org/wiki/Consistent_hashing">consistent hashing</a> algorithm ensures that when the cluster is resized, only a few number of keys are hashed differently.</p><p>In our architecture, each server hosts a shard of the database. As we already had experience with this system, we wanted to leverage it for the rate limit as well.</p>
    <div>
      <h3>Algorithms</h3>
      <a href="#algorithms">
        
      </a>
    </div>
    <p>Now let’s take a deeper look at how the different rate limit algorithms work. What we call the <i>sampling period</i> in the next paragraph is the reference unit of time for the counter (1 second for a 10 req/sec rule, 1 minute for a 600 req/min rule, ...).</p><p>The most naive implementation is to simply increment a counter that we reset at the start of each sampling period. This works but is not terribly accurate as the counter will be arbitrarily reset at regular intervals, allowing regular traffic spikes to go through the rate limiter. This can be a problem for resource intensive endpoints.</p><p>Another solution is to store the timestamp of every request and count how many were received during the last sampling period. This is more accurate, but has huge processing and memory requirements as checking the state of the counter require reading and processing a lot of data, especially if you want to rate limit over long period of time (for instance 5,000 req per hour).</p><p>The <a href="https://en.wikipedia.org/wiki/Leaky_bucket">leaky bucket</a> algorithm allows a great level of accuracy while being nicer on resources (this is what the stock NGINX module is using). Conceptually, it works by incrementing a counter when each request comes in. That same counter is also decremented over time based on the allowed rate of requests until it reaches zero. The capacity of the bucket is what you are ready to accept as “burst” traffic (important given that legitimate traffic is not always perfectly regular). If the bucket is full despite its decay, further requests are mitigated.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/nur2wjEAyjE0S6VYblkr3/080ad1cbb09aa1bf6a294ffb4955e9e0/leakybucket.svg.png" />
            
            </figure><p>However, in our case, this approach has two drawbacks:</p><ul><li><p>It has two parameters (average rate and burst) that are not always easy to tune properly</p></li><li><p>We were constrained to use the memcached protocol and this algorithm requires multiple distinct operations that we cannot do atomically<a href="#fn3">[3]</a></p></li></ul><p>So the situation was that the only operations available were <code>GET</code>, <code>SET</code> and <code>INCR</code> (atomic increment).</p>
    <div>
      <h3>Sliding windows to the rescue</h3>
      <a href="#sliding-windows-to-the-rescue">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2gvU2RabELQEWsauNMm8cu/ac296e8bf4e122f18935500ef0bd1c2e/14247536929_1a6315311d_z.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by-sa/2.0/">CC BY-SA 2.0</a> <a href="https://www.flickr.com/photos/halfrain/14247536929/">image</a> by <a href="https://www.flickr.com/photos/halfrain/">halfrain</a></p><p>The naive fixed window algorithm is actually not that bad: we just have to solve the problem of completely resetting the counter for each sampling period. But actually, can’t we just use the information from the previous counter in order to extrapolate an accurate approximation of the request rate?</p><p>Let’s say I set a limit of 50 requests per minute on an API endpoint. The counter can be thought of like this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4SfLGYqNjjjxfJo0C9rU4H/e000ba22680e27910c8963a7111a97c8/sliding.svg.png" />
            
            </figure><p>In this situation, I did 18 requests during the current minute, which started 15 seconds ago, and 42 requests during the entire previous minute. Based on this information, the rate approximation is calculated like this:</p>
            <pre><code>rate = 42 * ((60-15)/60) + 18
     = 42 * 0.75 + 18
     = 49.5 requests</code></pre>
            <p>One more request during the next second and the rate limiter will start being very angry!</p><p>This algorithm assumes a constant rate of requests during the previous sampling period (which can be any time span), this is why the result is only an approximation of the actual rate. This algorithm can be improved, but in practice it proved to be good enough:</p><ul><li><p>It smoothes the traffic spike issue that the fixed window method has</p></li><li><p>It very easy to understand and configure: no average vs. burst traffic, longer sampling periods can be used to achieve the same effect</p></li><li><p>It is still very accurate, as an analysis on 400 million requests from 270,000 distinct sources shown:</p><ul><li><p>0.003% of requests have been wrongly allowed or rate limited</p></li><li><p>An average difference of 6% between real rate and the approximate rate</p></li><li><p>3 sources have been allowed despite generating traffic slightly above the threshold (false negatives), the actual rate was less than 15% above the threshold rate</p></li><li><p>None of the mitigated sources was below the threshold (false positives)</p></li></ul></li></ul><p>Moreover, it offers interesting properties in our case:</p><ul><li><p>Tiny memory usage: only two numbers per counter</p></li><li><p>Incrementing a counter can be done by sending a single <code>INCR</code> command</p></li><li><p>Calculating the rate is reasonably easy: one GET command<a href="#fn4">[4]</a> and some very simple, fast math</p></li></ul><p>So here we are: we can finally implement a good counting system using only a few memcache primitives and without much contention. Still we were not happy with that: it requires a memcached query to get the rate. At Cloudflare we’ve seen a few of the largest L7 attacks ever. We knew that large scale attacks would have crushed the memcached cluster like this. More importantly, such operations would slow down legitimate requests a little, even under normal conditions. This is not acceptable.</p><p>This is why the increment jobs are run asynchronously without slowing down the requests. If the request rate is above the threshold, another piece of data is stored asking all servers in the PoP to start applying the mitigation for that client. Only this bit of information is checked during request processing.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3pCPtEGvM5WPv1vUPzc0MH/ba54eae2f0aeaa2ae960c84bfe32be8c/el-sequence.svg.png" />
            
            </figure><p>Even more interesting: once a mitigation has started, we know exactly when it will end. This means that we can cache that information in the server memory itself. Once a server starts to mitigate a client, it will not even run another query for the subsequent requests it might see from that source!</p><p>This last tweak allowed us to efficiently mitigate large L7 attacks without noticeably penalizing legitimate requests.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Despite being a young product, the rate limiter is already being used by many customers to control the rate of requests that their origin servers receive. The rate limiter already handles several billion requests per day and we recently mitigated attacks with as many as 400,000 requests per second to a single domain without degrading service for legitimate users.</p><p>We just started to explore how we can efficiently protect our customers with this new tool. We are looking into more advanced optimizations and create new features on the top of the existing work.</p><p>Interested in working on high-performance code running on thousands of servers at the edge of the network? Consider <a href="https://www.cloudflare.com/careers/">applying</a> to one of our open positions!</p><hr /><hr /><ol><li><p>The inner workings of anycast route changes are outside of the scope of this article, but we can assume that they are rare enough in this case. <a href="#fnref1">↩︎</a></p></li><li><p>Twemproxy also supports Redis, but our existing infrastructure was backed by <a href="https://github.com/twitter/twemcache">Twemcache</a> (a Memcached fork) <a href="#fnref2">↩︎</a></p></li><li><p>Memcache does support <a href="https://github.com/memcached/memcached/wiki/Commands#cas">CAS</a> (Compare-And-Set) operations and so optimistic transactions are possible, but it is hard to use in our case: during attacks, we will have a lot of requests, creating a lot of contention, in turn resulting in a lot of CAS transactions failing. <a href="#fnref3">↩︎</a></p></li><li><p>The counters for the previous and current minute can be retrieved with a single GET command <a href="#fnref4">↩︎</a></p></li></ol> ]]></content:encoded>
            <category><![CDATA[Attacks]]></category>
            <category><![CDATA[Rate Limiting]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[Network]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Reliability]]></category>
            <guid isPermaLink="false">3zVAZ4bZB7IdYAVjbqN8RP</guid>
            <dc:creator>Julien Desgats</dc:creator>
        </item>
    </channel>
</rss>