<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tifestorm</title>
    <description>The latest articles on DEV Community by Tifestorm (@goodnessoladipo17rgb).</description>
    <link>https://dev.to/goodnessoladipo17rgb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3903727%2Fc18caeff-3e36-4667-8526-3c8daab6ccfd.png</url>
      <title>DEV Community: Tifestorm</title>
      <link>https://dev.to/goodnessoladipo17rgb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/goodnessoladipo17rgb"/>
    <language>en</language>
    <item>
      <title>How I Built a Real-Time DDoS Detection Engine for Nextcloud</title>
      <dc:creator>Tifestorm</dc:creator>
      <pubDate>Wed, 29 Apr 2026 07:13:46 +0000</pubDate>
      <link>https://dev.to/goodnessoladipo17rgb/how-i-built-a-real-time-ddos-detection-engine-for-nextcloud-h71</link>
      <guid>https://dev.to/goodnessoladipo17rgb/how-i-built-a-real-time-ddos-detection-engine-for-nextcloud-h71</guid>
      <description>&lt;h2&gt;
  
  
  ** What This Project Does and Why It Matters**
&lt;/h2&gt;

&lt;p&gt;Imagine you run a cloud storage service used by thousands of people. One day, a hacker starts sending millions of requests per second to your server, crashing it for everyone. This is called a DDoS (Distributed Denial of Service) attack.&lt;/p&gt;

&lt;p&gt;I built an anomaly detection engine that watches all incoming traffic in real time, learns what normal looks like, and automatically blocks attackers before they can cause damage.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How the Sliding Window Works&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A sliding window is like a moving snapshot of recent traffic. I used Python's &lt;code&gt;deque&lt;/code&gt; (double-ended queue) data structure to track request timestamps over the last 60 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;deque&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;ip_window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deque&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ip_window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove timestamps older than 60 seconds
&lt;/span&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;ip_window&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;ip_window&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ip_window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_rate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip_window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;  &lt;span class="c1"&gt;# requests per second
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time a request comes in, we add the timestamp. Old timestamps (older than 60 seconds) are removed from the left. The rate is simply the number of remaining timestamps divided by 60.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How the Baseline Learns from Traffic&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The baseline is how the system learns what "normal" looks like. Every second, we record how many requests came in. Every 60 seconds, we calculate the mean and standard deviation of the last 30 minutes of data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mean&lt;/strong&gt; = average requests per second&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard deviation&lt;/strong&gt; = how much the traffic varies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We store baselines per hour, so the system adapts to morning traffic vs evening traffic automatically.&lt;/p&gt;

&lt;p&gt;Floor values prevent false positives during quiet periods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimum mean: 1.0 req/s&lt;/li&gt;
&lt;li&gt;Minimum stddev: 0.5&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the Detection Logic Makes a Decision
&lt;/h2&gt;

&lt;p&gt;We use a &lt;strong&gt;z-score&lt;/strong&gt; to decide if traffic is anomalous:&lt;/p&gt;

&lt;p&gt;z-score = (current_rate - baseline_mean) / baseline_stddev&lt;/p&gt;

&lt;p&gt;If the z-score exceeds 3.0, it means the current rate is 3 standard deviations above normal — statistically very unlikely to happen by chance.&lt;/p&gt;

&lt;p&gt;We also check if the rate is more than 5x the baseline mean. Whichever threshold fires first triggers a ban.&lt;/p&gt;

&lt;p&gt;If an IP has a high error rate (4xx/5xx responses), we tighten the threshold to 2.1 to catch more subtle attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How iptables Blocks an IP
&lt;/h2&gt;

&lt;p&gt;When an attack is detected, we use &lt;code&gt;iptables&lt;/code&gt; — Linux's built-in firewall — to drop all packets from the attacking IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ban&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;iptables&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-I&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;INPUT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-s&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-j&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DROP&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-I INPUT&lt;/code&gt; inserts a rule at the top of the INPUT chain. &lt;code&gt;-s&lt;/code&gt; specifies the source IP. &lt;code&gt;-j DROP&lt;/code&gt; silently drops all packets from that IP.&lt;/p&gt;

&lt;p&gt;Bans are temporary with a backoff schedule: 10 minutes → 30 minutes → 2 hours → permanent. This gives legitimate users a chance to get unbanned if they were falsely flagged.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Building this tool taught me how real security systems work — not just blocking known bad actors, but learning from traffic patterns and adapting automatically. The combination of sliding windows, statistical baselines, and iptables makes a powerful and lightweight DDoS protection system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/goodnessoladipo17-rgb/hng-stage3" rel="noopener noreferrer"&gt;https://github.com/goodnessoladipo17-rgb/hng-stage3&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Live Dashboard:&lt;/strong&gt; &lt;a href="http://13.53.37.195:8080" rel="noopener noreferrer"&gt;http://13.53.37.195:8080&lt;/a&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>python</category>
      <category>security</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
