<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://organicdarius.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://organicdarius.com/" rel="alternate" type="text/html" /><updated>2026-04-23T16:11:17+00:00</updated><id>https://organicdarius.com/feed.xml</id><title type="html">Security and coding tutorials by Darius Pirvulescu</title><subtitle>Practical write-ups, tutorials and other musings on software engineering, web security, Ruby and Rails, C#, Linux, and Docker - by Darius Pirvulescu.</subtitle><author><name>Darius Pirvulescu</name></author><entry><title type="html">SSE, Clerk, and rotating tokens: A debugging story</title><link href="https://organicdarius.com/blog/sse-clerk-and-rotating-tokens-a-debugging-story/" rel="alternate" type="text/html" title="SSE, Clerk, and rotating tokens: A debugging story" /><published>2026-02-17T00:00:00+00:00</published><updated>2026-02-17T00:00:00+00:00</updated><id>https://organicdarius.com/blog/sse-clerk-and-rotating-tokens-a-debugging-story</id><content type="html" xml:base="https://organicdarius.com/blog/sse-clerk-and-rotating-tokens-a-debugging-story/"><![CDATA[<p>This is the story of a subtle workflow error that polluted our logs and kept triggering pointless alerts. It lingered in the background long enough. I decided to track it down and fix it for good. Enough is enough.</p>

<p>In my client’s system, we rely on <a href="https://en.wikipedia.org/wiki/Server-sent_events">Server-Sent Events</a> (SSE) to stream web notifications to active users on our client apps. Something along this flow triggered 401 Unauthorized errors constantly.</p>

<h2 id="the-sse-workflow">The SSE workflow</h2>
<ol>
  <li>Client app - opens a connection (type <code class="language-plaintext highlighter-rouge">text/event-stream</code>) to receive these notifications</li>
  <li>Server - authenticates the user</li>
  <li>subscribes it to the messages stream</li>
  <li>sends events (messages and/or data) to the client</li>
  <li>Connection’s lifetime ends when either the client disconnects or the server cancels the connection</li>
</ol>

<p>As our auth provider, we use Clerk. Our frontend apps, built with Vite, are implementing the Clerk SDK (<a href="https://www.npmjs.com/package/@clerk/clerk-react">@clerk/clerk-react</a>).</p>

<h2 id="the-problem">The problem</h2>
<p>At certain intervals during this process, the SSE requests failed with 401 Unauthorized. When users were active on the app, the number of failures compounded. This triggered the alerts we configured in the Azure Monitoring portal, and those alerts were sent to our Slack. It became annoying as they clouded other real alerts from our system.</p>

<p><img src="/assets/images/posts/sse-dev-tools.png" alt="The error on the dev tool" /></p>

<p>And the Azure logs:
<img src="/assets/images/posts/sse-azure-alerts.png" alt="SSE Azure Alerts" /></p>

<h2 id="the-symptoms">The symptoms</h2>
<p>I noticed the first request failed, followed by successful requests. What made that first request fail? I looked at the difference between them.  <br />
A few symptoms started to stand out in those failed requests:</p>
<ul>
  <li>The request type was <code class="language-plaintext highlighter-rouge">plain</code> instead of <code class="language-plaintext highlighter-rouge">eventsource</code></li>
  <li>The bearer token was different. This was a tell-tale sign, as the new token didn’t change in the successful requests.</li>
  <li>The failed one seemed like it got triggered once the app was accessed (switched tab to it)</li>
</ul>

<p>This last symptom had me thinking, it probably is related to an event firing. While digging around DevTools for clues, I checked at the stack trace. Comparing the stack traces from the two requests offered plenty of clues on what went wrong.</p>

<p>The stack trace from the 401 requests:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>create
fetch.ts:105:40
onVisibilityChange
fetch.ts:145:9
(Async: EventListener.handleEvent) fetchEventSource/&lt;
fetch.ts:67:12
fetchEventSource
sse.ts:64:13
connect
RealtimeNotificationsProvider.tsx:122:22
initializeSSE
</code></pre></div></div>

<p>And the one resulting in 200:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>create
fetch.ts:105:40
fetchEventSource/&lt;
fetch.ts:145:9
fetchEventSource
fetch.ts:67:12
connect
sse.ts:64:13
scheduleReconnect/this.reconnectTimeout&lt;
RealtimeNotificationsProvider.tsx:122:22
(Async: setTimeout handler) scheduleReconnect
RealtimeNotificationsProvider.tsx:125:10
onerror
react-dom-client.development.js:25989:20
create
</code></pre></div></div>

<p>I noticed the <code class="language-plaintext highlighter-rouge">onVisibilityChange</code> was right before the 401 error. This shines some light as it might be related to the browser visibility event.</p>

<p>After googling for <em>“onVisibilityChange fetch”</em>, among the first results, I found <a href="https://github.com/Azure/fetch-event-source#readme">https://github.com/Azure/fetch-event-source#readme</a>, the same library we’re using handling SSE.  <br />
In their README:</p>

<blockquote>
  <p>In addition, this library also plugs into the browser’s <a href="https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API">Page Visibility API</a> so the connection closes if the document is hidden (e.g., the user minimizes the window), and automatically retries with the last event ID when it becomes visible again</p>
</blockquote>

<p>Aha! Just like in the symptom I observed, it connects when the app comes into focus. So while the user is not looking at the app (so to speak) the connection is closed. As soon as the app is visible again, it retries to connect.</p>

<h3 id="the-code">The code</h3>
<p>It was still not very clear why the authorization failed. Time to paste the code here. Well, parts of the code:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class=""><code> <span class="k">async</span> <span class="nx">connect</span><span class="p">():</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="k">void</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">isConnecting</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">this</span><span class="p">.</span><span class="nx">isConnecting</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

    <span class="k">try</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">getToken</span><span class="p">();</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">isConnecting</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
        <span class="k">return</span><span class="p">;</span>
      <span class="p">}</span>

      <span class="p">[...]</span>

      <span class="k">await</span> <span class="nx">fetchEventSource</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">GET</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
          <span class="na">Authorization</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">token</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span>
          <span class="na">Accept</span><span class="p">:</span> <span class="dl">'</span><span class="s1">text/event-stream</span><span class="dl">'</span><span class="p">,</span>
        <span class="p">},</span>
        <span class="na">signal</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">abortController</span><span class="p">.</span><span class="nx">signal</span><span class="p">,</span>
        <span class="na">onmessage</span><span class="p">:</span> <span class="nx">event</span> <span class="o">=&gt;</span> <span class="p">{</span>
          <span class="p">[...]</span>
        <span class="p">},</span>
        <span class="na">onerror</span><span class="p">:</span> <span class="nx">error</span> <span class="o">=&gt;</span> <span class="p">{</span>
          <span class="c1">// Only reconnect if not aborted (user-initiated disconnect)</span>
          <span class="p">[...]</span>
        <span class="p">},</span>
      <span class="p">});</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// Only reconnect if not aborted (user-initiated disconnect)</span>
      <span class="p">[...]</span>
    <span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">isConnecting</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
</code></pre></div></div>
<p>I kept experimenting until I noticed the token gets cached. We pass the <em>headers</em> to <code class="language-plaintext highlighter-rouge">fetchEventSource</code>, and those same headers are reused when the <a href="https://github.com/Azure/fetch-event-source/blob/a0529492576e094374602f24d5e64b3a271b4576/src/fetch.ts#L75C9-L80C10">visibility change</a>, they’re passed down to the library’s internal <a href="https://github.com/Azure/fetch-event-source/blob/a0529492576e094374602f24d5e64b3a271b4576/src/fetch.ts#L107">create()</a> call.</p>

<p>So we pass the valid token for SSE and it works well. But it eventually fails, and the token gets changed by then.  <br />
Can the token be rotated while we “look away” (the app in the background)? I went through the Clerk docs and <strong>finally I found the root cause</strong>.</p>

<h2 id="the-bug">The bug</h2>
<p>Clerk issues tokens with very short lifespan (that’s 60 seconds short). And uses a <a href="https://clerk.com/docs/guides/how-clerk-works/overview#token-refresh-mechanism">token refresh mechanism</a> that is triggered automatically by the frontend SDKs.</p>

<p>This and the token being cached was the cause of us getting all those 401 Unauthorized errors.</p>

<ol>
  <li>SSE connects with token A (valid) - the token is passed to <code class="language-plaintext highlighter-rouge">fetchEventSource</code></li>
  <li>User switches tabs - the library closes the connection</li>
  <li>Clerk rotates the token - token A expires, replaced by token B</li>
  <li>User returns to the tab - the internal <code class="language-plaintext highlighter-rouge">onVisibilityChange</code> handler fires and tries to reconnect while re-using the same static headers object from step <strong>1</strong>. It sends the expired token A</li>
  <li>Our server returns 401 (token A is expired)</li>
  <li>The 401 triggers our <code class="language-plaintext highlighter-rouge">onerror</code> which eventually calls <code class="language-plaintext highlighter-rouge">getToken()</code>. This refreshes the token B</li>
</ol>

<h2 id="the-fix">The fix</h2>
<p>I needed to make sure <code class="language-plaintext highlighter-rouge">fetchEventSource</code> has always the latest token. So a refresh mechanism was required. <br />
As a helpful aid, the fetch library allows passing a <a href="https://github.com/Azure/fetch-event-source/blob/a0529492576e094374602f24d5e64b3a271b4576/src/fetch.ts#L64">custom fetch</a>. <br />
So instead of static headers, I could pass my custom fetch method. This way I can construct the headers dynamically. And ensure the connection always uses the latest credentials.</p>

<p>My new code looks like this:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class=""><code><span class="p">[...]</span>
      <span class="k">await</span> <span class="nx">fetchEventSource</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">GET</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">fetch</span><span class="p">:</span> <span class="k">async</span> <span class="p">(</span><span class="nx">input</span><span class="p">,</span> <span class="nx">init</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
          <span class="kd">const</span> <span class="nx">freshToken</span> <span class="o">=</span> <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">getToken</span><span class="p">();</span>
          <span class="kd">const</span> <span class="nx">headers</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Headers</span><span class="p">(</span><span class="nx">init</span><span class="p">?.</span><span class="nx">headers</span><span class="p">);</span>
          <span class="nx">headers</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">,</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">freshToken</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
          <span class="nx">headers</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="dl">'</span><span class="s1">Accept</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">text/event-stream</span><span class="dl">'</span><span class="p">);</span>
          <span class="k">return</span> <span class="nx">globalThis</span><span class="p">.</span><span class="nx">fetch</span><span class="p">(</span><span class="nx">input</span><span class="p">,</span> <span class="p">{</span> <span class="p">...</span><span class="nx">init</span><span class="p">,</span> <span class="nx">headers</span> <span class="p">});</span>
        <span class="p">},</span>
        <span class="na">signal</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">abortController</span><span class="p">.</span><span class="nx">signal</span><span class="p">,</span>
        <span class="na">onmessage</span><span class="p">:</span> <span class="nx">event</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="p">[...]</span> <span class="p">},</span>
        <span class="na">onerror</span><span class="p">:</span> <span class="nx">error</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="p">[...]</span> <span class="p">},</span>
      <span class="p">});</span>
<span class="p">[...]</span>
</code></pre></div></div>

<h2 id="the-wins">The wins</h2>
<p>Investigating this was a pleasant and rewarding journey, one that got me more used to dive into the source code.  <br />
The change itself is small, but one that compounds: every user, every tab switch, every day.</p>

<p>Right now, there is less noise in our server logs, and less “fake” alerts getting triggered. So we have a better visibility, the real issues stand out instead of getting buried in the noise. We can focus on what matters. <br />
Most importantly, this improved the user experience. When users switch back to the app, notifications arrive instantly. No more waiting 5-seconds <code class="language-plaintext highlighter-rouge">reconnectDelay</code> to kick in.</p>]]></content><author><name>Darius Pirvulescu</name></author><category term="typescript" /><summary type="html"><![CDATA[This is the story of a subtle workflow error that polluted our logs and kept triggering pointless alerts. It lingered in the background long enough. I decided to track it down and fix it for good. Enough is enough.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Loading related DB entities in EF Core</title><link href="https://organicdarius.com/blog/loading-related-db-entities-in-ef-core/" rel="alternate" type="text/html" title="Loading related DB entities in EF Core" /><published>2026-01-30T00:00:00+00:00</published><updated>2026-01-30T00:00:00+00:00</updated><id>https://organicdarius.com/blog/loading-related-db-entities-in-ef-core</id><content type="html" xml:base="https://organicdarius.com/blog/loading-related-db-entities-in-ef-core/"><![CDATA[<p>For my latest client, I’ve been working with C# and ASP.NET Core, using Entity Framework (EF) Core as the ORM. This gave me the chance to explore how relationships between entities are modeled and and how EF Core loads related data.</p>

<p>Choosing the right data loading strategy directly impacts the database queries, resource consumption, response times, and even code clarity.  <br />
These are the main strategies:</p>

<ul>
  <li>Eager - related entities are loaded together with the parent ones</li>
  <li>Explicit - related entities are loaded when you decide to load them</li>
  <li>Lazy - related entities are loaded when you try and access them</li>
</ul>

<p>I’ll illustrate the difference with practical code examples.</p>

<h2 id="the-project">The project</h2>
<p>I setup a Warehouse API with these entities:</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">namespace</span> <span class="nn">Warehouse.Api.Entities</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">Customer</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Email</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">private</span> <span class="n">ICollection</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;?</span> <span class="n">_orders</span><span class="p">;</span>
    <span class="k">public</span> <span class="k">virtual</span> <span class="n">ICollection</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;</span> <span class="n">Orders</span> <span class="p">=&gt;</span> <span class="n">_orders</span> <span class="p">??=</span> <span class="p">[];</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">Order</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">CustomerId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="k">virtual</span> <span class="n">Customer</span> <span class="n">Customer</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">null</span><span class="p">!;</span>    
    <span class="k">public</span> <span class="k">virtual</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Item</span><span class="p">&gt;</span> <span class="n">Items</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="n">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="p">[];</span>
    <span class="k">public</span> <span class="k">virtual</span> <span class="n">ICollection</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;</span> <span class="n">OrderItems</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="n">init</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="p">[];</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">OrderItem</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="k">virtual</span> <span class="n">Order</span> <span class="n">Order</span><span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">null</span><span class="p">!;</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">ItemId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="k">virtual</span> <span class="n">Item</span> <span class="n">Item</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">null</span><span class="p">!;</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">Item</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">required</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">required</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This API has a <code class="language-plaintext highlighter-rouge">CustomerService</code> that handles fetching the <code class="language-plaintext highlighter-rouge">Customer</code> with their data from the DB. It has a method for each loading type, implementing the interface:</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">namespace</span> <span class="nn">Warehouse.Api.Services</span><span class="p">;</span>

<span class="k">using</span> <span class="nn">Entities</span><span class="p">;</span>

<span class="k">public</span> <span class="k">interface</span> <span class="nc">ICustomerService</span>
<span class="p">{</span>
    <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Customer</span><span class="p">&gt;&gt;</span> <span class="nf">GetCustomersEagerAsync</span><span class="p">();</span>
    <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Customer</span><span class="p">&gt;&gt;</span> <span class="nf">GetCustomersExplicitAsync</span><span class="p">();</span>
    <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Customer</span><span class="p">&gt;&gt;</span> <span class="nf">GetCustomersLazyAsync</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I also added some extension methods to (pretty) print the records:</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Print</span><span class="p">(</span><span class="k">this</span> <span class="n">Order</span> <span class="n">order</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"    Order #</span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="the-strategies">The strategies</h2>
<h3 id="eager-loading">Eager Loading</h3>
<p>This approach loads all related data in a single database query. Eager loading prevents additional round-trips to the database.  <br />
In EF Core, this is achieved with the <code class="language-plaintext highlighter-rouge">Include()</code> method to load the child entity, followed by <code class="language-plaintext highlighter-rouge">ThenInclude()</code> if loading any nested entities. Under the hood, EF Core translates this into SQL <code class="language-plaintext highlighter-rouge">JOIN </code> operations.</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Customer</span><span class="p">&gt;&gt;</span> <span class="nf">GetCustomersEagerAsync</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"~~~ EAGER LOADING START ~~~"</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">customers</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_dbContext</span><span class="p">.</span><span class="n">Customers</span>
        <span class="p">.</span><span class="nf">Include</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="n">Orders</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">ThenInclude</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">OrderItems</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ThenInclude</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Item</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">AsNoTracking</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">AsSplitQuery</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

    <span class="c1">// All the data already loaded</span>

    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"~~~ EAGER LOADING END ~~~"</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">customers</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>To optimize performance, I added the <code class="language-plaintext highlighter-rouge">AsNoTracking()</code> method. This pulls the data in a “read-only” mode and speeds up queries. It skips setting up the <a href="https://learn.microsoft.com/en-us/ef/core/querying/tracking#no-tracking-queries">change tracker</a>.</p>

<p>Note for further reference, when adding more same-level JOINs (multiple <code class="language-plaintext highlighter-rouge">Include()</code>), it is recommended to use the <code class="language-plaintext highlighter-rouge">AsSplitQuery</code> to avoid <a href="https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#cartesian-explosion">cartesian explosion</a>.</p>

<h3 id="explicit-loading">Explicit Loading</h3>
<p>This allows developers to decide exactly when to load data. Calling the <code class="language-plaintext highlighter-rouge">Load</code> method triggers the ORM to query for the navigational property. Explicit loading offers fine-grained control of when to load data based on runtime conditions.</p>

<p>The helper methods used for different scenarios:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Reference()</code> - single navigation property</li>
  <li><code class="language-plaintext highlighter-rouge">Collection()</code> - collection</li>
</ul>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Customer</span><span class="p">&gt;&gt;</span> <span class="nf">GetCustomersExplicitAsync</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"~~~ EXPLICIT LOADING START ~~~"</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">customers</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_dbContext</span><span class="p">.</span><span class="n">Customers</span><span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">customer</span> <span class="k">in</span> <span class="n">customers</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">customer</span><span class="p">.</span><span class="n">Id</span> <span class="p">&lt;=</span> <span class="m">3</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Explicitly load the orders</span>
            <span class="k">await</span> <span class="n">_dbContext</span><span class="p">.</span><span class="nf">Entry</span><span class="p">(</span><span class="n">customer</span><span class="p">).</span><span class="nf">Collection</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Orders</span><span class="p">).</span><span class="nf">LoadAsync</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="n">customer</span><span class="p">.</span><span class="nf">Print</span><span class="p">();</span>

        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">order</span> <span class="k">in</span> <span class="n">customer</span><span class="p">.</span><span class="n">Orders</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">order</span><span class="p">.</span><span class="nf">Print</span><span class="p">();</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span> <span class="p">&lt;=</span> <span class="m">4</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="c1">// Also explicitly load the orderItems</span>
                <span class="k">await</span> <span class="n">_dbContext</span><span class="p">.</span><span class="nf">Entry</span><span class="p">(</span><span class="n">order</span><span class="p">).</span><span class="nf">Collection</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">OrderItems</span><span class="p">).</span><span class="nf">LoadAsync</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">orderItem</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">OrderItems</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="c1">// Explicitly load the single navigation property - item</span>
                <span class="k">await</span> <span class="n">_dbContext</span><span class="p">.</span><span class="nf">Entry</span><span class="p">(</span><span class="n">orderItem</span><span class="p">).</span><span class="nf">Reference</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Item</span><span class="p">).</span><span class="nf">LoadAsync</span><span class="p">();</span>
                <span class="n">orderItem</span><span class="p">.</span><span class="nf">Print</span><span class="p">();</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"~~~ EXPLICIT LOADING END ~~~"</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">customers</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="lazy-loading">Lazy Loading</h3>
<p>The main entity is loaded first, and the related ones are only loaded when the navigation property is accessed.</p>

<p>The ORM makes use of proxies (dynamic classes) to trigger queries as they intercept any access to the navigation property.
The proxies are not enabled by default, so for this, I installed the <code class="language-plaintext highlighter-rouge">Microsoft.EntityFrameworkCore.Proxies</code> NuGet package. Then, I included the option to enable lazy loading:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>builder.Services.AddDbContext&lt;WarehouseDbContext&gt;(options =&gt;
{
    options.UseLazyLoadingProxies()
        .UseSqlite(connString);
});
</code></pre></div></div>
<p>I also had to change the entities from <code class="language-plaintext highlighter-rouge">sealed record</code> to <code class="language-plaintext highlighter-rouge">class</code>, and change some navigational properties by adding the <code class="language-plaintext highlighter-rouge">virtual</code> keyword.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Customer</span><span class="p">&gt;&gt;</span> <span class="nf">GetCustomersLazyAsync</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"~~~ LAZY LOADING START ~~~"</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">customers</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_dbContext</span><span class="p">.</span><span class="n">Customers</span>
        <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">customer</span> <span class="k">in</span> <span class="n">customers</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">customer</span><span class="p">.</span><span class="nf">Print</span><span class="p">();</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">order</span> <span class="k">in</span> <span class="n">customer</span><span class="p">.</span><span class="n">Orders</span><span class="p">)</span> <span class="c1">// Orders accessed, loading Orders</span>
        <span class="p">{</span>
            <span class="n">order</span><span class="p">.</span><span class="nf">Print</span><span class="p">();</span>
            <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">items</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">Items</span><span class="p">)</span> <span class="c1">// Now loading OrderItems</span>
            <span class="p">{</span>
                <span class="n">items</span><span class="p">.</span><span class="nf">Print</span><span class="p">();</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"~~~ LAZY LOADING END ~~~"</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">customers</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This might be problematic, triggering the infamous N+1 problem. Here, we execute repeated database queries each time we access the navigation property, this can easily grow to a high number of queries to the database, increasing execution time and workload. Eventually, this might bring the database to a halt. <br />
Let’s say we have this number of records together with the amount of queries we produce:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>10 customers - 1 Customer query
10 orders    - 10 Order queries
5 items      - 100 Items queries

Total of 111 queries
</code></pre></div></div>
<p>So we will execute 111 database queries to fetch the ten customers with their orders and items.</p>

<h2 id="which-one-to-use">Which one to use?</h2>
<p>The decision to pick one over the other depends on various factors like the database used, the data access patterns, performance requirements, how much data to load upfront, etc.</p>

<p><strong>Eager loading</strong> is great when you know upfront what related data you need. Or for when the related data is (almost) always accessed. It minimizes the queries, which makes the access more predictable. But it may fetch data that’s not being used, and it has a larger initial query.</p>

<p><strong>Explicit loading</strong> is useful for conditional or data retrieval based on user actions (ex: pressing a “Show Details” button). It offers the most control over data loading. <br />
But all that manual loading requires more complex/maybe ugly code. It is easier to get null references when you forget to load some data. And it is also susceptible to the N+1 issue.</p>

<p>Usually, you want to avoid <strong>Lazy Loading</strong> in favor of eager loading, especially if you always access the nested data. Lazy loading creates unnecessary data round-trips and makes performance issues harder to debug. It doesn’t work with <code class="language-plaintext highlighter-rouge">AsNoTracking()</code> and requires changing the entities (<code class="language-plaintext highlighter-rouge">virtual</code>) and proxy support. As the codebase grows and more data relations are added, it requires monitoring for N+1 issues. <br />
That said, lazy loading has some strengths. It provides a faster initial load (main entity), keeps the code simpler, and it’s favored when related data is rarely accessed. It is also convenient for rapid development. <br />
For embedded databases like SQLite, the cost of individual queries is much lower compared to client-server databases. Since SQLite doesn’t have the network overhead, the performance impact of lazy loading is <a href="https://sqlite.org/np1queryprob.html">reduced</a>.</p>

<p>In large applications, the usual choice is a combination of these strategies. Each scenario has different access patterns and constraints. So picking the strategy case by case is better than relying on one strategy alone.</p>]]></content><author><name>Darius Pirvulescu</name></author><category term="csharp" /><summary type="html"><![CDATA[For my latest client, I’ve been working with C# and ASP.NET Core, using Entity Framework (EF) Core as the ORM. This gave me the chance to explore how relationships between entities are modeled and and how EF Core loads related data.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Exploring the ANSI escape injection in Active Record logging [CVE-2025-55193]</title><link href="https://organicdarius.com/blog/exploring-the-ansi-escape-injection-in-active-record-logging-cve-2025-55193/" rel="alternate" type="text/html" title="Exploring the ANSI escape injection in Active Record logging [CVE-2025-55193]" /><published>2025-08-18T00:00:00+00:00</published><updated>2025-08-18T00:00:00+00:00</updated><id>https://organicdarius.com/blog/exploring-the-ansi-escape-injection-in-active-record-logging-cve-2025-55193</id><content type="html" xml:base="https://organicdarius.com/blog/exploring-the-ansi-escape-injection-in-active-record-logging-cve-2025-55193/"><![CDATA[<p>Last week, two security patches were added to Rails. One of them was meant to guard against the ANSI escape injection [CVE-2025-55193], a vulnerability affecting Active Record logging. I was curious what an attacker could achieve by exploiting this vulnerability. 
Here, I logged my findings and created a simple PoC.</p>

<p>The sink is <a href="https://github.com/rails/rails/blob/34d52e30e3ba231660a271245e24e2d8b2ccef15/activerecord/lib/active_record/core.rb#L253">here</a>. This line prints to the console, and the id is a user-controlled parameter, so it should not be trusted.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>                                                                         ⌄
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
&gt;&gt;&gt;                                                                      ^bad boiii
</code></pre></div></div>

<p>It is not exploitable under most circumstances, and the impact is reduced on most terminals. However, it may still increase the attack surface, particularly if there are misconfigurations.</p>

<p>The affected versions:</p>
<ul>
  <li>activerecord &gt;= 8.0, &lt; 8.0.2.1 (patched in 8.0.2.1)</li>
  <li>activerecord &gt;= 7.2, &lt; 7.2.2.2 (patched in 7.2.2.2)</li>
  <li>activerecord &gt;= 0, &lt; 7.1.5.2 (patched in 7.1.5.2)</li>
</ul>

<blockquote>
  <p>Please upgrade to one of the latest Rails Versions 7.1.5.2, 7.2.2.2, or 8.0.2.1.</p>
</blockquote>

<p>I wanted to see how it can be triggered, so for this, I set up a basic Rails app at the vulnerable version <code class="language-plaintext highlighter-rouge">7.1.0</code>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nb">mkdir </span>ansi-vulnerable
<span class="nb">cd </span>ansi-vulnerable
<span class="nb">echo</span> <span class="s2">"source 'https://rubygems.org'"</span> <span class="o">&gt;</span> Gemfile
<span class="nb">echo</span> <span class="s2">"gem 'rails', '7.1.0'"</span> <span class="o">&gt;&gt;</span> Gemfile
bundle <span class="nb">install</span>

<span class="c"># Check the rails version</span>
bin/rails <span class="nt">-v</span>
</code></pre></div></div>
<p>Then I created the Rails app, the DB, and a placeholder scaffold.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>bin/rails new <span class="nb">.</span> <span class="nt">--force</span> <span class="nt">--skip-bundle</span>
bin/rails g scaffold book title:string
bin/rails db:create db:migrate

bin/rails s <span class="c"># localhost:3000</span>
</code></pre></div></div>

<p>Locally, I am using the <code class="language-plaintext highlighter-rouge">xterm-256color</code> term. So the payload for this might differ based on your terminal.</p>

<h2 id="the-escape-sequences">The escape sequences</h2>
<p>I’ll not get into too many details on these. <br />
In 1967, the “C0” control character set was first defined (ISO 646). <br />
But soon after, in the 70s, video terminals were the new cool thing. They could display colors/styles/formats, move the cursor around, modify previously written text, etc.  <br />
There was a need for standardization of the code performing these “magic” features. This was achieved by ECMA-48 (1976), ANSI X3.64 (1979), and ISO 6429 (1983). <br />
The terms “<em>ANSI escape sequences</em>” and “<em>ANSI control sequences</em>” are often used interchangeably, but the control ones are actually a subset of the escape sequences.</p>

<p>Back to the Control Characters (Cc), there are two types:</p>
<ul>
  <li>C0 - the first 32 non-printable characters of the ASCII table (defined initially in ISO 646).</li>
  <li>C1 - an additional 32 Ccs, both in 7-bit and 8-bit encodings. The 8-bit set is more straightforward and encodes each Cc in a single byte; it spans from 128 to 159 (decimal). The 7-bit systems cannot encode values over 128 in a single unit, so to represent them, it was decided to combine the ESC character with one character between decimal 64 and 95.</li>
</ul>

<p>The format of a Control Sequence:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>CSI Pn In F
</code></pre></div></div>
<ul>
  <li>CSI - Control Sequence Introducer (<code class="language-plaintext highlighter-rouge">\x1b</code> (ESC)/<code class="language-plaintext highlighter-rouge">\x9b</code>/<code class="language-plaintext highlighter-rouge">\x5b</code>)</li>
  <li>Pn - Parameter bytes (optional, code points <code class="language-plaintext highlighter-rouge">\x30</code> &lt;&gt; <code class="language-plaintext highlighter-rouge">\x3f</code>, of <code class="language-plaintext highlighter-rouge">n</code> length, separated by “;”)</li>
  <li>In - Intermediate bytes (optional, code points <code class="language-plaintext highlighter-rouge">\x20</code> &lt;&gt; <code class="language-plaintext highlighter-rouge">\x2f</code>, of <code class="language-plaintext highlighter-rouge">n</code> length)</li>
  <li>F - Final bytes (a bit combination from <code class="language-plaintext highlighter-rouge">\x40</code> &lt;&gt; <code class="language-plaintext highlighter-rouge">\x7e</code>)</li>
</ul>

<h3 id="alternative-notation">Alternative notation</h3>
<p>You might see the string <code class="language-plaintext highlighter-rouge">\e[32m</code> represented as:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>printf '\x1b\x5b\x33\x32\x6d' - Hex
printf '\033\133\063\062\155' - Octal
printf '\u001b\u005b\u0033\u0032\u006d' - Unicode
printf '\27\91\51\50\109' - Decimal
printf '\e[32m' - ASCII
</code></pre></div></div>
<p><strong>Note</strong>: the true Decimal notation would be plain numbers without the <code class="language-plaintext highlighter-rouge">\</code> character.</p>

<h2 id="the-payload">The payload</h2>

<p>I tested with this payload:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>\x1b\x5b3;32;44m hello \x1b\x5b0m
</code></pre></div></div>
<p>Here:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">\x1b</code> and <code class="language-plaintext highlighter-rouge">\x5b</code> - CSI</li>
  <li><code class="language-plaintext highlighter-rouge">3;32;44</code> - Pn</li>
  <li>
    <ul>
      <li>3 - italics</li>
    </ul>
  </li>
  <li>
    <ul>
      <li>32 - color green</li>
    </ul>
  </li>
  <li>
    <ul>
      <li>44 - background color blue</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">0</code> - resets the style</li>
  <li><code class="language-plaintext highlighter-rouge">m</code> - calling the function</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nv">PAYLOAD</span><span class="o">=</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b\x5b3;32;44m hello \x1b\x5b0m'</span><span class="si">)</span>

wget http://localhost:3000/books/<span class="nv">$PAYLOAD</span>
</code></pre></div></div>

<p>This triggers the <code class="language-plaintext highlighter-rouge">RecordNotFound</code> error of ActiveRecord which prints the requested ID to the console. Being vulnerable to ANSI escape injection, it prints the styling as well.</p>

<p><img src="/assets/images/posts/ansi_escape_injection.png" alt="ANSI-escape-injection-in-terminal" /></p>

<p>This here suffices in demonstrating the vulnerability.</p>

<p>I researched what other things an attacker might be able to do. These depend on the terminal:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">\x1b\x5b20F hello</code> - move the cursor to previous 20 lines</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b\x5b10M hello</code> - delete 10 lines</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b]8;;http://example.com\e\\This is a link\e]8;;\e\\\n</code> - print links in the victim’s terminal</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b]52;c;c2xlZXAgMQplY2hvICQod2hvYW1pKQ==</code> - clipboard injection (injecting <code class="language-plaintext highlighter-rouge">echo $(whoami)</code>)</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b[?1001h\x1b[?1002h\x1b[?1003h\x1b[?1004h\x1b[?1005h\x1b[?1006h\x1b[?1007h\x1b[?1015h\x1b[?10016h\</code> - print mouse tracking values in the terminal</li>
</ul>

<p>In some rare cases, it might even open up the possibility for <a href="https://github.com/dgl/houdini-kubectl-poc?tab=readme-ov-file#cves">remote command execution</a>.</p>

<h2 id="the-patch">The patch</h2>
<p>To fix this, the Rails team added a call <code class="language-plaintext highlighter-rouge">.inspect</code> on the id before printing it to the console (commit <a href="https://github.com/rails/rails/commit/3beef20013736fd52c5dcfdf061f7999ba318290">3beef20</a>).</p>

<h2 id="resources">Resources</h2>
<ul>
  <li>https://nicholas-morris.com/articles/ansi-codes - Great read</li>
  <li>https://invisible-island.net/xterm/ctlseqs/ctlseqs.html - Documentation to all the sequences xterm supports</li>
  <li>https://www.youtube.com/watch?v=opW_Q7jvSbc - Weaponizing Plain Text: ANSI Escape Sequences as a Forensic Nightmare</li>
</ul>]]></content><author><name>Darius Pirvulescu</name></author><category term="ruby-rails" /><category term="sec" /><category term="exploit" /><summary type="html"><![CDATA[Last week, two security patches were added to Rails. One of them was meant to guard against the ANSI escape injection [CVE-2025-55193], a vulnerability affecting Active Record logging. I was curious what an attacker could achieve by exploiting this vulnerability. Here, I logged my findings and created a simple PoC.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">5 simple steps to a lean Docker image</title><link href="https://organicdarius.com/blog/5-simple-steps-to-a-lean-docker-image/" rel="alternate" type="text/html" title="5 simple steps to a lean Docker image" /><published>2025-07-07T00:00:00+00:00</published><updated>2025-07-07T00:00:00+00:00</updated><id>https://organicdarius.com/blog/5-simple-steps-to-a-lean-docker-image</id><content type="html" xml:base="https://organicdarius.com/blog/5-simple-steps-to-a-lean-docker-image/"><![CDATA[<p>Docker is a tool I often use, both for developing personal projects and also during my <a href="/blog/place-an-ssh-honeypot/">Cybersec studies</a>. Recently, I researched how Docker builds an image and discovered ways to limit the image size.</p>

<p>This came after I set up a separate, basic VPS for testing stuff that quickly ran out of storage. Instead of simply upgrading the VPS storage, I went for frugality and optimized my Docker images.</p>

<p>Here are some basic first steps you can take to limit the image size. I tried keeping these steps language agnostic, but I’ll use a Node app to exemplify the concepts.</p>

<p>The build command I used:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>docker build --no-cache -t node-api:v0 .
</code></pre></div></div>
<p>After each step, you’ll see <em>the image size and build time</em>. Notice that the build time may vary based on your system, network connection, time of day, and how depressed your machine is.</p>

<h3 id="initial-build">Initial build</h3>
<p>I’m starting from this Dockerfile:</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">FROM</span><span class="s"> node:latest</span>

<span class="k">WORKDIR</span><span class="s"> /usr/src/node-api</span>

<span class="k">COPY</span><span class="s"> package*.json ./</span>
<span class="k">RUN </span>npm <span class="nb">install</span> <span class="nt">--verbose</span>

<span class="k">COPY</span><span class="s"> . .</span>

<span class="k">RUN </span>npm run build

<span class="k">CMD</span><span class="s"> ["npm", "start"]</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Size:
node-api     v0        1.43GB
Time
Building 17.2s (12/12)
</code></pre></div></div>

<h2 id="steps">Steps</h2>
<h3 id="1-ignore-files">1. Ignore files</h3>

<p>With <code class="language-plaintext highlighter-rouge">.dockerignore</code>. Placed in the root directory. <br />
This speeds up the build and also prevents sensitive files from showing up in the final image.</p>

<p>Here are some more details on <a href="https://docs.docker.com/build/concepts/context/#syntax">syntax</a>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Size:
node-api     v1        1.37GB
Time:
Building 15.3s (12/12)
</code></pre></div></div>

<h3 id="2-base-image">2. Base Image</h3>

<p>This has a major impact on the size of the final image. <br />
The main frameworks offer different image tags to use. Go for the leaner images, as you can save on storage, but this may come with a caveat.</p>

<p>For Node, use <code class="language-plaintext highlighter-rouge">alpine</code> images instead of <code class="language-plaintext highlighter-rouge">latest</code>.  <br />
Alpine-based images are popular for their minimal size and smaller vulnerability count. They are not officially supported by the Node team though. See the list of <a href="https://github.com/nodejs/unofficial-builds/">unofficial Node builds</a>.  <br />
Alpine project uses <a href="https://www.musl-libc.org/">musl</a> to implement the C standard library, whereas Debian’s Node.js tags (for instance <code class="language-plaintext highlighter-rouge">bullseye</code> or <code class="language-plaintext highlighter-rouge">slim</code>) rely on the <code class="language-plaintext highlighter-rouge">glibc</code>. For this reason, it might cause compatibility issues with dependencies that include native code.  <br />
However, <code class="language-plaintext highlighter-rouge">alpine</code> will suffice for most projects.</p>

<p>I downloaded some common Node image tags to compare them:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>REPOSITORY               TAG               SIZE
node                     latest            1.13GB
node                     bookworm          1.13GB
node                     bullseye          1.03GB
node                     slim              230MB
node                     alpine            165MB
</code></pre></div></div>

<p>Additionally, you can use <a href="https://github.com/GoogleContainerTools/distroless">distroless</a> base images. They contain only your app with its runtime dependencies. The package managers, shells, and others are skipped. Using them dramatically decreases the image size and its attack surface.   <br />
This approach is more advanced and out of the scope of this article.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Size:
node-api     v2        408MB
Time:
Building 17.1s (12/12)
</code></pre></div></div>

<h3 id="3-multi-stage-build">3. Multi-stage build</h3>

<p>This allows you to separate the build and runtime envs. You can include only the essential files in the final image.</p>

<p>A Dockerfile accepts multiple <a href="https://docs.docker.com/reference/dockerfile/#from"><code class="language-plaintext highlighter-rouge">FROM</code></a> statements. Each <code class="language-plaintext highlighter-rouge">FROM</code> instruction begins a new stage of the build (and can use a different base image). And each stage can be named with the <code class="language-plaintext highlighter-rouge">AS</code> keyword.</p>

<p>Here is my updated Dockerfile:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c"># Build stage</span>
<span class="k">FROM</span><span class="w"> </span><span class="s">node:alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">build</span>
<span class="k">WORKDIR</span><span class="s"> /usr/src/node-api</span>
<span class="k">COPY</span><span class="s"> package*.json ./</span>
<span class="k">RUN </span>npm <span class="nb">install</span> <span class="nt">--verbose</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">RUN </span>npm run build

<span class="c"># Prod stage</span>
<span class="k">FROM</span><span class="s"> node:alpine</span>
<span class="k">WORKDIR</span><span class="s"> /usr/src/node-api</span>
<span class="k">COPY</span><span class="s"> --from=build /usr/src/node-api/build ./build</span>
<span class="k">COPY</span><span class="s"> package*.json ./</span>
<span class="k">RUN </span>npm <span class="nb">install</span> <span class="nt">--verbose</span>

<span class="k">CMD</span><span class="s"> ["npm", "start"]</span>
</code></pre></div></div>

<p>You can stop the build at a specific stage using the <code class="language-plaintext highlighter-rouge">--target</code> flag:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>docker build <span class="nt">--target</span> build <span class="nt">-t</span> node-api:v3 <span class="nb">.</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Size:
node-api     v3        234MB
Time:
Building 8.6s (15/15)
</code></pre></div></div>

<h3 id="4-skip-dev-dependencies">4. Skip dev dependencies</h3>
<p>Besides multi-stage, you can further skip dev dependencies during install.</p>

<p>For Node, always use <code class="language-plaintext highlighter-rouge">ci</code> (clean install) instead of <code class="language-plaintext highlighter-rouge">i</code>. This command is more efficient and installs the exact versions based on <code class="language-plaintext highlighter-rouge">package-lock.json</code>. It throws an error and exits for any version mismatch. It also accepts an <code class="language-plaintext highlighter-rouge">--omit</code> flag to skip some dependencies.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>RUN npm ci --omit=dev
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Size:
node-api     v4        172MB
Time:
Building 6.3s (16/16)
</code></pre></div></div>

<h3 id="5-merge-layers-and-cleanup-between-them">5. Merge layers and cleanup between them</h3>

<p>We can clean the temporary files that are created after a <code class="language-plaintext highlighter-rouge">RUN</code> instruction. It is common for package managers to install additional components and keep a local cache. We can save space by:</p>
<ul>
  <li>Instructing the package manager to install the minimum dependencies</li>
  <li>Remove the cache after installation, or instruct the package manager to disable the cache altogether</li>
</ul>

<p>For example, after installing Node dependencies, npm creates metadata files that take up space in the image. We can use these commands to remove them:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>RUN npm cache clean --force
RUN rm -rf /tmp/* /var/cache/apk/*
</code></pre></div></div>

<p>For <strong>Debian/Ubuntu</strong> use <code class="language-plaintext highlighter-rouge">--no-install-recomends</code>. It keeps the cache at <code class="language-plaintext highlighter-rouge">/var/lib/apt/lists</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>RUN apt-get install -y --no-install-recomends
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*
</code></pre></div></div>
<p><br />
For <strong>Python (pip)</strong>, we can specify the same with <code class="language-plaintext highlighter-rouge">--no-cache-dir</code>.</p>

<h4 id="limit-the-number-of-layers">Limit the number of layers</h4>

<p>Every instruction in a Dockerfile creates a new layer in the image. Docker utilizes an overlay-type file system, stacking these layers cumulatively.     <br />
In the above examples, even if we instruct to delete the files, <strong>they are not deleted and the image size will not decrease</strong>, so the disk space will not be returned.</p>

<p>We can merge the <code class="language-plaintext highlighter-rouge">RUN</code> commands to avoid this.
If we do the cleanup before the <code class="language-plaintext highlighter-rouge">RUN</code> command is completed, the files we want deleted will not end up in the image:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>RUN npm ci --omit=dev &amp;&amp; \
    npm cache clean --force &amp;&amp; \
    rm -rf /tmp/* /var/cache/apk/*
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Size:
node-api     v5        170MB
Time:
Building 6.7s (16/16)
</code></pre></div></div>

<h2 id="summary">Summary</h2>

<p><img src="/assets/images/posts/docker-imgs-size.png" alt="Final Docker images" />
And my final Dockerfile:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c"># Build stage</span>
<span class="k">FROM</span><span class="w"> </span><span class="s">node:alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">build</span>
<span class="k">WORKDIR</span><span class="s"> /usr/src/node-api</span>
<span class="k">COPY</span><span class="s"> package*.json ./</span>
<span class="k">RUN </span>npm ci <span class="o">&amp;&amp;</span> <span class="se">\
</span>    npm cache clean <span class="nt">--force</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span>    <span class="nb">rm</span> <span class="nt">-rf</span> /tmp/<span class="k">*</span> /var/cache/apk/<span class="k">*</span>i
<span class="k">COPY</span><span class="s"> tsconfig.json ./</span>
<span class="k">COPY</span><span class="s"> src ./src</span>
<span class="k">RUN </span>npm run build

<span class="c"># Prod stage</span>
<span class="k">FROM</span><span class="s"> node:alpine</span>
<span class="k">WORKDIR</span><span class="s"> /usr/src/node-api</span>
<span class="k">COPY</span><span class="s"> --from=build /usr/src/node-api/build ./build</span>
<span class="k">COPY</span><span class="s"> package*.json ./</span>
<span class="k">RUN </span>npm ci <span class="nt">--omit</span><span class="o">=</span>dev <span class="o">&amp;&amp;</span> <span class="se">\
</span>    npm cache clean <span class="nt">--force</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span>    <span class="nb">rm</span> <span class="nt">-rf</span> /tmp/<span class="k">*</span> /var/cache/apk/<span class="k">*</span>


<span class="k">CMD</span><span class="s"> ["npm", "start"]</span>
</code></pre></div></div>

<h2 id="measuring-image-sizes">Measuring image sizes</h2>
<p>You can get the image size by listing the images:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>docker images node-api
</code></pre></div></div>
<p>For more advanced insight, a useful tool is <a href="https://github.com/wagoodman/dive">dive</a>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>dive node-api
</code></pre></div></div>
<p>It offers TUI for interactively exploring a Docker image. You can see each layer in detail, check for wasted space, and identify where you can further optimize.
It breaks down each layer including which files were added their size.</p>

<p>Alternatively, I found out you can just create the image without running it. Then, you can export its contents and inspect them manually:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>docker create node-api:v5
docker container list -a
docker export &lt;CONTAINER_NAME&gt; &gt; node-api.tar
</code></pre></div></div>
<p>Or:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>docker export $(docker create node-api:v5) &gt; node-api.tar
</code></pre></div></div>]]></content><author><name>Darius Pirvulescu</name></author><category term="vps" /><category term="docker" /><summary type="html"><![CDATA[Docker is a tool I often use, both for developing personal projects and also during my Cybersec studies. Recently, I researched how Docker builds an image and discovered ways to limit the image size.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Safeguard against DoS in Rails helper</title><link href="https://organicdarius.com/blog/safeguard-against-dos-in-rails-helper/" rel="alternate" type="text/html" title="Safeguard against DoS in Rails helper" /><published>2025-04-28T00:00:00+00:00</published><updated>2025-04-28T00:00:00+00:00</updated><id>https://organicdarius.com/blog/safeguard-against-dos-in-rails-helper</id><content type="html" xml:base="https://organicdarius.com/blog/safeguard-against-dos-in-rails-helper/"><![CDATA[<p>One recent contribution to the Rails codebase caught my attention. It concerns the <code class="language-plaintext highlighter-rouge">distance_of_time_in_words</code> method. The fix is meant to prevent a possible Denial of Service while using this method.</p>

<p>The contribution was brought by <a href="https://github.com/Stazer">Stazer</a>.
I found out about the <a href="https://github.com/rails/rails/pull/54923">PR</a> in the newsletter <a href="https://world.hey.com/this.week.in.rails/improved-leap-year-counting-performance-and-more-4c28a8ac">This week in Rails</a>.</p>

<h2 id="the-problem">The problem</h2>
<p>The <code class="language-plaintext highlighter-rouge">distance_of_time_in_words</code> method returns the approximate distance in time between two timeframes (can be <code class="language-plaintext highlighter-rouge">Time</code>, <code class="language-plaintext highlighter-rouge">Date</code>, or <code class="language-plaintext highlighter-rouge">DateTime</code> objects or integers) and displays it in a nice, humanized format.
To be correct, the leap years between those two timeframes should be considered. It uses <code class="language-plaintext highlighter-rouge">count</code> and a range to get the number of leap years.</p>

<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">leap_years</span> <span class="o">=</span> <span class="p">(</span><span class="n">from_year</span> <span class="o">&gt;</span> <span class="n">to_year</span><span class="p">)</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="p">(</span><span class="n">from_year</span><span class="o">..</span><span class="n">to_year</span><span class="p">).</span><span class="nf">count</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="no">Date</span><span class="p">.</span><span class="nf">leap?</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">}</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
</code></pre></div></div>

<p>This is a blocking process. The calculation can take a long time if the distance between <code class="language-plaintext highlighter-rouge">from_year</code> and <code class="language-plaintext highlighter-rouge">to_year</code> is big enough.  <br />
Users might be able to trigger this DoS if they can set a timestamp which is then being passed to <code class="language-plaintext highlighter-rouge">distance_of_time_in_words</code>.</p>

<p>I found it interesting how subtle this vulnerability is. The contributor encountered this problem in one of their personal projects and decided to open a PR to Rails.</p>

<h2 id="the-fix">The fix</h2>
<p>This contribution safeguards against DoS. It calculates the leap years in <strong>constant time</strong>.</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="n">fyear</span> <span class="o">=</span> <span class="n">from_year</span> <span class="o">-</span> <span class="mi">1</span>
<span class="p">(</span><span class="n">to_year</span> <span class="o">/</span> <span class="mi">4</span> <span class="o">-</span> <span class="n">to_year</span> <span class="o">/</span> <span class="mi">100</span> <span class="o">+</span> <span class="n">to_year</span> <span class="o">/</span> <span class="mi">400</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">fyear</span> <span class="o">/</span> <span class="mi">4</span> <span class="o">-</span> <span class="n">fyear</span> <span class="o">/</span> <span class="mi">100</span> <span class="o">+</span> <span class="n">fyear</span> <span class="o">/</span> <span class="mi">400</span><span class="p">)</span>
</code></pre></div></div>

<p>I will present how you can test this fix locally.</p>

<h2 id="testing-this-fix">Testing this fix</h2>
<p>For this, I created a new, minimal Rails app:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>rails new my_awesome_app --minimal 
</code></pre></div></div>
<p>Then I wanted to override the <code class="language-plaintext highlighter-rouge">distance_of_time_in_words</code> method. So I created this new file:</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c1"># config/initializer/actionview.rb</span>

<span class="nb">require</span> <span class="s1">'action_view'</span>

<span class="k">module</span> <span class="nn">ActionView::Helpers::DateHelper</span>
  <span class="k">alias</span> <span class="n">__distance_of_time_in_words</span> <span class="n">distance_of_time_in_words</span>
  <span class="kp">private</span> <span class="ss">:__distance_of_time_in_words</span>

  <span class="k">def</span> <span class="nf">distance_of_time_in_words</span><span class="p">(</span><span class="n">_from_time</span><span class="p">,</span> <span class="n">_to_time</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">_options</span> <span class="o">=</span> <span class="p">{})</span>
    <span class="p">[</span><span class="o">...</span><span class="p">]</span>
    <span class="n">leap_years</span> <span class="o">=</span> <span class="k">if</span> <span class="n">from_year</span> <span class="o">&gt;</span> <span class="n">to_year</span>
      <span class="mi">0</span>
    <span class="k">else</span>
      <span class="n">fyear</span> <span class="o">=</span> <span class="n">from_year</span> <span class="o">-</span> <span class="mi">1</span>
      <span class="p">(</span><span class="n">to_year</span> <span class="o">/</span> <span class="mi">4</span> <span class="o">-</span> <span class="n">to_year</span> <span class="o">/</span> <span class="mi">100</span> <span class="o">+</span> <span class="n">to_year</span> <span class="o">/</span> <span class="mi">400</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">fyear</span> <span class="o">/</span> <span class="mi">4</span> <span class="o">-</span> <span class="n">fyear</span> <span class="o">/</span> <span class="mi">100</span> <span class="o">+</span> <span class="n">fyear</span> <span class="o">/</span> <span class="mi">400</span><span class="p">)</span>
    <span class="k">end</span>
    <span class="p">[</span><span class="o">...</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">old_distance_of_time_in_words</span><span class="p">(</span><span class="n">_from_time</span><span class="p">,</span> <span class="n">_to_time</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">_options</span> <span class="o">=</span> <span class="p">{})</span>
    <span class="p">[</span><span class="o">...</span><span class="p">]</span>
    <span class="n">leap_years</span> <span class="o">=</span> <span class="p">(</span><span class="n">from_year</span> <span class="o">&gt;</span> <span class="n">to_year</span><span class="p">)</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="p">(</span><span class="n">from_year</span><span class="o">..</span><span class="n">to_year</span><span class="p">).</span><span class="nf">count</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="no">Date</span><span class="p">.</span><span class="nf">leap?</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">[</span><span class="o">...</span><span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>I replaced the <a href="https://github.com/Stazer/rails/blob/f08836bea882c4daa0cf498f8374416c6d2c74d2/actionview/lib/action_view/helpers/date_helper.rb#L96">rest of the code in the method</a> from the rails repo.</p>

<p>I’m now able to test the fix straight in the Rails console:</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nb">require</span> <span class="s2">"benchmark"</span>

<span class="n">num_years</span> <span class="o">=</span> <span class="mi">100_000_000</span><span class="p">.</span><span class="nf">years</span>
<span class="no">Benchmark</span><span class="p">.</span><span class="nf">bm</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"old"</span><span class="p">)</span> <span class="p">{</span> 
    <span class="no">ApplicationController</span><span class="p">.</span><span class="nf">helpers</span><span class="p">.</span><span class="nf">old_distance_of_time_in_words</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">,</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span> <span class="o">+</span> <span class="n">num_years</span><span class="p">)</span>
  <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"new"</span><span class="p">)</span> <span class="p">{</span>
    <span class="no">ApplicationController</span><span class="p">.</span><span class="nf">helpers</span><span class="p">.</span><span class="nf">distance_of_time_in_words</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">,</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span> <span class="o">+</span> <span class="n">num_years</span><span class="p">)</span>
  <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>         user     system      total        real
old  6.095959   0.000000   6.095959 <span class="o">(</span>  6.096444<span class="o">)</span>
new  0.000117   0.000000   0.000117 <span class="o">(</span>  0.000117<span class="o">)</span>
<span class="o">[</span>...]
</code></pre></div></div>
<p>Here we can see the big difference. The old code counted the leap years in a way that slowed things down, here, taking around 6 seconds to perform the count. <br />
As the number of years between the two dates increases, the computing time grows exponentially. When I tested it with a range of <code class="language-plaintext highlighter-rouge">1,000,000,000</code> years, it took 61 seconds. This has the potential to bring the application to a halt.  <br />
The updated code performs the calculation in constant time, regardless of the numbers of years.</p>]]></content><author><name>Darius Pirvulescu</name></author><category term="ruby-rails" /><summary type="html"><![CDATA[One recent contribution to the Rails codebase caught my attention. It concerns the distance_of_time_in_words method. The fix is meant to prevent a possible Denial of Service while using this method.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">DNS lookup from scratch</title><link href="https://organicdarius.com/blog/dns-lookup-from-scratch/" rel="alternate" type="text/html" title="DNS lookup from scratch" /><published>2025-02-26T00:00:00+00:00</published><updated>2025-02-26T00:00:00+00:00</updated><id>https://organicdarius.com/blog/dns-lookup-from-scratch</id><content type="html" xml:base="https://organicdarius.com/blog/dns-lookup-from-scratch/"><![CDATA[<p>My findings after implementing the DNS query without any library. This domain name system is nicely tucked away in the network drawers, so you don’t even notice it. Nonetheless, it is used by everyone on the internet multiple times a day.</p>

<p>Also called the “phone book of the internet”, DNS helps translate from human-readable hostnames (example.com) to computer-friendly IP addresses (23.192.228.80).</p>

<p>While learning, I put together a toy project, <a href="https://github.com/panacotar/rbdig/">rbdig</a>, written in Ruby, as I’m more comfortable with this language. Due to refactoring, the project’s code might not exactly match the code snippets presented here.</p>

<p>The steps I’ll describe:</p>
<ol>
  <li><strong>Building the DNS request</strong></li>
  <li><strong>Creating a socket and sending the DNS request</strong></li>
  <li><strong>Receiving and parsing the DNS reply</strong></li>
  <li><strong>Handling the recursive queries myself</strong></li>
</ol>

<p>I guided myself using this official document, <a href="https://datatracker.ietf.org/doc/html/rfc1035">RFC1035</a>, to construct the DNS request and parse the response.</p>

<h2 id="step-1-building-the-dns-request">Step 1: Building the DNS request</h2>
<p>The DNS request has two parts:</p>
<ul>
  <li>header (12 bytes)</li>
  <li>question (variable length)</li>
</ul>

<p>I wanted to how this is done by other tools. With the help of Netcat, I captured a DNS lookup from <code class="language-plaintext highlighter-rouge">dig</code>. I also used Wireshark to view the UDP packet as it does a good job of representing network packets.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c"># Start a listener on port 2020 (saving the output to a file)</span>
nc <span class="nt">-u</span> <span class="nt">-l</span> 2020 <span class="o">&gt;</span> dns_lookup.txt

<span class="c"># Send a dig request to that port</span>
dig +retry<span class="o">=</span>0 <span class="nt">-p</span> 2020 @127.0.0.1 +noedns example.com

<span class="c"># Tip: you can use nc to forward the request to a DNS server (ex: Cloudflare's)</span>
nc <span class="nt">-u</span> 1.1.1.1 53 &lt; dns_lookup.txt <span class="o">&gt;</span> resp_dns_lookup.txt
</code></pre></div></div>

<p>This is the whole request sent by <code class="language-plaintext highlighter-rouge">dig</code> (as hex bytes):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>840f01200001000000000000076578616d706c6503636f6d0000010001
</code></pre></div></div>
<p>But what does it all mean?</p>

<p>The first 12 bytes are the header: <code class="language-plaintext highlighter-rouge">840f01200001000000000000</code>. I spread the method handling so that it includes comments for each component. <br />
The header is described in section <a href="https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1">4.1.1</a>. You can find further info on what each means.</p>

<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">def</span> <span class="nf">query_header</span>
  <span class="n">query_id</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x84\x0f</span><span class="s2">"</span> <span class="c1"># 2 random bytes. When we get the response, the same bytes should be included</span>
  <span class="n">flags</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x01\x00</span><span class="s2">"</span>    <span class="c1"># the standard flag</span>
  <span class="n">qd_count</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x00\x01</span><span class="s2">"</span> <span class="c1"># the # of entries in the question section</span>
  <span class="n">an_count</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x00\x00</span><span class="s2">"</span> <span class="c1"># the # of resource records in the answer session</span>
  <span class="n">ns_count</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x00\x00</span><span class="s2">"</span> <span class="c1"># the # of name server resource records (in authority records section)</span>
  <span class="n">ar_count</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x00\x00</span><span class="s2">"</span> <span class="c1"># the # of resource records</span>
  <span class="n">query_id</span> <span class="o">+</span> <span class="n">flags</span> <span class="o">+</span> <span class="n">qd_count</span> <span class="o">+</span> <span class="n">an_count</span> <span class="o">+</span> <span class="n">ns_count</span> <span class="o">+</span> <span class="n">ar_count</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Now that the header is handled, I could move to the next section.  <br />
The <strong>question section</strong> is made of:</p>
<ul>
  <li>question name - the actual domain name we’re looking for</li>
  <li>query type - the type of record we’re looking for (ex: “A” for IPv4 record)</li>
  <li>query class - the class of record we’re looking for (ex: “IN” for the INternet)</li>
</ul>

<p>The more complex part was building the question name. DNS has a format for encoding domain names. It follows a sequence of labels. Each label is made of a length octet + that number of octets. The domain name is terminated with a null label <code class="language-plaintext highlighter-rouge">\x00</code>.</p>

<p>A domain name as <code class="language-plaintext highlighter-rouge">www.example.com</code> becomes <code class="language-plaintext highlighter-rouge">3www7example3com0</code>. My code for this:</p>

<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">def</span> <span class="nf">encode_domain</span><span class="p">(</span><span class="n">domain</span><span class="p">)</span>
  <span class="n">enc</span> <span class="o">=</span> <span class="n">domain</span><span class="p">.</span><span class="nf">strip</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s1">'.'</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span> <span class="p">[</span><span class="n">s</span><span class="p">.</span><span class="nf">length</span><span class="p">].</span><span class="nf">pack</span><span class="p">(</span><span class="s2">"C"</span><span class="p">)</span> <span class="o">+</span> <span class="n">s</span> <span class="p">}.</span><span class="nf">join</span>
  <span class="n">enc</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\x00</span><span class="s2">"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Here is a screenshot of the request in Wireshark. If you want to reproduce this, set Wireshark to listen to the loopback interface and filter for the right <code class="language-plaintext highlighter-rouge">udp.port</code>.</p>

<p><img src="/assets/images/posts/wireshark_dig_capture.png" alt="Wireshark-dig-capture" /></p>

<p>I put all the encoding logic in the <a href="https://github.com/panacotar/rbdig/blob/afddbbd202d002ca83973a751651f7b703f5abbc/lib/message.rb#L1">DNSQuery</a> class.</p>

<h2 id="step-2-creating-a-socket-and-sending-the-dns-request">Step 2: Creating a socket and sending the DNS request</h2>
<p>I’ll not get into details here. The idea is to get this request out and listen to a response from the DNS server. <br />
I created a UDP socket for this and wrapped everything in the <code class="language-plaintext highlighter-rouge">connect</code> method.</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">def</span> <span class="nf">connect</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">server</span> <span class="o">=</span> <span class="s1">'1.1.1.1'</span><span class="p">,</span> <span class="n">port</span> <span class="o">=</span> <span class="mi">53</span><span class="p">)</span>
  <span class="n">socket</span> <span class="o">=</span> <span class="no">UDPSocket</span><span class="p">.</span><span class="nf">new</span>
  <span class="n">socket</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">server</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span>
  <span class="n">response</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="nf">recvfrom</span><span class="p">(</span><span class="mi">512</span><span class="p">)</span> <span class="c1"># RFC1035 specifies a 512 octets size limit for UDP messages</span>

  <span class="n">socket</span><span class="p">.</span><span class="nf">close</span>
  <span class="n">response</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="step-3-receiving-and-parsing-the-dns-reply">Step 3: Receiving and parsing the DNS reply</h2>
<p>The DNS server will send back the response, which might or might not include the answer (the IPv4 address in our case). After receiving the response, I validated it has the same <code class="language-plaintext highlighter-rouge">query_id</code> as the request, and I was starting to parse it. Basically, I reversed the steps I used when building the request, and parsing the header and question sections. But, <strong>in addition</strong>, the DNS response might include 3 more sections, each including zero or more Resource Records (RRs):</p>

<ul>
  <li>Answers - the answer we’re looking for</li>
  <li>Authorities (NS records) - when a nameserver doesn’t have the answer, it will redirect you to other servers</li>
  <li>Additional - also when a nameserver doesn’t have the answer, but it includes the IPv4 address of those servers that might have the answer. This section could contain other data, but that’s out of the scope of this article.
These Resource Records (RRs) all have the same format.</li>
</ul>

<p>Here is a visual of how the DNS response is structured (source: RFC1035):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>+---------------------+
|        Header       |
+---------------------+
|       Question      |     # the question for the name server
+---------------------+
|        Answer       |     # RRs with the answer
+---------------------+
|      Authority      |     # RRs pointing toward authority servers
+---------------------+
|      Additional     |     # RRs holding additional information
+---------------------+
</code></pre></div></div>

<h3 id="a-word-on-reader">A word on <code class="language-plaintext highlighter-rouge">Reader</code></h3>
<p>The DNS response will be a string of bytes, I needed to go over it while parsing. To keep track of where I was in the string, I created the <code class="language-plaintext highlighter-rouge">Reader</code> class. This gets initialized with a string. It can read a specific number of bytes from that string while keeping a pointer of the position I’m in the string. <br />
A brief example:</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="n">r</span> <span class="o">=</span> <span class="no">Reader</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"</span><span class="se">\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A</span><span class="s2">"</span><span class="p">.</span><span class="nf">b</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="nf">pos</span>     <span class="c1"># =&gt; 0</span>
<span class="n">r</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># =&gt; "\x00\x01"</span>
<span class="n">r</span><span class="p">.</span><span class="nf">pos</span>    <span class="c1"># =&gt; 2</span>
</code></pre></div></div>

<p>Ruby has the <code class="language-plaintext highlighter-rouge">StringIO</code> class, which does this and more. But for this project, I wanted to implement the functionality I needed.</p>

<h3 id="the-parsing-class">The parsing class</h3>
<p>I created the <code class="language-plaintext highlighter-rouge">DNSResponse</code> class responsible for handling the response. It accepts the raw response and initiates an instance of <code class="language-plaintext highlighter-rouge">Reader</code> with that bytes string:</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">class</span> <span class="nc">DNSResponse</span>
  <span class="nb">attr_reader</span> <span class="ss">:header</span><span class="p">,</span> <span class="ss">:body</span><span class="p">,</span> <span class="ss">:answers</span><span class="p">,</span> <span class="ss">:authorities</span><span class="p">,</span> <span class="ss">:additional</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">dns_reply</span><span class="p">)</span>
    <span class="vi">@buffer</span> <span class="o">=</span> <span class="no">Reader</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">dns_reply</span><span class="p">.</span><span class="nf">b</span><span class="p">)</span>
    <span class="vi">@header</span> <span class="o">=</span> <span class="n">parse_header</span>
    <span class="vi">@body</span> <span class="o">=</span> <span class="n">parse_body</span>
    <span class="vi">@answers</span> <span class="o">=</span> <span class="n">parse_resource_records</span><span class="p">(</span><span class="vi">@header</span><span class="p">[</span><span class="ss">:an_count</span><span class="p">])</span>
    <span class="vi">@authorities</span> <span class="o">=</span> <span class="n">parse_resource_records</span><span class="p">(</span><span class="vi">@header</span><span class="p">[</span><span class="ss">:ns_count</span><span class="p">])</span>
    <span class="vi">@additional</span> <span class="o">=</span> <span class="n">parse_resource_records</span><span class="p">(</span><span class="vi">@header</span><span class="p">[</span><span class="ss">:ar_count</span><span class="p">])</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">parse_header</span>
    <span class="n">query_id</span><span class="p">,</span> <span class="n">flags</span><span class="p">,</span> <span class="n">qd_count</span><span class="p">,</span> <span class="n">an_count</span><span class="p">,</span> <span class="n">ns_count</span><span class="p">,</span> <span class="n">ar_count</span> <span class="o">=</span> <span class="vi">@buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">12</span><span class="p">).</span><span class="nf">unpack</span><span class="p">(</span><span class="s1">'n6'</span><span class="p">)</span>
    <span class="p">{</span> <span class="n">query_id</span><span class="p">:,</span> <span class="n">flags</span><span class="p">:,</span> <span class="n">qd_count</span><span class="p">:,</span> <span class="n">an_count</span><span class="p">:,</span> <span class="n">ns_count</span><span class="p">:,</span> <span class="ss">ar_count: </span><span class="p">}</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">parse_body</span>
    <span class="n">question</span> <span class="o">=</span> <span class="n">extract_domain_name</span><span class="p">(</span><span class="vi">@buffer</span><span class="p">)</span>
    <span class="n">q_type</span> <span class="o">=</span> <span class="vi">@buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="nf">unpack</span><span class="p">(</span><span class="s1">'n'</span><span class="p">).</span><span class="nf">first</span>
    <span class="n">q_class</span> <span class="o">=</span> <span class="vi">@buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="nf">unpack</span><span class="p">(</span><span class="s1">'n'</span><span class="p">).</span><span class="nf">first</span>
    <span class="p">{</span> <span class="n">question</span><span class="p">:,</span> <span class="n">q_type</span><span class="p">:,</span> <span class="ss">q_class: </span><span class="p">}</span>
  <span class="k">end</span>
  <span class="p">[</span><span class="o">...</span><span class="p">]</span>
</code></pre></div></div>
<p>Extracting the domain name was maybe the most complex part. Up until now, it is straightforward, I could transform from <code class="language-plaintext highlighter-rouge">\x07example\x03com\x00</code> to <code class="language-plaintext highlighter-rouge">example.com</code> and that would suffice.  <br />
However, I encountered some exceptions while I progressed to parsing the RR sections. Here is the method which does the parsing. It is neat that all RRs I care about for now have the same format.</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c1"># class DNSResponse</span>
<span class="k">def</span> <span class="nf">parse_resource_records</span><span class="p">(</span><span class="n">num_records</span><span class="p">)</span>
  <span class="c1"># It returns an array of records if any</span>
  <span class="n">num_records</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">collect</span> <span class="k">do</span>
    <span class="n">rr_name</span> <span class="o">=</span> <span class="n">extract_domain_name</span><span class="p">(</span><span class="vi">@buffer</span><span class="p">)</span>              <span class="c1"># A domain name to which this RR belongs</span>
    <span class="n">rr_type</span><span class="p">,</span> <span class="n">rr_class</span> <span class="o">=</span> <span class="vi">@buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">4</span><span class="p">).</span><span class="nf">unpack</span><span class="p">(</span><span class="s1">'n2'</span><span class="p">)</span>    <span class="c1"># The type &amp; class of this record</span>
    <span class="n">ttl</span> <span class="o">=</span> <span class="vi">@buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">4</span><span class="p">).</span><span class="nf">unpack</span><span class="p">(</span><span class="s1">'N'</span><span class="p">).</span><span class="nf">first</span>             <span class="c1"># Time-to-live for this record (how long it should be cached)</span>
    <span class="n">rr_data_length</span> <span class="o">=</span> <span class="vi">@buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="nf">unpack</span><span class="p">(</span><span class="s1">'n'</span><span class="p">).</span><span class="nf">first</span>  <span class="c1"># The length (bytes) of the rr_data field</span>
    <span class="c1"># Data describing the resource, variable length depending on the type of resource.</span>
    <span class="c1"># Ex: for TYPE='A' and CLASS='IN', the data = IPv4 address (4 bytes length)</span>
    <span class="n">rr_data</span> <span class="o">=</span> <span class="n">extract_record_data</span><span class="p">(</span><span class="vi">@buffer</span><span class="p">,</span> <span class="n">rr_type</span><span class="p">,</span> <span class="n">rr_data_length</span><span class="p">)</span>
    <span class="p">{</span> <span class="n">rr_name</span><span class="p">:,</span> <span class="n">rr_type</span><span class="p">:,</span> <span class="n">rr_class</span><span class="p">:,</span> <span class="n">ttl</span><span class="p">:,</span> <span class="n">rr_data_length</span><span class="p">:,</span> <span class="ss">rr_data: </span><span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># Sample RR</span>
<span class="c1"># {:rr_name=&gt;"com", :rr_type=&gt;2, :rr_class=&gt;1, :ttl=&gt;172800, :rr_data_length=&gt;20, :rr_data=&gt;"a.gtld-servers.net"}</span>
</code></pre></div></div>

<h3 id="handling-dns-compression-and-preventing-loops">Handling DNS compression and preventing loops</h3>
<p>When the server encodes the DNS message, there might be repeated domain names. In order to keep the message size to a minimum, the domain system uses a compression scheme. If a certain value appeared beforehand in the message, instead of repeating the same name, it places a <strong>pointer</strong> to a previous occurrence of the same name. How does this look in practice?  <br />
If we search for <code class="language-plaintext highlighter-rouge">example.com</code>, the server might not have the answer, so it directs you to various <code class="language-plaintext highlighter-rouge">.com</code> TLD servers. It lists NS records, so, when it encodes the <code class="language-plaintext highlighter-rouge">rr_name</code> field, instead of repeating <code class="language-plaintext highlighter-rouge">com</code>, it points you to the <em>question section</em> which has the <code class="language-plaintext highlighter-rouge">com</code> value.</p>

<p>How does the pointer… points? <br />
A domain label can have a maximum length of 63 character, or <code class="language-plaintext highlighter-rouge">00111111</code>. Notice those two leading zeros? They can be used to differentiate a label from a pointer. The octet that points will have the first two bits set to one <code class="language-plaintext highlighter-rouge">11000000</code> (which is <code class="language-plaintext highlighter-rouge">\xc0</code> in hex, 192 in decimal). The byte values starting with <code class="language-plaintext highlighter-rouge">01</code> &amp; <code class="language-plaintext highlighter-rouge">10</code> are reserved for future use.  <br />
Then, it indicates the <strong>offset</strong>, the position where we can find the label. This is the remaining 14 bits.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1  1|                OFFSET                   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
</code></pre></div></div>

<p><br /></p>

<p><img src="/assets/images/posts/dns_response_compression.png" alt="DNS-pointer-compression" /></p>

<p><br /></p>

<p>Here is a reply from a DNS root server answering to <code class="language-plaintext highlighter-rouge">example.com</code>. The first pointer in the response is highlighted (<code class="language-plaintext highlighter-rouge">\xc0\x14</code>). We start by reading this and notice the first byte is (<code class="language-plaintext highlighter-rouge">\xc0</code>) indicating a pointer. We would read the rest of the byte and sum it to the second byte to see where it points to, <code class="language-plaintext highlighter-rouge">\x14</code> is 20 in decimal. So we would need to go back to position 20 and read the label from there. The label is <code class="language-plaintext highlighter-rouge">com</code> in this example.</p>

<p>Notice also the other highlighted pointers. This shows how DNS compression saved message estate by preventing repetition. <br />
Section <a href="https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4">4.1.4</a> of the RFC1035 describes the DNS compression.</p>

<p>Here is the code for extracting the domain name and handling DNS compression:</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c1"># class DNSResponse</span>
<span class="k">def</span> <span class="nf">extract_domain_name</span><span class="p">(</span><span class="n">buffer</span><span class="p">)</span>
  <span class="n">domain_labels</span> <span class="o">=</span> <span class="p">[]</span>
  <span class="kp">loop</span> <span class="k">do</span>
    <span class="n">read_length</span> <span class="o">=</span> <span class="n">buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="nf">bytes</span><span class="p">.</span><span class="nf">first</span>
    <span class="k">break</span> <span class="k">if</span> <span class="n">read_length</span> <span class="o">==</span> <span class="mi">0</span>
    <span class="k">if</span> <span class="n">read_length</span> <span class="o">==</span> <span class="mb">0b11000000</span>
      <span class="c1"># Byte is pointer (DNS compression)</span>
      <span class="n">pointing_to</span> <span class="o">=</span> <span class="n">buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="nf">bytes</span><span class="p">.</span><span class="nf">first</span>
      <span class="n">current_pos</span> <span class="o">=</span> <span class="n">buffer</span><span class="p">.</span><span class="nf">pos</span>
      <span class="n">buffer</span><span class="p">.</span><span class="nf">pos</span> <span class="o">=</span> <span class="n">pointing_to</span>
      <span class="n">domain_labels</span> <span class="o">&lt;&lt;</span> <span class="n">extract_domain_name</span><span class="p">(</span><span class="n">buffer</span><span class="p">)</span>
      <span class="n">buffer</span><span class="p">.</span><span class="nf">pos</span> <span class="o">=</span> <span class="n">current_pos</span>
      <span class="k">break</span>
    <span class="k">else</span>
      <span class="c1"># Normal case, read the label as it is</span>
      <span class="n">domain_labels</span> <span class="o">&lt;&lt;</span> <span class="n">buffer</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">read_length</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
  <span class="n">domain_labels</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"."</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>
<h4 id="preventing-an-infinite-loop">Preventing an infinite loop</h4>

<p>When RFC1035 was created, it didn’t warn about any harmful implementations of DNS compression. If we blindly follow the pointer without validating its value, we expose ourselves to memory corruption bugs and buffer overruns. This open the gates to possible DoS and even RCE attacks.</p>

<p>For example, if the pointer is set to <code class="language-plaintext highlighter-rouge">\xff\xff</code>, the offset value will be 16383, way out of the bounds of a DNS packet.
The same is for decoding the domain name, we should make sure the length label’s value is no more than 63, so we prevent reading from other parts of memory.</p>

<p>Or if a pointer will offset to the current position minus one, to the pointer itself, that is, it will result in an infinite loop.</p>

<p>Here is the <a href="https://github.com/panacotar/rbdig/blob/cae23ae5f10b9f0e4dd023f3e49849cd5275e90f/lib/rbdig/response.rb#L42">method</a>, updated for handling these edge cases.</p>

<h2 id="a-simple-query">A simple query</h2>
<p>Up until this point, I could do this basic query. Notice we’re asking Cloudflare’s DNS resolver, which will do all the work, sending subsequent queries to find the domain address (if not already cached).</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="n">domain</span> <span class="o">=</span> <span class="s2">"example.com"</span>
<span class="n">dns_resolver</span> <span class="o">=</span> <span class="s2">"1.1.1.1"</span>
<span class="n">query_id</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x00\x01</span><span class="s2">"</span>

<span class="n">msg</span> <span class="o">=</span> <span class="no">DNSQuery</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">query_id</span><span class="p">).</span><span class="nf">query_message</span><span class="p">(</span><span class="n">domain</span><span class="p">)</span>
<span class="n">socket_response</span> <span class="o">=</span> <span class="n">connect</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">dns_resolver</span><span class="p">)</span>
<span class="k">raise</span> <span class="s2">"Invalid response: query ID mismatch."</span> <span class="k">if</span> <span class="n">socket_response</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="n">query_id</span>

<span class="n">dns_response</span> <span class="o">=</span> <span class="no">DNSResponse</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">socket_response</span><span class="p">).</span><span class="nf">parse</span>
<span class="k">if</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">answers</span><span class="p">.</span><span class="nf">any?</span>
  <span class="nb">puts</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">answers</span><span class="p">.</span><span class="nf">first</span><span class="p">[</span><span class="ss">:rr_data</span><span class="p">]</span>
<span class="k">else</span>
  <span class="nb">puts</span> <span class="s2">"Answer not found for </span><span class="si">#{</span><span class="n">domain</span><span class="si">}</span><span class="s2">."</span>
<span class="k">end</span>
<span class="c1"># =&gt; 23.215.0.138</span>
</code></pre></div></div>

<h2 id="4-no-answer-on-the-first-try-looping-and-querying-ns-servers">4. No answer on the first try? (looping and querying NS servers)</h2>
<p>I wanted to see the whole DNS process, and until now, my request flags have the Recursive Desired (RD) bit set to one. This means, I rely on the DNS server to handle any further queries until it finds the answer (if it supports RD). The conversation will be:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>me: Can you tell me the IP address for "example.com"?
DNS server: I don't have it, but I'll ask other servers and come back with an answer.
</code></pre></div></div>
<p>If setting RD to zero, the discussion will be:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>me: Can you tell me the IP address for "example.com"?
DNS server: I don't have it, but here is a list of servers who might know.
</code></pre></div></div>
<p>The new flag will then be <code class="language-plaintext highlighter-rouge">\x00\x00</code>, and I’ll also switch to querying one of the root servers (ex: <em>l.root-servers.net</em> at <code class="language-plaintext highlighter-rouge">199.7.83.42</code>).</p>

<p>This new modification means I need to send more queries if the first one doesn’t return an answer. I’ll use a loop and always check the <code class="language-plaintext highlighter-rouge">answers</code> section of the DNS response. If no answers, the DNS server will hopefully return a list of records (in the <code class="language-plaintext highlighter-rouge">additional</code> section) with their own IP addresses. I will use it to query the next servers, which are likely to have the answer.  <br />
In some cases, the response has no additional records, but instead, the <code class="language-plaintext highlighter-rouge">authorities</code> section contains a list of authoritative nameservers. They are presented with their domain names instead of the IP address, which requires me to find out their own IP address before querying them.</p>

<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="k">def</span> <span class="nf">lookup</span><span class="p">(</span><span class="n">domain</span><span class="p">)</span>
  <span class="n">nameserver</span> <span class="o">=</span>  <span class="s1">'199.7.83.42'</span> <span class="c1"># l.root-servers.net</span>
  <span class="n">max_lookups</span> <span class="o">=</span> <span class="mi">10</span>

  <span class="n">max_lookups</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span>
    <span class="nb">puts</span> <span class="s2">"Querying </span><span class="si">#{</span><span class="n">nameserver</span><span class="si">}</span><span class="s2"> for </span><span class="si">#{</span><span class="n">domain</span><span class="si">}</span><span class="s2">"</span>
    <span class="n">query_id</span> <span class="o">=</span> <span class="p">[</span><span class="nb">rand</span><span class="p">(</span><span class="mi">65_535</span><span class="p">)].</span><span class="nf">pack</span><span class="p">(</span><span class="s1">'n'</span><span class="p">)</span>
    <span class="n">msg</span> <span class="o">=</span> <span class="no">DNSQuery</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">query_id</span><span class="p">).</span><span class="nf">query_message</span><span class="p">(</span><span class="n">domain</span><span class="p">)</span>
    <span class="n">socket_response</span> <span class="o">=</span> <span class="n">connect</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">nameserver</span><span class="p">)</span>
    <span class="k">raise</span> <span class="s2">"Invalid response: query ID mismatch."</span> <span class="k">if</span> <span class="n">socket_response</span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="n">query_id</span>

    <span class="n">dns_response</span> <span class="o">=</span> <span class="no">DNSResponse</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">socket_response</span><span class="p">).</span><span class="nf">parse</span>

    <span class="k">if</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">answers</span><span class="p">.</span><span class="nf">any?</span>
      <span class="c1"># The query found an answer</span>
      <span class="k">return</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">answers</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="ss">:rr_data</span><span class="p">]</span>
    <span class="k">end</span>

    <span class="k">if</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">additional</span><span class="p">.</span><span class="nf">any?</span>
      <span class="c1"># No answer, try querying these additional resources</span>
      <span class="n">nameserver</span> <span class="o">=</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">additional</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="ss">:rr_data</span><span class="p">]</span>
      <span class="k">next</span>
    <span class="k">end</span>

    <span class="k">if</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">authorities</span><span class="p">.</span><span class="nf">any?</span>
      <span class="c1"># No answer, but here is the authority servers that might know the answer</span>
      <span class="n">ns_name</span> <span class="o">=</span> <span class="n">dns_response</span><span class="p">.</span><span class="nf">authorities</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="ss">:rr_data</span><span class="p">]</span> <span class="c1"># An example: a.iana-servers.net</span>
      <span class="c1"># Lookup authority server's IP address</span>
      <span class="n">nameserver</span> <span class="o">=</span> <span class="n">lookup</span><span class="p">(</span><span class="n">ns_name</span><span class="p">)</span>
      <span class="k">next</span>
    <span class="k">end</span>
  <span class="k">end</span>
  <span class="k">raise</span> <span class="s2">"Max lookups reached."</span>
<span class="k">end</span>

<span class="n">result</span> <span class="o">=</span> <span class="n">lookup</span><span class="p">(</span><span class="s1">'example.com'</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">Answer: </span><span class="si">#{</span><span class="n">result</span><span class="si">}</span><span class="s2">"</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Querying 199.7.83.42 for example.com
Querying 192.5.6.30 for example.com
Querying 199.7.83.42 for a.iana-servers.net
Querying 192.5.6.30 for a.iana-servers.net
Querying 199.43.135.53 for a.iana-servers.net
Querying 199.43.135.53 for example.com

Answer: 23.192.228.80
</code></pre></div></div>
<p>In the latest implementation of my <a href="https://github.com/panacotar/rbdig/tree/cae23ae5f10b9f0e4dd023f3e49849cd5275e90f">project</a>, the same can be achieved with the command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>./dig.rb -t -s example.com
</code></pre></div></div>
<p>This was the implementation of a recursive, and then iterative DNS query, from scratch. There are infinite improvements and features that can be added this project, like:</p>
<ul>
  <li>support querying other record types, and other record classes</li>
  <li>DNSSEC</li>
  <li>ability to resolve a list of domain names, instead of a single domain</li>
  <li>support for reverse DNS lookups</li>
  <li>etc.</li>
</ul>

<p>Just some features I might add in the future.</p>]]></content><author><name>Darius Pirvulescu</name></author><category term="networking" /><summary type="html"><![CDATA[My findings after implementing the DNS query without any library. This domain name system is nicely tucked away in the network drawers, so you don’t even notice it. Nonetheless, it is used by everyone on the internet multiple times a day.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">2024 annual review</title><link href="https://organicdarius.com/blog/2024-annual-review/" rel="alternate" type="text/html" title="2024 annual review" /><published>2025-01-06T00:00:00+00:00</published><updated>2025-01-06T00:00:00+00:00</updated><id>https://organicdarius.com/blog/2024-annual-review</id><content type="html" xml:base="https://organicdarius.com/blog/2024-annual-review/"><![CDATA[<p>I’m continuing the <a href="/blog/2023-annual-review/">practice</a> of reflecting on the year that passed.</p>

<p>Looking back at 2024, I will answer these questions:</p>
<ol>
  <li><strong>What went well this year?</strong></li>
  <li><strong>What didn’t go so well this year?</strong></li>
  <li><strong>What am I working towards?</strong></li>
</ol>

<h2 id="1-what-went-well-this-year">1. <strong>What went well this year?</strong></h2>
<h3 id="my-travel-in-south-america">My travel in South America</h3>
<p>This trip has been my plan for a very long time, and I’m so happy to finally be able to take it. <br />
It’s been ten months of wandering on this beautiful continent. During this time, I met many lovely people, made some strong connections, and even started building a relationship with a special person.  My Spanish has improved here, and I now feel comfortable engaging in more advanced conversations.  <br />
It’s hard to capture my whole journey here.  <br />
I volunteered at a hostel in Montevideo for around five weeks, then at a farm outside Buenos Aires, and finally a month at a dog shelter in Peru.</p>

<p>I’ve spent quite some time in Uruguay. There is a wonderful tech community here, and I was pleasantly surprised by how the size of it. Ruby/Rails meetups are regularly held here, I joined a couple of them, one even had around 50-60 participants. Everybody is so welcoming and approachable, they even applauded me for being from Romania. They are definitely doing an excellent job bringing the community together.  <br />
The infosec community is not lacking here also, there are almost monthly OWASP meetups, culminating with the December meetup by <a href="https://appsecriodelaplata.org/">OWASP Rio de la Plata</a>. There were around 600 participants and lots of awesome speakers. The talks were in Spanish, but I was able to understand them (ok, maybe around 80% of what was going on).</p>

<h3 id="letting-go-of-social-anxiety">Letting go of social anxiety</h3>
<p>As I traveled almost this entire year, the socializing opportunities were all around. I was looking for hostel stays. Although intense at times, being in this highly social environment is beneficial for building social skills. <br />
All this helped me become comfortable with new spaces, and approaching people I don’t know yet. It even builds resilience by adapting to the unknown and to diverse perspectives.</p>

<p>The ability to meet people, form connections, and exchange ideas is so important. Even more essential in the era of AI where the threat of replacement looms over everyone. That’s why I’m stressing fine-tuning these abilities.</p>

<h3 id="my-first-contribution-to-an-open-source-project">My first contribution to an open-source project</h3>
<p>Another thing I had under my radar for a long time. Contributing to open-source projects always felt intimidating. This past year I committed to this, got over my insecurities, and made it happen.  <br />
My very first contribution went to the Casa project from <a href="https://rubyforgood.org/">Ruby for Good</a> organization. This is a group of tech professionals who create software solutions for social good.</p>

<p><img src="/assets/images/posts/casa_contrib.png" alt="Casa-contrib" /></p>

<p>The maintainers were patient and communicated well, resulting in a positive experience.</p>

<p>Soon after, I pushed other contributions toward Casa and another Ruby for Good project, Human Essentials.</p>

<p>In the future, I intend to contribute more towards these positive projects. I recommend this to everybody thinking about contributing, just take the leap. It is an awesome way to shape your skills, meet others in this field, and learn from people more advanced than you.</p>

<h2 id="2-what-didnt-go-so-well-this-year">2. <strong>What didn’t go so well this year?</strong></h2>
<p>This was a truly positive year, and I have no regrets. It took me a while to think about what would fall under this header.  <br />
But some things can be mentioned.</p>
<h3 id="not-having-a-routine-to-learn-cybersec">Not having a routine to learn cybersec</h3>
<p>Traveling and changing places so often is not conducive to following a routine while learning. I didn’t enjoy as much time as I wanted to sit down and learn more about this field. I focused on exploring my surroundings, meeting people, and improving my social skills. This made the most sense as I was fortunate enough to be in this travel and wanted to take advantage of it.</p>

<p>Nevertheless, I didn’t fully stop learning. I spent most of the time on the Hack The Box platform which offers some of the best learning resources and labs I found.  <br />
I finished their “Information Security Foundations” path and almost all “SOC Analyst” job role path, plus other misc resources. This encompasses around 22 completed modules.</p>

<h3 id="not-being-consistent-with-sportphysical-exercise">Not being consistent with sport/physical exercise</h3>
<p>This is again related to the absence of a routine. Besides some hiking in nature and exploring the cities on foot, I haven’t practiced physical exercise as often as I wanted.  <br />
This lack of exercise catches on me and I feel I’m becoming more sluggish. That is not just physical, but mental as well. Even my creativity gets affected after a while.   <br />
I realized that I don’t like indoor gyms, except for bouldering walls, and running in a city with so many cars isn’t enjoyable for me.</p>

<h2 id="3-what-am-i-working-towards">3. <strong>What am I working towards?</strong></h2>
<h3 id="continue-my-cybersec-journey-getting-certified">Continue my cybersec journey (getting certified)</h3>
<p>I’m committed to this transition and there are so many new things to learn. This time of travel allowed me to explore this field better. Playing around with different aspects of cybersecurity, I’m now leaning more on the Blue teaming/defensive security. This is generally considered a more boring side of the cybersec, but for some reason, it caught on me. I don’t find it boring at all. Discovering ways to harden a system, analyzing malware, or searching the logs for post-attack artifacts all sounds fascinating. And even more, wondering about ways to integrate AI into this.  <br />
Blue teaming is in itself a grand system, and I’m still figuring out my place in it. Inspired by some people in the cybersec space, I started documenting my journey in this field.</p>

<h3 id="connecting-with-professionals-in-the-cybersec-space">Connecting with professionals in the cybersec space</h3>
<p>Simply gathering knowledge is not enough, meeting colleagues is an essential part. I realized I don’t personally know many people in this field, maybe there are two I can think of. When I switched to programming, I was in the same spot, but eventually, I got to build a good network of mentors and peers.  <br />
These next years, I’m looking to do the same in the cybersecurity field.</p>

<h3 id="more-contributing-to-open-source-projects">More contributing to open-source projects</h3>
<p>After my initial contribution, I recognized its learning potential. You get access to a legacy project with more advanced issues. These complex issues will challenge you when finding ways to solve them. It also improves communication and collaboration skills, if you’re stuck, you can get help from the team. Also, as the code and git history are public, you can see how others have solved more complex issues before.  <br />
In the future, I’m looking to contribute more to these positive projects, focussed more on those related to cybersecurity and ruby/rails.</p>]]></content><author><name>Darius Pirvulescu</name></author><category term="annual-review" /><summary type="html"><![CDATA[I’m continuing the practice of reflecting on the year that passed.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Place an SSH honeypot</title><link href="https://organicdarius.com/blog/place-an-ssh-honeypot/" rel="alternate" type="text/html" title="Place an SSH honeypot" /><published>2024-10-28T00:00:00+00:00</published><updated>2024-10-28T00:00:00+00:00</updated><id>https://organicdarius.com/blog/place-an-ssh-honeypot</id><content type="html" xml:base="https://organicdarius.com/blog/place-an-ssh-honeypot/"><![CDATA[<p>After deploying my VPS and taking <a href="/blog/linux-vps-hardening-a-checklist/">steps to secure it</a>, I had the original SSH port (22) inactive. But it kept me curious about the default SSH activity going on there. How much brute forcing is happening on a publicly exposed server? I started experimenting with honeypots to find out more.</p>

<p>But first, let’s get the definition out of the way. In Cybersecurity, a honeypot is a decoy resource designed to look as a legitimate target. It is often deployed to distract attackers from the important resources on the network, and/or profiling potential threats.</p>

<h2 id="choosing-a-honeypot">Choosing a honeypot</h2>
<p>There are plenty of honeypots available, each a for different purpose, deployment context, OS, and network systems. <br />
For my needs, I wanted a low-interaction SSH honeypot, not too resource-intensive, compatible with Linux, and relatively easy to set up and understand.</p>

<p>After tinkering with some of them, I’m describing here the <strong>Basic SSH Honeypot</strong> created by <a href="https://github.com/sjbell">Simon Bell</a>.  <br />
I’ve forked and updated it to suit my needs, and you can find it <a href="https://github.com/panacotar/basic_ssh_honeypot">here</a>.</p>

<h2 id="prerequisites">Prerequisites</h2>
<blockquote>
  <p><strong>Important</strong>: Using this honeypot setup is only meant to be tested on a vanilla installation of Ubuntu.</p>
</blockquote>

<p>I highly recommend having a simple VPS exclusive for testing honeypots; unless you know what you’re doing, don’t play with this on your production server. Although tiny, there’s a chance honeypots have (undiscovered) vulnerabilities. Allowing attackers to escape the honeypot, so to say, and get into the server. <br />
Also, a good idea is to create a dedicated, non-root user for running the honeypot. <br />
Never run a honeypot with sudo privilege, in the case an attacker manages to break out of the honeypot, it will have sudo access to the server.</p>

<ul>
  <li>Ubuntu 24.04.1 or similar</li>
  <li>Docker installed (can be installed following these <a href="https://docs.docker.com/engine/install/ubuntu/">instructions</a>)</li>
  <li>A non-root user handling the docker container. Follow the steps <a href="https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user">here</a></li>
  <li>git</li>
  <li>ufw</li>
  <li>Optional, but recommended, running the docker in <a href="https://docs.docker.com/engine/security/rootless/">rootless mode</a></li>
</ul>

<h2 id="set-up-the-ssh-honeypot">Set up the SSH honeypot</h2>
<p>First, set a firewall rule to redirect SSH requests from port 22 to 2222 (a non-privileged port).</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nb">sudo </span>iptables <span class="nt">-t</span> nat <span class="nt">-A</span> PREROUTING <span class="nt">-p</span> tcp <span class="nt">--dport</span> 22 <span class="nt">-j</span> REDIRECT <span class="nt">--to-port</span> 2222

<span class="c"># And, if your firewall is enabled, allow connections the port 2222</span>
<span class="nb">sudo </span>ufw allow 2222/tcp
</code></pre></div></div>
<p>From now, you should not use the <code class="language-plaintext highlighter-rouge">sudo</code> command anymore. <br />
Clone the repository from above:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>git clone https://github.com/panacotar/basic_ssh_honeypot.git &amp;&amp; cd basic_ssh_honeypot
</code></pre></div></div>

<p>Create the RSA key pair:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>ssh-keygen <span class="nt">-t</span> rsa <span class="nt">-f</span> server.key 
<span class="c"># When asking for a password, just skip it (press enter)</span>

<span class="c"># Rename the public key</span>
<span class="nb">mv </span>server.key.pub server.pub
</code></pre></div></div>

<p>Build the Docker image (provided you added your user to the <code class="language-plaintext highlighter-rouge">docker</code> group as described in the prerequisites):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>docker build --no-cache -t basic_sshpot .
</code></pre></div></div>
<p>Then run it:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>docker run -d -v ${PWD}:/usr/src/app -p 2222:2222 basic_sshpot
</code></pre></div></div>
<p>Some parameters here:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">-d</code> (<code class="language-plaintext highlighter-rouge">--detach</code>) - runs the container in the background. It prints the new container’s ID and you’ll get the prompt back.</li>
  <li><code class="language-plaintext highlighter-rouge">-v</code> (<code class="language-plaintext highlighter-rouge">--volume</code>) - creates a bind mount, creating the <code class="language-plaintext highlighter-rouge">ssh_honeypot.log</code> file in the current directory.</li>
</ul>

<p>The honeypot now listens to incoming SSH connections and logs them to the log file (<code class="language-plaintext highlighter-rouge">ssh_honeypot.log</code>). <br />
Run <code class="language-plaintext highlighter-rouge">ss -tulpn</code> to check the open ports, you should see the honeypot running:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Netid         State          Recv-Q          Send-Q                   Local Address:Port                    Peer Address:Port         Process
[...]
tcp           LISTEN         0               4096                              [::]:2222                            [::]:*  
</code></pre></div></div>

<p>After running the honeypot for a while, you will find its logs in the current directory, <code class="language-plaintext highlighter-rouge">ssh_honeypot.log</code>. You can also view them live with the command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>tail -f ssh_honeypot.log
</code></pre></div></div>

<h3 id="stopping-the-dockerized-honeypot">Stopping the dockerized honeypot</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>docker stop $(docker ps -a -q  --filter ancestor=basic_sshpot)
</code></pre></div></div>

<h2 id="other-honeypots">Other honeypots</h2>
<ul>
  <li><a href="https://github.com/cowrie/cowrie">Cowrie</a> - a great alternative that is very simple to set up and use. They also provide helpful documentation.</li>
  <li><a href="https://github.com/thinkst/opencanary">OpenCanary</a> - modular and decentralized honeypot daemon that runs several canary versions of services that alert when a service is (ab)used.</li>
  <li><a href="https://github.com/telekom-security/tpotce">T-Pot</a> - all-in-one honeypot appliance (can be resource intensive)</li>
  <li><a href="https://github.com/droberson/ssh-honeypot">ssh_honeypot</a> - a light alternative, it logs the IP address, username, and password</li>
</ul>]]></content><author><name>Darius Pirvulescu</name></author><category term="sec" /><category term="vps" /><category term="honeypot" /><summary type="html"><![CDATA[After deploying my VPS and taking steps to secure it, I had the original SSH port (22) inactive. But it kept me curious about the default SSH activity going on there. How much brute forcing is happening on a publicly exposed server? I started experimenting with honeypots to find out more.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Benchmark your ruby code</title><link href="https://organicdarius.com/blog/benchmark-your-ruby-code/" rel="alternate" type="text/html" title="Benchmark your ruby code" /><published>2024-10-11T00:00:00+00:00</published><updated>2024-10-11T00:00:00+00:00</updated><id>https://organicdarius.com/blog/benchmark-your-ruby-code</id><content type="html" xml:base="https://organicdarius.com/blog/benchmark-your-ruby-code/"><![CDATA[<p>Ruby offers an easy way to benchmark the code. Here is some syntax for basic benchmarking.</p>

<p>This is done with the <a href="https://github.com/ruby/benchmark">Benchmark</a> module included in the Ruby standard library. You can run it even in IRB, simply <code class="language-plaintext highlighter-rouge">require "benchmark"</code>. <br />
In its simplest form, <code class="language-plaintext highlighter-rouge">Benchmark.measure</code> accepts a code block and outputs the time it takes to execute it.</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nb">require</span> <span class="s2">"benchmark"</span>

<span class="nb">puts</span> <span class="no">Benchmark</span><span class="p">.</span><span class="nf">measure</span> <span class="p">{</span> <span class="nb">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>
<p>Returning:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>  0.000061   0.000031   0.000092 (  1.001175)
</code></pre></div></div>
<p>The meaning of these stats (measured unit is second):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>user CPU time   system CPU time   sum user + system CPU   times elapsed real time
  0.000061        0.000031          0.000092                (  1.001175)
</code></pre></div></div>

<p>A more advanced form is using <code class="language-plaintext highlighter-rouge">Benchmark.bm</code>, this allows us to compare the execution of different code blocks:</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nb">require</span> <span class="s2">"benchmark"</span>

<span class="n">arr</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="nb">rand</span> <span class="p">}</span>
<span class="no">Benchmark</span><span class="p">.</span><span class="nf">bm</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="c1"># Each x.report is a different test item to compare against</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span> <span class="p">{</span> <span class="n">arr</span><span class="p">.</span><span class="nf">dup</span><span class="p">.</span><span class="nf">sort</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span> <span class="p">{</span> <span class="n">arr</span><span class="p">.</span><span class="nf">dup</span><span class="p">.</span><span class="nf">sort!</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>   user     system      total        real
0.021121   0.001285   0.022406 (  0.022459)
0.018150   0.003547   0.021697 (  0.021704)
</code></pre></div></div>

<p>We can also label the reports <code class="language-plaintext highlighter-rouge">x.report("sort")</code>. <br />
Also, we can provide predefined methods in order to compare them. <br />
Using <code class="language-plaintext highlighter-rouge">Benchmark.bmbm</code> will run the tests twice for a (supposedly) better reading.</p>

<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nb">require</span> <span class="s2">"benchmark"</span>

<span class="n">arr</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000_000</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="nb">rand</span> <span class="p">}</span>
<span class="k">def</span> <span class="nf">first_method</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span>
  <span class="n">arr</span><span class="p">.</span><span class="nf">last</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">second_method</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span>
  <span class="n">arr</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">end</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"first_method"</span><span class="p">)</span> <span class="p">{</span> <span class="mi">100_000</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span><span class="p">;</span> <span class="n">first_method</span><span class="p">(</span><span class="n">arr</span><span class="p">);</span> <span class="k">end</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"second_method"</span><span class="p">)</span> <span class="p">{</span> <span class="mi">100_000</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span><span class="p">;</span> <span class="n">second_method</span><span class="p">(</span><span class="n">arr</span><span class="p">);</span> <span class="k">end</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>Rehearsal -------------------------------------------------
first_method    0.004724   0.000000   0.004724 (  0.004793)
second_method   0.004317   0.000000   0.004317 (  0.004381)
---------------------------------------- total: 0.009041sec

                    user     system      total        real
first_method    0.005145   0.000000   0.005145 (  0.005263)
second_method   0.004220   0.000000   0.004220 (  0.004278)
</code></pre></div></div>

<h2 id="benchmark-ips">Benchmark-ips</h2>
<p>Another performance gem built on the Benchmark from above. <a href="https://github.com/evanphx/benchmark-ips">Benchmark-ips</a> measure how many times a code block will run in a second (iterations per second - IPS) rather than measuring the time it takes for a code block to run.</p>

<p>You have to install the gem: <code class="language-plaintext highlighter-rouge">gem install benchmark-ips</code>. The syntax is:</p>
<div class="language-rb highlighter-rouge"><div class="highlight"><pre class=""><code><span class="nb">require</span> <span class="s2">"benchmark/ips"</span>

<span class="n">arr</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000_000</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="nb">rand</span> <span class="p">}</span>
<span class="k">def</span> <span class="nf">first_method</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span>
  <span class="n">arr</span><span class="p">.</span><span class="nf">last</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">second_method</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span>
  <span class="n">arr</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">end</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">ips</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"first method"</span><span class="p">)</span> <span class="p">{</span> <span class="n">first_method</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"second method"</span><span class="p">)</span> <span class="p">{</span> <span class="n">second_method</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span> <span class="p">}</span>

  <span class="n">x</span><span class="p">.</span><span class="nf">compare!</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
Warming up --------------------------------------
        first method     1.685M i/100ms
       second method     1.921M i/100ms
Calculating -------------------------------------
        first method     16.390M (± 3.2%) i/s   (61.01 ns/i) -     82.542M in   5.042099s
       second method     18.257M (± 1.1%) i/s   (54.77 ns/i) -     92.216M in   5.051680s

Comparison:
       second method: 18256810.3 i/s
        first method: 16389785.2 i/s - 1.11x  slower
</code></pre></div></div>

<p>Recommended read: <a href="https://shopify.engineering/how-fix-slow-code-ruby">https://shopify.engineering/how-fix-slow-code-ruby</a>.</p>]]></content><author><name>Darius Pirvulescu</name></author><category term="ruby-rails" /><summary type="html"><![CDATA[Ruby offers an easy way to benchmark the code. Here is some syntax for basic benchmarking.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fix the N+1 queries in Rails</title><link href="https://organicdarius.com/blog/fix-the-n-1-query-in-rails/" rel="alternate" type="text/html" title="Fix the N+1 queries in Rails" /><published>2024-09-02T00:00:00+00:00</published><updated>2024-09-02T00:00:00+00:00</updated><id>https://organicdarius.com/blog/fix-the-n-1-query-in-rails</id><content type="html" xml:base="https://organicdarius.com/blog/fix-the-n-1-query-in-rails/"><![CDATA[<p>The N+1 query problem is a common performance issue encountered in Rails applications.</p>

<p>There are tools to detect this problem automatically. And Active Record provides ways to fix it.</p>

<h2 id="the-problem">The problem</h2>
<p>This is related to having associations and the way we load the respective records. Active Record simplifies database interaction, but it can lead to issues like N+1 query. By default, Active Record uses a lazy loading approach, meaning it only loads records when they are accessed.</p>

<p>N+1 query issue occur when the application queries the database, loops over the results, and <strong>executes a separate query for each associated record</strong> in the list.</p>

<p>An example association: <code class="language-plaintext highlighter-rouge">Users</code> having many <code class="language-plaintext highlighter-rouge">Dogs</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class=""><code>class User &lt; ApplicationRecord
  has_many :dogs
end

class Dog &lt; ApplicationRecord
  belongs_to :user
end
</code></pre></div></div>

<p>It is common in the Rails app to load all records and then loop over them, accessing their associated model (for instance, wanting to display the records in an index view).</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c"># rails c</span>
User.all.each <span class="o">{</span> |u| puts u.dogs <span class="o">}</span><span class="p">;</span>nil
</code></pre></div></div>
<p>Here, we list the user’s dogs. While the code works correctly, it triggers too many database queries. Specifically, it prompts Active Record to execute one query to fetch the users and additional queries for each user in the database (a total of <strong>1+N</strong> queries):</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>User Load <span class="o">(</span>0.1ms<span class="o">)</span>  SELECT <span class="s2">"users"</span>.<span class="k">*</span> FROM <span class="s2">"users"</span>
Dog Load <span class="o">(</span>0.1ms<span class="o">)</span>  SELECT <span class="s2">"dogs"</span>.<span class="k">*</span> FROM <span class="s2">"dogs"</span> WHERE <span class="s2">"dogs"</span>.<span class="s2">"user_id"</span> <span class="o">=</span> ?  <span class="o">[[</span><span class="s2">"user_id"</span>, 1]]
Dog Load <span class="o">(</span>0.1ms<span class="o">)</span>  SELECT <span class="s2">"dogs"</span>.<span class="k">*</span> FROM <span class="s2">"dogs"</span> WHERE <span class="s2">"dogs"</span>.<span class="s2">"user_id"</span> <span class="o">=</span> ?  <span class="o">[[</span><span class="s2">"user_id"</span>, 2]]
<span class="o">[</span>...]
Dog Load <span class="o">(</span>0.0ms<span class="o">)</span>  SELECT <span class="s2">"dogs"</span>.<span class="k">*</span> FROM <span class="s2">"dogs"</span> WHERE <span class="s2">"dogs"</span>.<span class="s2">"user_id"</span> <span class="o">=</span> ?  <span class="o">[[</span><span class="s2">"user_id"</span>, 300]]
</code></pre></div></div>

<p>This can slow down the app and result in a high database load, especially in apps with large datasets. 
For an app with 100.000 associated records, there will be 1 + 100.000 queries.</p>

<h2 id="the-active-record-solution">The Active Record solution</h2>
<p>One solution provided by Active Record is to <strong>eager load</strong> the associated records upfront. This is achieved with the <a href="https://apidock.com/rails/ActiveRecord/QueryMethods/includes">#includes</a> query method, allowing the app to load users and all their dogs in two queries. It avoids the N+1 query problem.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c"># rails c</span>
User.includes<span class="o">(</span>:dogs<span class="o">)</span>.all.each <span class="o">{</span> |u| puts u.dogs <span class="o">}</span><span class="p">;</span>nil
</code></pre></div></div>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>User Load <span class="o">(</span>0.1ms<span class="o">)</span>  SELECT <span class="s2">"users"</span>.<span class="k">*</span> FROM <span class="s2">"users"</span>
Dog Load <span class="o">(</span>0.4ms<span class="o">)</span>  SELECT <span class="s2">"dogs"</span>.<span class="k">*</span> FROM <span class="s2">"dogs"</span> WHERE <span class="s2">"dogs"</span>.<span class="s2">"user_id"</span> IN <span class="o">(</span>?, ?, ?, ?, ?, ?, ?, ?, ?, ?<span class="o">)</span>  <span class="o">[[</span><span class="s2">"user_id"</span>, 1], <span class="o">[</span><span class="s2">"user_id"</span>, 2], <span class="o">[</span><span class="s2">"user_id"</span>, 3], <span class="o">[</span><span class="s2">"user_id"</span>, 4], <span class="o">[</span><span class="s2">"user_id"</span>, 5], <span class="o">[</span><span class="s2">"user_id"</span>, 6], <span class="o">[</span><span class="s2">"user_id"</span>, 7], <span class="o">[</span><span class="s2">"user_id"</span>, 8], <span class="o">[</span><span class="s2">"user_id"</span>, 9], <span class="o">[</span><span class="s2">"user_id"</span>, 10]]
</code></pre></div></div>

<h3 id="the-case-of-nested-associations">The case of nested associations</h3>
<p>What happens if we need to access data from a nested association? <br />
Let’s say each dog <code class="language-plaintext highlighter-rouge">has_many</code> toys, and we want to print the number of toys for each dog. <br />
If we simply add a call <code class="language-plaintext highlighter-rouge">dog.toys.size</code>, the records for dogs and users will be eager loaded, while the toys will still be lazy loaded.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c"># rails c</span>
User.includes<span class="o">(</span>:dogs<span class="o">)</span>.all.each <span class="o">{</span> |u| u.dogs.each <span class="o">{</span> |d| puts d.toys.size <span class="o">}</span> <span class="o">}</span><span class="p">;</span>nil
</code></pre></div></div>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>User Load <span class="o">(</span>0.1ms<span class="o">)</span>  <span class="o">[</span>...]
Dog Load <span class="o">(</span>0.4ms<span class="o">)</span>  <span class="o">[</span>...]
Toy Count <span class="o">(</span>0.1ms<span class="o">)</span>  SELECT COUNT<span class="o">(</span><span class="k">*</span><span class="o">)</span> FROM <span class="s2">"toys"</span> WHERE <span class="s2">"toys"</span>.<span class="s2">"dog_id"</span> <span class="o">=</span> ?  <span class="o">[[</span><span class="s2">"dog_id"</span>, 1]]
Toy Count <span class="o">(</span>0.1ms<span class="o">)</span>  <span class="o">[</span>...] 
Toy Count <span class="o">(</span>0.0ms<span class="o">)</span>  <span class="o">[</span>...]
<span class="o">[</span>...]
Toy Count <span class="o">(</span>0.0ms<span class="o">)</span>  <span class="o">[</span>...]
</code></pre></div></div>
<p>The syntax for including the association is <code class="language-plaintext highlighter-rouge">User.includes(dogs: :toys)</code> or <code class="language-plaintext highlighter-rouge">User.includes(dogs: [:toys])</code>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code><span class="c"># rails c</span>
User.includes<span class="o">(</span>dogs: :toys<span class="o">)</span>.all.each <span class="o">{</span> |u| u.dogs.each <span class="o">{</span> |d| puts d.toys.size <span class="o">}</span> <span class="o">}</span><span class="p">;</span>nil
</code></pre></div></div>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class=""><code>User Load <span class="o">(</span>0.2ms<span class="o">)</span>  <span class="o">[</span>...]
Dog Load <span class="o">(</span>0.4ms<span class="o">)</span>   <span class="o">[</span>...]
Toy Load <span class="o">(</span>0.5ms<span class="o">)</span>   <span class="o">[</span>...]
</code></pre></div></div>

<h2 id="useful-tools">Useful tools</h2>

<p>The <a href="https://github.com/flyerhzm/bullet">Bullet</a> gem can be implemented in your app. It automatically checks your app and notifies you when it detects N+1 queries. Moreover, it also notifies when you’re using eager loading that isn’t necessary and when you should use counter cache. Make sure to add it under the development gems. <br />
Once Bullet detects an N+1 query issue, it will trigger a warning:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class=""><code>user: john
GET /
USE eager loading detected
  User <span class="o">=&gt;</span> <span class="o">[</span>:dogs]
  Add to your query: .includes<span class="o">([</span>:dogs]<span class="o">)</span>
Call stack
<span class="o">[</span>...]
</code></pre></div></div>

<!-- # When N+1 is this not a problem? -->

<h2 id="resources">Resources</h2>
<ul>
  <li>ruby.mobidev.biz</li>
  <li>www.visuality.pl</li>
</ul>]]></content><author><name>Darius Pirvulescu</name></author><category term="ruby-rails" /><summary type="html"><![CDATA[The N+1 query problem is a common performance issue encountered in Rails applications.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://organicdarius.com/assets/images/og_me.jpg" /><media:content medium="image" url="https://organicdarius.com/assets/images/og_me.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>