<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aditya Sharma</title>
    <description>The latest articles on DEV Community by Aditya Sharma (@aditya_d_sharma).</description>
    <link>https://dev.to/aditya_d_sharma</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4051470%2Fe252149c-c45f-483e-935c-5356bb60e731.png</url>
      <title>DEV Community: Aditya Sharma</title>
      <link>https://dev.to/aditya_d_sharma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aditya_d_sharma"/>
    <language>en</language>
    <item>
      <title>What Is a Man-in-the-Middle Attack? How Does TLS Stop It?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Mon, 07 Sep 2026 07:32:47 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-is-a-man-in-the-middle-attack-how-does-tls-stop-it-hkk</link>
      <guid>https://dev.to/aditya_d_sharma/what-is-a-man-in-the-middle-attack-how-does-tls-stop-it-hkk</guid>
      <description>&lt;p&gt;When your browser connects to a website, data travels through networks you don't control. Routers, ISPs, cables, wireless access points. Any of these could, in principle, be operated by someone who can see or modify your traffic.&lt;/p&gt;

&lt;p&gt;The obvious response is encryption. Encrypt the data before sending it, and an observer sees only ciphertext. But encryption alone doesn't solve the harder problem.&lt;/p&gt;

&lt;p&gt;Before Alice can encrypt anything for Bob, she needs Bob's public key. And if someone is sitting between Alice and Bob, they can intercept that key exchange.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice → "Give me your public key"
              ↓
        Attacker intercepts
              ↓
        Sends attacker's key instead
              ↓
Alice encrypts to the attacker's key,
believing she's communicating with Bob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alice now has an "encrypted" connection. But she's encrypting to the wrong person. The attacker decrypts her messages, reads or modifies them, re-encrypts to Bob's actual key, and forwards them. Both sides think they have a private connection. Neither does.&lt;/p&gt;

&lt;p&gt;This is the core problem. Encryption protects communication only if you know whose key you're encrypting to. The interesting question isn't whether traffic can be encrypted. It's how Alice can verify that the key she received actually belongs to Bob.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Public Key Doesn't Come with Identity
&lt;/h2&gt;

&lt;p&gt;Public-key cryptography gives us a useful primitive: a key pair where one key can sign and the other can verify. As covered in the digital signatures article, a signature over some data proves that the signer possessed the corresponding private key at signing time.&lt;/p&gt;

&lt;p&gt;But a public key by itself says nothing about who created it. Anyone can generate a key pair. The fact that you have a key labeled "belongs to example.com" doesn't make it true. What's needed is a trustworthy mechanism for binding a public key to an identity.&lt;/p&gt;

&lt;p&gt;This is what a digital certificate does.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a Certificate Is
&lt;/h2&gt;

&lt;p&gt;A TLS certificate is a signed document. It contains a domain name, the server's public key, validity information, and the issuer. Critically, it includes a digital signature from a Certificate Authority that vouches for the binding between that domain and that public key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Domain identity
       +
Server public key
       ↓
Certificate
       ↓
Signed by Certificate Authority
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CA's signature is what allows a client to verify that a trusted authority has attested to the binding between that domain identity and public key. Because the client can verify the CA's signature using the CA's public key, which it already has, a valid signature over the certificate means a trusted authority stands behind the claim that this public key belongs to this domain. Operating systems and browsers ship with a trust store: a set of root CA certificates that are considered trustworthy by default. If a certificate chains up to one of those roots, and the signature is valid, the client can trust that the domain-to-key binding has been attested by a trusted authority.&lt;/p&gt;

&lt;p&gt;If an attacker creates a certificate for a target domain without the corresponding private key, or gets a certificate signed by a CA that clients don't trust, the verification fails. The browser flags the connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attacker's certificate
        ↓
Not signed by a trusted CA for that domain
        ↓
Verification fails
        ↓
Browser warns / connection rejected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The TLS Handshake
&lt;/h2&gt;

&lt;p&gt;Knowing the theory is one thing. Seeing how it plays out during a connection is another. Here's a simplified TLS 1.3 handshake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Client                              Server

ClientHello  ──────────────────────→
             ←────────────────────  ServerHello
             ←────────────────────  Certificate
             ←────────────────────  CertificateVerify
             ←────────────────────  Finished
Finished     ──────────────────────→

         Encrypted application data
         ←─────────────────────────→
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ClientHello:&lt;/strong&gt; The client sends its supported protocol versions, cipher suites, and key-share information for the key exchange.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ServerHello:&lt;/strong&gt; The server selects the protocol version and cipher suite, and responds with its own key-share contribution. At this point, both sides have enough information to derive shared secrets. The key exchange happens through a scheme like ECDHE, where both sides contribute values that allow them to compute a shared secret independently without ever transmitting the secret itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Certificate:&lt;/strong&gt; The server sends its certificate chain so the client can authenticate the server's identity. The client validates the chain against its trust store, checks the certificate's validity period, and verifies that the domain name in the certificate matches the server being connected to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CertificateVerify:&lt;/strong&gt; This is the step most explanations skip, and it's important. The certificate proves that some trusted authority bound a public key to a domain. But it doesn't prove that the server currently possesses the corresponding private key. An attacker who obtained a copy of someone's certificate but not their private key could present that certificate.&lt;/p&gt;

&lt;p&gt;CertificateVerify closes this gap. The server signs the handshake transcript so far using its private key. The client verifies this signature against the public key in the certificate. A valid signature proves that whoever is running this server possesses the private key, not just the certificate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finished:&lt;/strong&gt; Both sides send a Finished message that covers the entire handshake transcript. This verifies that neither side's view of the handshake has been tampered with, and that both have derived the same session keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Symmetric Encryption Handles the Data
&lt;/h2&gt;

&lt;p&gt;Public-key operations are computationally expensive. Encrypting every byte of application data with asymmetric cryptography would be impractically slow for most connections.&lt;/p&gt;

&lt;p&gt;The handshake solves this elegantly. The key exchange produces shared secret material from which the connection derives symmetric session keys. Those keys are used for all subsequent application data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Handshake key exchange
        ↓
Shared secret
        ↓
Symmetric session keys
        ↓
Fast symmetric encryption
        ↓
Application data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Public-key cryptography does the authentication and key agreement. Symmetric cryptography does the heavy lifting on the actual data. This separation is a deliberate design choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the MITM Now Fails
&lt;/h2&gt;

&lt;p&gt;Return to the original attack. An attacker sitting between Alice and the server wants to substitute their own key.&lt;/p&gt;

&lt;p&gt;Without TLS, they can. Alice receives the attacker's key, encrypts to it, and the attacker decrypts the traffic.&lt;/p&gt;

&lt;p&gt;With TLS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice
  ↓
Receives certificate
  ↓
Verifies chain against trust store
  ↓
Confirms domain matches
  ↓
Verifies CertificateVerify signature
  ↓
Proves server holds the private key
  ↓
Session established
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the attacker to impersonate the server, they'd need a certificate for the target domain that chains to a trusted CA, and the private key corresponding to the public key in that certificate. A fraudulent certificate from a CA that clients don't trust fails at the verification step. A valid certificate without the matching private key fails at CertificateVerify.&lt;/p&gt;

&lt;p&gt;The MITM position doesn't help anymore. Observing the traffic reveals ciphertext encrypted under session keys the attacker doesn't have. Injecting their own certificate fails certificate verification. The attacker is stuck.&lt;/p&gt;




&lt;h2&gt;
  
  
  What TLS Doesn't Cover
&lt;/h2&gt;

&lt;p&gt;TLS protects data in transit between authenticated endpoints. It doesn't protect against a compromised server, malicious code already running on the client, stolen private keys, or data after it's been decrypted at the destination. A padlock in the browser means the connection to that server is authenticated and encrypted. It says nothing about what the server does with your data once it arrives.&lt;/p&gt;




&lt;p&gt;TLS doesn't solve the MITM problem by adding encryption. It solves a harder problem first: establishing who you're actually communicating with before that encryption can be trusted.&lt;/p&gt;

&lt;p&gt;Certificates bind identities to public keys. Certificate Authorities create a chain of trust the client can verify. The TLS handshake authenticates the server, proves private-key possession, and negotiates shared secrets through key exchange. Symmetric encryption protects the connection efficiently from there.&lt;/p&gt;

&lt;p&gt;That's what happens in the moment between typing a URL and the page loading. It's fast enough to feel instantaneous and careful enough that an attacker sitting on the network between you and the server can't impersonate either side.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>networking</category>
      <category>security</category>
    </item>
    <item>
      <title>What Is IDOR? How Can Changing an ID Expose Someone Else's Data?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Sun, 06 Sep 2026 10:58:11 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-is-idor-how-can-changing-an-id-expose-someone-elses-data-216c</link>
      <guid>https://dev.to/aditya_d_sharma/what-is-idor-how-can-changing-an-id-expose-someone-elses-data-216c</guid>
      <description>&lt;p&gt;You're logged into an e-commerce site. You just placed an order, and the confirmation page shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/orders/1042
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server returns your order details. Everything looks normal.&lt;/p&gt;

&lt;p&gt;Now consider what happens if you change that number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/orders/1043
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the server returns that order too, without checking whether you're actually allowed to see it, that's the vulnerability. Not the fact that you changed the number. The fact that the server didn't notice you shouldn't have access to what came back.&lt;/p&gt;

&lt;p&gt;This is Insecure Direct Object Reference, commonly called IDOR, or under the more precise modern framing, Broken Object Level Authorization (BOLA). The names describe the same underlying problem: an application that authenticates who you are but fails to verify whether you're authorized to access the specific object you're asking for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication Is Not Authorization
&lt;/h2&gt;

&lt;p&gt;These two concepts get conflated constantly, and that conflation is where IDOR lives.&lt;/p&gt;

&lt;p&gt;Authentication answers one question: who is this user? When you log in, the application establishes your identity. Your session token or cookie says "this request is from Alice."&lt;/p&gt;

&lt;p&gt;Authorization answers a different question: what is Alice allowed to do? More specifically for IDOR: is Alice allowed to perform this operation on this particular object?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice
  ↓
Authenticated ✓
  ↓
Can access:
  Order 1042 ✓  (Alice's order)
  Order 1043 ✗  (Bob's order)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An application that correctly performs authentication but skips or misses authorization at the object level will happily hand Alice Bob's data, because all it checked was whether Alice was logged in.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "Direct Object Reference" Actually Means
&lt;/h2&gt;

&lt;p&gt;An object reference is just an identifier that lets the application locate something: a database row, a file, a resource. The identifier itself isn't the problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /users/42
GET /orders/1042
GET /invoices/781
GET /documents/a3f9...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are all direct references. They're efficient and normal. The security question is what the server does when it receives one.&lt;/p&gt;

&lt;p&gt;A vulnerable application treats possession of the reference as sufficient permission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/orders/&amp;lt;order_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application fetches whatever order matches the ID and returns it. It never asks whether the currently authenticated user is allowed to see that order. If you can construct or guess a valid ID, you can retrieve the corresponding object.&lt;/p&gt;

&lt;p&gt;The fix is conceptually simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/orders/&amp;lt;order_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;forbidden&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the server checks that the object belongs to the requesting user before returning it. Unauthorized requests get a 403, not someone else's data.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bug Often Lives in the Query
&lt;/h2&gt;

&lt;p&gt;One level deeper: authorization bugs frequently appear not in request handlers but in the data-access layer. The database query itself may be the right place to enforce scope.&lt;/p&gt;

&lt;p&gt;Compare these two queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;versus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first retrieves any order matching the ID. The second retrieves an order only if it belongs to the current user. An unauthorized ID simply returns no rows, which the application can treat as a not-found or forbidden response.&lt;/p&gt;

&lt;p&gt;Scoping queries to the current user's context makes authorization a property of data retrieval rather than a separate check that can be forgotten. It also means that if an authorization check is accidentally skipped somewhere in the application, the query itself doesn't return objects the user isn't allowed to see.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unpredictability Is Not Authorization
&lt;/h2&gt;

&lt;p&gt;A common response to IDOR vulnerabilities is to make IDs harder to guess. Instead of sequential integers, use UUIDs or opaque tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/orders/7f3a92b4-e1d8-4c2a-bf19-3a8d1c7e05f6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes brute-force enumeration harder. It doesn't fix the underlying problem.&lt;/p&gt;

&lt;p&gt;If the server still returns an order to any authenticated user who knows its ID, the authorization check is still missing. An attacker who obtains a UUID through another means, from a shared link, an API response, a log entry, simply uses it. Unpredictability raises the bar for unauthenticated access. It is not a substitute for checking whether the authenticated user is allowed to access a specific object.&lt;/p&gt;

&lt;p&gt;This matters because it shifts developer thinking away from the real fix. The question isn't "how hard is the ID to guess?" It's "does the server verify that this user is allowed to access this object?"&lt;/p&gt;




&lt;h2&gt;
  
  
  Read Operations Are Not the Only Risk
&lt;/h2&gt;

&lt;p&gt;IDOR isn't limited to GET requests. Object-level authorization applies across every operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET    /api/orders/1042   → view
PUT    /api/orders/1042   → modify
DELETE /api/orders/1042   → delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An application might correctly protect reads while leaving writes unguarded, or correctly protect deletion while leaving modification open. Each operation needs its own authorization check against the specific object being acted on. The underlying question is always the same: is this user allowed to perform this particular action on this particular object?&lt;/p&gt;




&lt;h2&gt;
  
  
  Client-Side Controls Don't Count
&lt;/h2&gt;

&lt;p&gt;A frontend application might hide an edit button for objects the user doesn't own, or not display other users' orders in the UI. This provides no security.&lt;/p&gt;

&lt;p&gt;An attacker communicates directly with the API, not through the browser's rendered UI. The fact that your application doesn't show a delete button to unauthorized users has no bearing on what happens when an unauthorized user sends a DELETE request directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser UI (hides unauthorized controls)
         ↓
Direct API request (bypasses the UI entirely)
         ↓
Authentication check
         ↓
Object lookup
         ↓
Authorization check ← this must exist on the server
         ↓
Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The authorization check belongs in the server. Everything the frontend does is presentation. None of it enforces security.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building Authorization That Holds
&lt;/h2&gt;

&lt;p&gt;The defensive pattern follows from the mechanism. For every request that accesses a specific object, the server needs to establish three things: who is making the request, what object is being accessed, and whether that user is permitted to perform the requested operation on that object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authenticated user
        +
Requested object
        +
Requested action
        ↓
Authorization decision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One practical approach is to scope data access to the current user's context at the query level, as shown earlier. Another is to centralize authorization logic rather than scattering ad-hoc permission checks across individual endpoints, where they're easy to miss or apply inconsistently.&lt;/p&gt;

&lt;p&gt;Testing also matters. Authorization bugs are invisible to tests that only check "authenticated users can access this endpoint." Tests need to verify that User A cannot access User B's objects, cannot modify User B's objects, and cannot delete them. The test cases that catch IDOR are specifically the cross-user ones.&lt;/p&gt;




&lt;p&gt;Changing &lt;code&gt;/orders/1042&lt;/code&gt; to &lt;code&gt;/orders/1043&lt;/code&gt; isn't the vulnerability. If the server enforces authorization correctly, that request returns a 403. The vulnerability is what the server does when it retrieves an object without verifying the requesting user's permission to access it.&lt;/p&gt;

&lt;p&gt;Authentication tells the application who you are. That's a necessary first step. Authorization tells the application what you're allowed to do. Object-level authorization goes one step further: it asks whether you're allowed to do it to this specific object.&lt;/p&gt;

&lt;p&gt;IDOR happens in the gap between the first check and the third. Closing that gap is the entire fix.&lt;/p&gt;

</description>
      <category>api</category>
      <category>cybersecurity</category>
      <category>infosec</category>
      <category>security</category>
    </item>
    <item>
      <title>How Can Google Maps Find the Shortest Route So Fast?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Sun, 06 Sep 2026 07:17:45 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/how-can-google-maps-find-the-shortest-route-so-fast-22fb</link>
      <guid>https://dev.to/aditya_d_sharma/how-can-google-maps-find-the-shortest-route-so-fast-22fb</guid>
      <description>&lt;p&gt;You type two addresses into a navigation app. Within a fraction of a second, it returns a route. Not an approximate one. The shortest one, accounting for real road constraints, distances, and travel times.&lt;/p&gt;

&lt;p&gt;The road network it searched could span an entire continent. The graph underlying Europe's road network alone has hundreds of millions of nodes and edges. So how does the system search it so quickly?&lt;/p&gt;

&lt;p&gt;The answer isn't one clever algorithm. It's a progression of improvements, each one shifting more work from query time to preprocessing, until the actual online search becomes almost trivially small.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Graph Underneath
&lt;/h2&gt;

&lt;p&gt;A road network maps naturally onto a weighted directed graph. Intersections become nodes. Roads become edges. Travel time or distance becomes the edge weight. Finding the fastest route between two places becomes finding the minimum-cost path between two nodes.&lt;/p&gt;

&lt;p&gt;The challenge is that this graph is enormous, and the number of possible paths through it grows fast. Brute force, trying every possible route to find the best one, is completely impractical. The problem isn't finding a route. It's finding the optimal route without exploring most of the graph.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dijkstra's Algorithm
&lt;/h2&gt;

&lt;p&gt;The first serious solution is Dijkstra's algorithm. It works with non-negative edge weights, which road networks satisfy, and it's guaranteed to find the optimal path.&lt;/p&gt;

&lt;p&gt;The mechanics are straightforward. Dijkstra maintains the best-known distance to every node, initially infinity for all except the source, which starts at zero. At each step, it selects the unvisited node with the lowest current distance, then relaxes its outgoing edges: for each neighbor, if the path through the current node is cheaper than what we've recorded, we update the neighbor's distance. A priority queue makes selecting the cheapest unvisited node efficient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source: A, Destination: F

Initial distances: A=0, B=∞, C=∞, D=∞, E=∞, F=∞

Step 1: Visit A, relax neighbors → B=4, C=2
Step 2: Visit C (cheapest), relax neighbors → D=5, E=8
Step 3: Visit B, relax neighbors → D=4 (updated)
...until F is settled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that Dijkstra expands outward from the source in all directions simultaneously. To find the route from London to Edinburgh, it might settle nodes across most of England before reaching Edinburgh. For queries spanning a large area, the search region grows enormous.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0pj7ha1hmrdfp1sfdn7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0pj7ha1hmrdfp1sfdn7.png" alt="Dijkstra's Algorithm" width="800" height="904"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A* Guides the Search
&lt;/h2&gt;

&lt;p&gt;A* improves on Dijkstra by adding a heuristic that estimates how promising each node is relative to the destination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;f(n) = g(n) + h(n)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where &lt;code&gt;g(n)&lt;/code&gt; is the cost from the source to node &lt;code&gt;n&lt;/code&gt;, and &lt;code&gt;h(n)&lt;/code&gt; is an estimate of the remaining cost from &lt;code&gt;n&lt;/code&gt; to the destination. A* prioritizes nodes with lower &lt;code&gt;f(n)&lt;/code&gt; values, guiding the search toward the destination rather than expanding uniformly.&lt;/p&gt;

&lt;p&gt;For road networks, straight-line geographic distance is an intuitive heuristic: a node geographically closer to the destination is plausibly a better candidate to explore next. For the heuristic to guarantee an optimal solution, it must be admissible: it must never overestimate the true remaining cost. Straight-line distance works because real roads are never shorter than straight lines.&lt;/p&gt;

&lt;p&gt;A* can dramatically reduce the number of nodes visited compared to Dijkstra. But it still operates on the entire road graph and can be slow for long-distance queries where the heuristic doesn't guide the search effectively.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bidirectional Search
&lt;/h2&gt;

&lt;p&gt;Another improvement is running two simultaneous searches: one forward from the source, one backward from the destination. Both searches expand inward, and they meet somewhere in the middle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source ──────────→ meeting region ←────────── Destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces the search region compared to a single-direction Dijkstra, though the savings depend on graph structure and the query. The technical details of when the bidirectional searches are terminated and how the optimal meeting point is identified require care to get right.&lt;/p&gt;

&lt;p&gt;Bidirectional search is useful, but it still operates on the unmodified graph. Which brings us to the more fundamental question:&lt;/p&gt;

&lt;p&gt;What if we could do most of the expensive work before the user ever asks for directions?&lt;/p&gt;




&lt;h2&gt;
  
  
  Contraction Hierarchies
&lt;/h2&gt;

&lt;p&gt;Contraction Hierarchies are built on a different insight. Instead of making each query search faster on the same graph, preprocess the graph so that future queries search a much smaller, smarter structure.&lt;/p&gt;

&lt;p&gt;The first step is assigning an importance ordering to nodes. Some intersections are genuinely more important for long-distance travel than others. A residential dead-end street is barely relevant. A major motorway junction might sit on the optimal path for millions of routes. The exact ranking uses metrics like edge difference: how many shortcut edges would need to be added if this node were removed.&lt;/p&gt;

&lt;p&gt;Once nodes are ranked, the algorithm contracts them in order from least to most important.&lt;/p&gt;

&lt;p&gt;When a node &lt;code&gt;B&lt;/code&gt; is contracted, it's temporarily removed from the graph. If the shortest path between any pair of &lt;code&gt;B&lt;/code&gt;'s neighbors went through &lt;code&gt;B&lt;/code&gt;, and no alternative path of equal or lesser cost exists, a shortcut edge is added directly connecting those neighbors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before contracting B:
A ──(3)──→ B ──(2)──→ C

After contracting B:
A ──────────(5)──────→ C   (shortcut)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crucially, before adding each shortcut, the algorithm runs a witness search: a limited Dijkstra that checks whether another path already connects those neighbors with the same or lower cost. If such a path exists, the shortcut isn't needed. This keeps the graph from bloating with redundant shortcuts.&lt;/p&gt;

&lt;p&gt;The important point: contracting a node doesn't delete information. It replaces multi-hop paths through unimportant nodes with direct shortcut edges, preserving all relevant shortest-path distances.&lt;/p&gt;

&lt;p&gt;After all contractions, the graph has an upward structure. Every edge points from a less important node to a more important one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Changes the Query
&lt;/h2&gt;

&lt;p&gt;Preprocessing transforms the problem. During a query:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A bidirectional search runs on the contracted graph.&lt;/li&gt;
&lt;li&gt;The forward search from the source follows only upward edges, moving toward increasingly important nodes.&lt;/li&gt;
&lt;li&gt;The backward search from the destination does the same.&lt;/li&gt;
&lt;li&gt;Both searches climb the hierarchy and meet near the top.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local streets
     ↓
Minor intersections
     ↓
Major junctions
     ↓
Regional highways
     ↓
Major motorways
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The forward and backward searches climb this hierarchy, meet near the top, and the optimal path is reconstructed by unpacking the shortcuts.&lt;/p&gt;

&lt;p&gt;The search doesn't have to explore local streets across the entire graph. It quickly ascends to important nodes and meets the backward search. Instead of settling millions of nodes, the query might settle thousands.&lt;/p&gt;

&lt;p&gt;This is the key insight: Contraction Hierarchies don't make Dijkstra magically faster. They change the graph that the query has to search.&lt;/p&gt;




&lt;h2&gt;
  
  
  Preprocessing vs Query Time
&lt;/h2&gt;

&lt;p&gt;The two costs are fundamentally different:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preprocessing (offline, done once):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rank nodes by importance&lt;/li&gt;
&lt;li&gt;Contract nodes in order&lt;/li&gt;
&lt;li&gt;Run witness searches&lt;/li&gt;
&lt;li&gt;Add shortcut edges&lt;/li&gt;
&lt;li&gt;Build the hierarchical graph structure
&lt;strong&gt;Query (online, runs millions of times):&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Receive source and destination&lt;/li&gt;
&lt;li&gt;Run bidirectional hierarchical search&lt;/li&gt;
&lt;li&gt;Unpack shortcuts to reconstruct the route
Preprocessing is expensive. It might take minutes for a continental road network. But it happens once. The millions of queries that follow operate on a structure designed to make them cheap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a general engineering principle that goes beyond routing: the fastest online computation is often the computation you moved offline.&lt;/p&gt;

&lt;p&gt;Some routing systems extend this further with Customizable Contraction Hierarchies (CCH), which separate the topology-dependent preprocessing from the weight-dependent parts. Road topology changes slowly, but edge weights change constantly due to traffic and road conditions. CCH allows the metric-sensitive parts of preprocessing to be updated quickly without rerunning the full contraction.&lt;/p&gt;

&lt;p&gt;Modern road-routing systems use techniques such as Contraction Hierarchies and related hierarchical routing methods. The exact approach used in production systems like Google Maps or Apple Maps isn't publicly documented in full detail, but hierarchical preprocessing is well-established in the academic literature and widely used in practice.&lt;/p&gt;




&lt;p&gt;The progression from brute force to Contraction Hierarchies isn't a series of marginal improvements. Each step changes what the algorithm is actually doing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brute force&lt;/strong&gt; → try everything&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Dijkstra&lt;/strong&gt; → search optimally but expands too far&lt;br&gt;&lt;br&gt;
&lt;strong&gt;A&lt;/strong&gt;* → guide the search toward the destination&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Bidirectional&lt;/strong&gt; → search from both ends&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Contraction Hierarchies&lt;/strong&gt; → preprocess the graph so queries barely have to search it  &lt;/p&gt;

&lt;p&gt;The impressive part isn't any single algorithm. It's the realization that you can invest heavily in offline work to make the online problem almost trivially small. A query that once required exploring millions of nodes can be answered by exploring thousands, because the expensive structural work already happened before you ever asked the question.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>performance</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>What Is a Digital Signature? How Can You Prove a Message Wasn't Changed?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Sat, 05 Sep 2026 07:44:13 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-is-a-digital-signature-how-can-you-prove-a-message-wasnt-changed-45fa</link>
      <guid>https://dev.to/aditya_d_sharma/what-is-a-digital-signature-how-can-you-prove-a-message-wasnt-changed-45fa</guid>
      <description>&lt;p&gt;You download a software package from the internet. The website tells you it's the official release from the expected publisher. But how do you actually know that? The file could have been modified in transit, or the server could have been compromised, or you could be downloading from a mirror you don't fully trust.&lt;/p&gt;

&lt;p&gt;A checksum helps with one part of this. If the publisher posts a SHA-256 hash of the file, you can download the file, compute its hash, and compare. If the hashes match, the file wasn't corrupted or modified after the hash was published.&lt;/p&gt;

&lt;p&gt;But there's a problem: the hash itself has the same authenticity problem as the file. If an attacker can replace the file, they can replace the published hash too. A hash tells you whether something changed. It doesn't tell you who computed it.&lt;/p&gt;

&lt;p&gt;Digital signatures solve the authenticity half of this problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Hashing Alone Isn't Enough
&lt;/h2&gt;

&lt;p&gt;A cryptographic hash function takes an arbitrary input and produces a fixed-size digest. Change a single byte in the input and the digest changes completely. That property is useful for detecting modification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
   ↓
SHA-256
   ↓
256-bit digest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But anyone who has the message can compute its hash. There's nothing in the hash itself that proves who created it or that it came from a specific source. For that, you need something only one party can produce: a private key.&lt;/p&gt;




&lt;h2&gt;
  
  
  Signing a Message
&lt;/h2&gt;

&lt;p&gt;The signing process goes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
   ↓
Hash function
   ↓
Message digest
   ↓
Private key + signature algorithm
   ↓
Digital signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signer computes a cryptographic hash of the message first. That digest is a compact, fixed-size representation of the message content. Then a signature algorithm uses the signer's private key to produce a signature over that digest.&lt;/p&gt;

&lt;p&gt;The reason for hashing before signing is practical: messages can be arbitrarily large, but the digest has a fixed size regardless of input length. The signature algorithm operates on the digest rather than the original message. This is efficient, and because a secure hash function makes it computationally infeasible to find two inputs with the same digest, signing the digest is equivalent to signing the message.&lt;/p&gt;

&lt;p&gt;One thing to be precise about: producing a digital signature is not the same as encrypting the message with a private key. That description is a common oversimplification and it's wrong in a way that matters. Digital signatures use signature algorithms designed specifically for this purpose. The output is a signature, not ciphertext, and the process is conceptually different from encryption.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Verification Works
&lt;/h2&gt;

&lt;p&gt;A verifier has three things: the message, the digital signature, and the signer's public key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Received message ──→ Hash ──────────────────────┐
                                                 ▼
Digital signature ──→ Public key ──→ Verification result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verifier hashes the received message to produce a digest. The signature verification algorithm uses the public key to check whether the signature is valid for that digest. If the message was altered after signing, the digest computed by the verifier won't match what was signed. Verification fails. If the signature was produced by a different private key than the one corresponding to the provided public key, verification fails.&lt;/p&gt;

&lt;p&gt;A valid verification result means two things: the message has not been modified since it was signed, and the signature was produced by whoever holds the corresponding private key. The public key can be distributed freely. It cannot be used to derive or reconstruct the private key in a properly designed system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Integrity and Authenticity Are Separate Properties
&lt;/h2&gt;

&lt;p&gt;A digital signature provides both integrity and authenticity, but they're worth distinguishing.&lt;/p&gt;

&lt;p&gt;Integrity means the signed data hasn't changed. A properly designed signature scheme uses a hash function where even a one-bit modification to the message causes verification to fail.&lt;/p&gt;

&lt;p&gt;Authenticity means the signature was produced by the holder of a specific private key. This is more subtle than it might seem. A valid signature proves control of a private key. It doesn't automatically prove the real-world identity of the person holding that key. Identity binding requires additional infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  This Is Not Encryption
&lt;/h2&gt;

&lt;p&gt;Digital signatures are often conflated with encryption, especially in casual descriptions. They're different mechanisms with different goals.&lt;/p&gt;

&lt;p&gt;Encryption provides confidentiality: only the intended recipient can read the content. A digital signature provides integrity and authenticity: anyone with the public key can verify that the signature is valid for that message. Signing a message doesn't hide it. If you want both authenticity and confidentiality, you need both signing and encryption, and they're applied separately.&lt;/p&gt;

&lt;p&gt;Non-repudiation is sometimes associated with digital signatures: if only one party holds a private key and a valid signature exists under the corresponding public key, it's difficult for that party to deny having signed the message. But non-repudiation as a legal or practical guarantee depends on key control, identity binding, and surrounding evidence. It's a useful property, not an automatic absolute guarantee.&lt;/p&gt;




&lt;h2&gt;
  
  
  Signature Algorithms in Practice
&lt;/h2&gt;

&lt;p&gt;Three signature schemes are worth knowing by name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RSA-PSS&lt;/strong&gt; is a signature scheme based on RSA. Modern RSA signing should use an appropriate padding and signature construction like PSS rather than naive textbook RSA. The underlying RSA key pair supports both signing and encryption, but those are distinct operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECDSA&lt;/strong&gt; (Elliptic Curve Digital Signature Algorithm) produces signatures using elliptic-curve cryptography. It achieves comparable security to RSA with significantly shorter key lengths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ed25519&lt;/strong&gt; is a modern elliptic-curve signature scheme built on the Edwards curve. It's fast, produces short signatures, and has a simpler security model than ECDSA. It's widely used in SSH keys, TLS, and secure messaging protocols.&lt;/p&gt;

&lt;p&gt;These schemes have different mathematical constructions and different performance characteristics. They don't work identically internally, but they serve the same conceptual purpose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Knowing Whose Key to Trust
&lt;/h2&gt;

&lt;p&gt;A digital signature proves that a specific private key produced the signature. But knowing that a signature is cryptographically valid doesn't tell you whose key you're trusting.&lt;/p&gt;

&lt;p&gt;Certificates solve this by binding a public key to an identity using a signature from a trusted certificate authority. When your browser validates an HTTPS connection, it's checking that the server's public key is certified by a CA your browser trusts. The certificate is itself a signed document: the CA's signature over the server's public key and identity information.&lt;/p&gt;

&lt;p&gt;Software signing works similarly. Operating system package managers, code signing for mobile apps, and signed Git commits all rely on some mechanism for trusting a public key in the first place.&lt;/p&gt;




&lt;h2&gt;
  
  
  Private Key Security
&lt;/h2&gt;

&lt;p&gt;The security of a digital signature scheme depends on the private key remaining private. If an attacker obtains the private key, they can produce signatures that verify successfully under the corresponding public key. The signatures would be cryptographically indistinguishable from legitimate ones.&lt;/p&gt;

&lt;p&gt;This is why private keys are protected carefully, stored in hardware security modules, rotated when compromised, and revoked through certificate revocation mechanisms when necessary. The cryptography is sound; the operational security around key management is where things go wrong in practice.&lt;/p&gt;




&lt;p&gt;The private key creates a signature over a digest of the message. The public key lets anyone verify that signature against the message. If the message changes, the digest changes, and verification fails. If the signature came from a different private key, verification fails.&lt;/p&gt;

&lt;p&gt;That's the entire mechanism. The layers on top, certificates, trust hierarchies, revocation, key management, all exist to answer the question the signature itself can't answer: whose key is this, and should you trust it?&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>What Is CSRF? How a Website Can Make Your Browser Send a Request</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Fri, 04 Sep 2026 08:02:46 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-is-csrf-how-a-website-can-make-your-browser-send-a-request-2k68</link>
      <guid>https://dev.to/aditya_d_sharma/what-is-csrf-how-a-website-can-make-your-browser-send-a-request-2k68</guid>
      <description>&lt;p&gt;You're logged into your bank. The session is active, the authentication cookie is sitting in your browser. You open another tab and visit an unrelated website. That website causes your browser to send a POST request to your bank.&lt;/p&gt;

&lt;p&gt;Your browser attaches the bank's authentication cookie automatically. The bank receives what looks like an authenticated request. It processes it.&lt;/p&gt;

&lt;p&gt;You didn't initiate that request. But your browser did, using your credentials.&lt;/p&gt;

&lt;p&gt;That's Cross-Site Request Forgery. The name describes the mechanism precisely: a request is forged, it's cross-site in origin, and it exploits the fact that your browser carries your credentials wherever it goes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the Browser Does This
&lt;/h2&gt;

&lt;p&gt;Cookies are scoped to domains. When your browser makes a request to a domain, it includes that domain's eligible cookies automatically. This is normal behavior, not a bug. It's what keeps you logged into websites as you navigate around.&lt;/p&gt;

&lt;p&gt;The browser doesn't distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a request you consciously initiated by clicking a button&lt;/li&gt;
&lt;li&gt;a request triggered by JavaScript on a page you're viewing&lt;/li&gt;
&lt;li&gt;a request caused by a resource on another website
From the browser's perspective, a request to bank.com gets bank.com's cookies. The origin of the request, which page caused it, is not reflected in those credentials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The server receives the HTTP request and sees valid authentication. It has no way to know, based on the cookie alone, whether you meant to send that request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Legitimate request:

Browser
   ↓
POST /change-email  +  authentication cookie
   ↓
Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSRF scenario:

Other website
   ↓
Triggers request from victim's browser
   ↓
POST /change-email  +  authentication cookie
   ↓
Application (sees valid credentials)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The attacker doesn't need to steal the cookie. They don't need to read the response. The goal is simply to cause a state-changing request to happen using the victim's existing session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication Is Not the Same as Intent
&lt;/h2&gt;

&lt;p&gt;This is the core of the problem.&lt;/p&gt;

&lt;p&gt;Authentication tells the server who is making the request. A valid cookie establishes that the request comes from an authenticated session. But authentication says nothing about whether the user actually intended to perform the action.&lt;/p&gt;

&lt;p&gt;When a server receives a POST to &lt;code&gt;/transfer&lt;/code&gt; with a valid authentication cookie, it knows the request is associated with a logged-in account. It does not know whether the account holder clicked a button, or whether another website silently triggered that request in the background.&lt;/p&gt;

&lt;p&gt;CSRF exploits the gap between those two things. The credentials are genuine. The request was not.&lt;/p&gt;




&lt;h2&gt;
  
  
  CSRF Tokens
&lt;/h2&gt;

&lt;p&gt;The standard defense against CSRF is introducing something the attacker cannot obtain: an unpredictable token tied to the user's session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/change-email"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"csrf_token"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"k9Zm3..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Update Email&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server generates this token and associates it with the user's session or request context. The legitimate application page includes the token in the form. When the form is submitted, the server checks that the token is present and valid before processing the request.&lt;/p&gt;

&lt;p&gt;The reason this works comes down to browser same-origin restrictions. A page on another domain cannot read the content of your application's pages. It can cause your browser to send a request to the application, but it cannot read the token embedded in the form. Without the token, the forged request fails the server's check.&lt;/p&gt;

&lt;p&gt;Implementations vary. The classic approach stores a token in the session and verifies it server-side. Stateless patterns like signed double-submit cookies avoid server-side state entirely. Both separate "authenticated request" from "request carrying evidence it came from our own page."&lt;/p&gt;




&lt;h2&gt;
  
  
  SameSite Cookies
&lt;/h2&gt;

&lt;p&gt;Modern browsers support a &lt;code&gt;SameSite&lt;/code&gt; attribute on cookies that controls whether they're sent with cross-site requests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SameSite=Strict&lt;/code&gt; prevents the cookie from being sent with any cross-site request, including navigations. Clicking a link from another site to your banking dashboard would not send the session cookie.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SameSite=Lax&lt;/code&gt; is more permissive. Cookies are sent with top-level navigation requests like clicking a link, but not with cross-site subresource requests like forms or fetches triggered by another page. Many browsers now use Lax as the default when &lt;code&gt;SameSite&lt;/code&gt; is not explicitly set.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SameSite=None&lt;/code&gt; sends the cookie with all cross-site requests, which requires &lt;code&gt;Secure&lt;/code&gt; and is used for contexts that genuinely need this behavior.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SameSite=Lax&lt;/code&gt; or &lt;code&gt;Strict&lt;/code&gt; can significantly reduce CSRF risk because the browser simply won't attach the session cookie to forged cross-site requests. But SameSite is a browser-level mitigation. Relying on it alone without application-level token validation isn't a complete approach, both because browser behavior isn't fully uniform across every scenario and because defense in depth matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Origin and Referer Headers
&lt;/h2&gt;

&lt;p&gt;Servers can also inspect the &lt;code&gt;Origin&lt;/code&gt; and &lt;code&gt;Referer&lt;/code&gt; headers to check where a request came from. A state-changing request arriving from an unexpected origin is a signal worth acting on.&lt;/p&gt;

&lt;p&gt;These checks add another layer, but they come with caveats. Neither header is guaranteed to be present on every request. Referer can be stripped by privacy settings or browser behavior. These checks work best as part of a defense-in-depth approach alongside CSRF tokens and appropriate cookie settings, not as the sole defense.&lt;/p&gt;




&lt;h2&gt;
  
  
  CSRF and XSS Are Different Threats
&lt;/h2&gt;

&lt;p&gt;Since XSS came up in the previous article, the comparison is worth making explicit.&lt;/p&gt;

&lt;p&gt;XSS introduces attacker-controlled content into the victim's browser in the application's own origin, where it can run as part of the page. CSRF causes the victim's browser to make a request to a target application from a different origin, exploiting automatic credential attachment.&lt;/p&gt;

&lt;p&gt;One important connection: XSS can undermine CSRF defenses within the affected application. If attacker-controlled JavaScript executes in the application's origin, it may be able to read CSRF tokens, make requests with those tokens attached, and bypass the protection entirely. Fixing XSS and CSRF vulnerabilities are separate efforts, but XSS in an application can render its CSRF mitigations ineffective.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bearer Tokens and the CSRF Threat Model
&lt;/h2&gt;

&lt;p&gt;APIs that require an explicit authorization header rather than a cookie work differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Bearer &amp;lt;token&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser doesn't automatically attach authorization headers to cross-site requests the way it does with cookies. Another site can cause your browser to send a request, but it cannot cause that request to include a bearer token it doesn't have access to.&lt;/p&gt;

&lt;p&gt;This changes the CSRF threat model. If authentication requires JavaScript to explicitly retrieve and attach a token that another origin cannot access, the traditional cookie-based CSRF attack doesn't apply in the same way.&lt;/p&gt;

&lt;p&gt;But this doesn't make bearer tokens unconditionally safe from CSRF. Security properties depend on where the token is stored, how it's obtained, and the application's overall design. The broader point is that CSRF risk is closely tied to the authentication mechanism and what the browser supplies automatically.&lt;/p&gt;




&lt;p&gt;CSRF works because browsers carry credentials on behalf of the user, and servers can't always tell the difference between a request the user intended and one they didn't.&lt;/p&gt;

&lt;p&gt;A session cookie proves authentication. It doesn't prove intent. The defenses, CSRF tokens, SameSite cookies, origin checks, all exist to give the server something that proves the request actually originated from its own trusted context.&lt;/p&gt;

&lt;p&gt;Authentication and intent are different things. CSRF is what happens when a server treats them as the same.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
      <category>web</category>
    </item>
    <item>
      <title>What Is SQL Injection? How User Input Can Become a Database Command</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Thu, 03 Sep 2026 04:43:34 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-is-sql-injection-how-user-input-can-become-a-database-command-3nn</link>
      <guid>https://dev.to/aditya_d_sharma/what-is-sql-injection-how-user-input-can-become-a-database-command-3nn</guid>
      <description>&lt;p&gt;Consider a login form. A user enters a username and password. Somewhere behind that form, the application needs to ask the database whether those credentials exist. A straightforward query for that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'alice'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application constructs that query, sends it to the database, and if a matching row comes back, the user is authenticated. This works. The problem starts with how the query gets constructed.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Strings Become SQL
&lt;/h2&gt;

&lt;p&gt;The simplest way to build that query is also the most dangerous: concatenate the user's input directly into the SQL string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE username = &lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;username&lt;/code&gt; is &lt;code&gt;alice&lt;/code&gt;, the resulting query is sensible SQL. But the database doesn't see &lt;code&gt;username&lt;/code&gt;; it sees the final string. It has no idea which characters came from the developer's query template and which came from the user's input. Once the string is assembled, it's all just SQL.&lt;/p&gt;

&lt;p&gt;This is where the boundary breaks down.&lt;/p&gt;

&lt;p&gt;If a user provides input that contains characters meaningful in SQL syntax, those characters don't stay inside the "data" portion of the query. They become part of the SQL itself. The application intended for the value to be a data literal. The database receives something with a different structure.&lt;/p&gt;

&lt;p&gt;This is SQL injection: user-controlled input influences the SQL syntax rather than remaining data within it.&lt;/p&gt;

&lt;p&gt;The parallel to XSS is direct. In XSS, untrusted data reaches the browser in a context where it becomes executable markup. In SQL injection, untrusted data reaches the database in a context where it becomes part of a command. The failure mode is the same: a boundary between data and executable instructions has been lost.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Fix: Parameterized Queries
&lt;/h2&gt;

&lt;p&gt;The solution isn't to scan input for suspicious characters and filter them out. The solution is to never combine the SQL structure and the user-supplied values into a single string in the first place.&lt;/p&gt;

&lt;p&gt;Parameterized queries do this by separating the two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE username = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query template defines the SQL structure. The value is supplied separately. The database driver handles them as distinct things: the structure tells the database what kind of query this is, and the value is data to be used within that structure.&lt;/p&gt;

&lt;p&gt;This isn't the database "detecting suspicious input." It's a different communication model entirely. Because the structure and the values are never combined into one string, there's no opportunity for user input to alter the query's shape. The username is always treated as a value to match against a column, regardless of what characters it contains.&lt;/p&gt;

&lt;p&gt;Placeholder syntax varies between databases and drivers. Some use &lt;code&gt;?&lt;/code&gt;, others use &lt;code&gt;%s&lt;/code&gt; or named parameters like &lt;code&gt;:username&lt;/code&gt;. The specific syntax is a detail; the principle is the same across all of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prepared Statements
&lt;/h2&gt;

&lt;p&gt;Prepared statements extend this idea further. The application sends the query template to the database first, the database parses and compiles it, and then values are supplied for execution. The SQL structure is fixed before any user data enters the picture.&lt;/p&gt;

&lt;p&gt;The practical effect is similar to parameterized queries: values are always data, never syntax. Some database systems also allow reusing a prepared statement across multiple executions with different values, though that's more of a performance consideration than a security one.&lt;/p&gt;




&lt;h2&gt;
  
  
  ORMs and Query Builders
&lt;/h2&gt;

&lt;p&gt;Modern ORMs and query builders often parameterize values automatically. When you call something like &lt;code&gt;User.where(username: params[:username])&lt;/code&gt; in an ORM, the library typically handles the parameterization for you. The generated SQL keeps structure and values separate.&lt;/p&gt;

&lt;p&gt;But using an ORM doesn't automatically make an application immune to SQL injection. Most ORMs also provide ways to drop into raw SQL when needed. If a developer constructs a raw query with concatenated input through an ORM's escape hatch, the vulnerability is still there. The abstraction helps when you use it correctly; it doesn't protect against deliberately bypassing it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Validation Isn't Enough
&lt;/h2&gt;

&lt;p&gt;Input validation is worth doing. Ensuring that a username only contains letters and numbers, for instance, limits what can be submitted and may reduce certain attack surfaces. But validation is not the fundamental defense against SQL injection.&lt;/p&gt;

&lt;p&gt;Validation works at the application level by restricting what input is accepted. Parameterized queries work at the query level by ensuring that whatever is accepted can never alter SQL structure. These are different guarantees. An application that validates inputs but constructs queries through concatenation is still vulnerable if the validation has gaps, or if the attacker finds a legitimate input that's still useful for injection purposes.&lt;/p&gt;

&lt;p&gt;The safe path is parameterization first, with validation as an additional layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Boundary That Has to Hold
&lt;/h2&gt;

&lt;p&gt;SQL injection is not really about databases being insecure, or about users being malicious, or about any specific characters being dangerous. It's about what happens when an application assembles a command from a template and external input by treating both as the same kind of string.&lt;/p&gt;

&lt;p&gt;The database receives one SQL statement. It parses that statement and executes it. It has no visibility into how the statement was assembled or which parts the developer intended as structure versus which parts a user supplied. If those two things were combined into the same string, the database can't separate them.&lt;/p&gt;

&lt;p&gt;Parameterized queries don't give the database better judgment. They change the interface so the developer never has to combine them in the first place. The SQL structure and the user-supplied values travel separately, and the database handles them as separate things.&lt;/p&gt;

&lt;p&gt;The problem isn't that users are allowed to enter text. The problem is allowing that text to become part of the program's command language. Keeping data as data, and keeping SQL as SQL, is the entire fix.&lt;/p&gt;

</description>
      <category>database</category>
      <category>programming</category>
      <category>security</category>
      <category>sql</category>
    </item>
    <item>
      <title>What Is Cross-Site Scripting (XSS)? Understanding a Critical Web Security Vulnerability.</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Wed, 02 Sep 2026 06:53:09 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-is-cross-site-scripting-xss-understanding-a-critical-web-security-vulnerability-231f</link>
      <guid>https://dev.to/aditya_d_sharma/what-is-cross-site-scripting-xss-understanding-a-critical-web-security-vulnerability-231f</guid>
      <description>&lt;p&gt;Imagine a website where users can post comments. Someone submits this as their comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the application takes that input and places it directly into the HTML it serves to other users, the browser doesn't see a comment. It sees a script tag. The vulnerability isn't that JavaScript exists on the page. JavaScript belongs on web pages. The problem is that untrusted user input ended up in a context where the browser interpreted it as executable content rather than inert text.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Problem
&lt;/h2&gt;

&lt;p&gt;A browser rendering a webpage doesn't distinguish between HTML the developer wrote and HTML that arrived through a comment field. It parses what it's given. If user input gets embedded into the page without being handled carefully, the browser processes it the same way it processes everything else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Input
    ↓
Web Application
    ↓
HTML / DOM
    ↓
Browser
    ↓
Input interpreted as executable content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Untrusted data should remain data. XSS occurs when the application allows that data to cross into a context where the browser interprets it as code or executable markup. The boundary between "string containing angle brackets" and "HTML the browser will parse" is where the vulnerability lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Forms of XSS
&lt;/h2&gt;

&lt;p&gt;XSS shows up in a few different ways depending on where the injection happens and how the input travels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stored XSS&lt;/strong&gt; is when untrusted input gets saved to a database and later served to other users. The comment example above is stored XSS. An attacker submits input once, and every user who views that page subsequently receives it. The application acts as an unwitting distribution mechanism.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reflected XSS&lt;/strong&gt; involves input that isn't stored but gets reflected back in an immediate server response. Search pages are a common example: if a query is echoed into the page as "You searched for: [query]" and the query isn't handled carefully, an attacker can craft a URL whose query parameter contains a payload. When another user visits that URL, the server reflects the input back in the response without the browser having any way to know it didn't come from the developer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DOM-based XSS&lt;/strong&gt; is different in that no server-side injection is required. The server sends a perfectly safe response, but client-side JavaScript reads from an attacker-controlled source, such as a URL fragment or a query parameter, and places it into an unsafe DOM context without appropriate handling. The server does not need to store or reflect the payload; the vulnerability exists entirely in how the client-side code processes data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why &lt;code&gt;innerHTML&lt;/code&gt; Matters
&lt;/h2&gt;

&lt;p&gt;This is where the mechanism becomes most concrete for web developers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you assign to &lt;code&gt;innerHTML&lt;/code&gt;, you're asking the browser to parse the string as HTML. Attacker-controlled markup can therefore create elements or attributes that introduce executable browser contexts. An image element with an &lt;code&gt;onerror&lt;/code&gt; handler, for example, doesn't require a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;img src="x" onerror="/* runs here */"&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The string has become markup, and the browser treats it accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;textContent&lt;/code&gt; is different. It assigns the string as plain text. Angle brackets are treated as literal characters, not HTML. Whatever the user submitted appears on the page as written, not as parsed markup.&lt;/p&gt;

&lt;p&gt;The distinction isn't that &lt;code&gt;innerHTML&lt;/code&gt; is inherently dangerous. &lt;code&gt;innerHTML&lt;/code&gt; is a legitimate API, and assigning developer-controlled or appropriately processed content through it is fine. The risk arises specifically when untrusted input flows into it without being handled: the browser cannot tell that the string originated from a user rather than the application, so it parses it all the same.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frameworks and Where XSS Still Appears
&lt;/h2&gt;

&lt;p&gt;Modern frontend frameworks generally escape interpolated content by default. When you render a variable in a React component or an Angular template, the framework treats it as text rather than HTML, preventing ordinary variables from being interpreted as markup.&lt;/p&gt;

&lt;p&gt;But frameworks provide explicit ways to opt out of this. React has &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt;. Angular has &lt;code&gt;bypassSecurityTrustHtml&lt;/code&gt;. These APIs exist for legitimate use cases where rendering HTML is genuinely necessary. When developers use them with untrusted input, they've deliberately stepped outside the safe defaults the framework was providing.&lt;/p&gt;

&lt;p&gt;The important point is that frameworks reduce the surface area for XSS by making the safe path the default path, but they don't eliminate the possibility. Developers can still create unsafe data flows when they work around framework protections, or when they use lower-level DOM APIs directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Defenses
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Output encoding&lt;/strong&gt; is the primary defense. When user-supplied data is placed into HTML, JavaScript, CSS, or a URL, it should be encoded appropriately for that context so the browser treats it as data rather than code. The encoding rules differ by context: what's safe to leave unescaped inside an HTML attribute is different from what's safe inside a JavaScript string. Treating all output encoding as a single uniform operation misses this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safe DOM APIs&lt;/strong&gt; are the practical expression of this for client-side code. &lt;code&gt;textContent&lt;/code&gt; is safe for plain text because it doesn't invoke HTML parsing. Building DOM nodes programmatically with &lt;code&gt;createElement&lt;/code&gt; and setting their properties explicitly avoids the risk of parsing an arbitrary HTML string, though safety still depends on what's assigned to those properties afterward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework-provided escaping&lt;/strong&gt; is useful because it pushes the safe default into the rendering layer, so developers get protection without having to remember to apply encoding manually on every interpolated value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content Security Policy&lt;/strong&gt; is defense in depth. A strong CSP can tell the browser which scripts are permitted to run, limiting the impact of a successful injection. It doesn't remove the underlying vulnerability; if untrusted input is making it into an executable context, CSP is constraining the damage rather than preventing the injection. It's a meaningful layer, but not a substitute for fixing the root cause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input validation&lt;/strong&gt; is worth doing for many reasons, but it shouldn't be treated as the primary XSS defense. Validating that a field looks like an expected format doesn't guarantee that whatever passes validation is safe to insert into every possible output context. An application that validates inputs but then places them into the page without encoding is still vulnerable.&lt;/p&gt;




&lt;p&gt;The browser has no way to know where a string came from. It doesn't know a piece of HTML arrived through a comment field rather than from the developer. It just parses what the application puts in front of it.&lt;/p&gt;

&lt;p&gt;XSS is not a browser flaw, and it's not a problem with JavaScript. It's a failure to maintain the boundary between data and code. The application accepted input, and somewhere between accepting it and rendering it, that input crossed into a context where the browser would execute it.&lt;/p&gt;

&lt;p&gt;Keeping that from happening is the whole job.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>javascript</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stack vs Heap Memory: What Actually Happens When You Create a Variable?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Tue, 01 Sep 2026 10:19:27 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/stack-vs-heap-memory-what-actually-happens-when-you-create-a-variable-5de4</link>
      <guid>https://dev.to/aditya_d_sharma/stack-vs-heap-memory-what-actually-happens-when-you-create-a-variable-5de4</guid>
      <description>&lt;p&gt;Look at this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What actually happens when the program executes it? Somewhere in memory, space for an integer gets set aside, and the value 10 goes into it. But where in memory? And what decides when that space gets reclaimed?&lt;/p&gt;

&lt;p&gt;The answer involves two different regions of memory that programs use for different purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Program
   ↓
Memory
   ├── Stack
   └── Heap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;When your program calls a function, the runtime needs somewhere to put things: the function's local variables, the arguments passed to it, a record of where to return when the function finishes. All of this gets stored in a structure called a stack frame, and that frame lives on the stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;example()&lt;/code&gt; is called, a frame is pushed onto the stack. &lt;code&gt;x&lt;/code&gt; lives inside that frame. When &lt;code&gt;example()&lt;/code&gt; returns, the frame is popped. The memory that held &lt;code&gt;x&lt;/code&gt; is gone, or more precisely, it's considered free to be reused. You don't have to do anything to make that happen. The lifetime of &lt;code&gt;x&lt;/code&gt; is tied to the lifetime of the function call.&lt;/p&gt;

&lt;p&gt;This is why local variables work the way they do. They exist while the function is running and disappear when it's done. The stack grows and shrinks as functions are called and return, and the bookkeeping is simple: the runtime just maintains a pointer to the current top of the stack.&lt;/p&gt;

&lt;p&gt;Allocation on the stack is cheap for this reason. Reserving space for a local variable is typically just adjusting that stack pointer by a fixed amount. No searching, no tracking, no negotiation with the operating system. The frame is there when you enter the function and gone when you leave.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Heap
&lt;/h2&gt;

&lt;p&gt;Some memory needs to outlive the function that created it. Maybe you're building a data structure that gets passed around. Maybe you don't know at compile time how much memory you'll need. The stack isn't designed for this. That's what the heap is for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;malloc&lt;/code&gt; allocates memory on the heap and returns a pointer to it. That memory doesn't disappear when the current function returns. It persists until someone explicitly frees it, or in languages with garbage collection, until the runtime determines it's no longer reachable.&lt;/p&gt;

&lt;p&gt;The pointer &lt;code&gt;p&lt;/code&gt; itself might live on the stack. But what &lt;code&gt;p&lt;/code&gt; points to lives on the heap. That distinction matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stack
┌──────────────┐
│      p  ──────────────┐
└──────────────┘        │
                        ↓
                      heap
                ┌───────────────┐
                │  int value    │
                └───────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a useful mental model, but treat it as conceptual. Different languages and runtimes manage this differently. The diagram is showing you the idea, not a literal memory map.&lt;/p&gt;

&lt;p&gt;The tradeoff with heap allocation is that it's more work. The runtime has to find a suitable free region, track what's in use, and eventually reclaim memory when it's no longer needed. In C, you do that manually with &lt;code&gt;free()&lt;/code&gt;. In Java or JavaScript, a garbage collector handles it. Either way, there's overhead that stack allocation doesn't have.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Both Exist
&lt;/h2&gt;

&lt;p&gt;The stack is well-suited to local execution state: things that exist for the duration of a function call and nothing longer. Its automatic lifetime management is a feature, not a limitation.&lt;/p&gt;

&lt;p&gt;The heap is well-suited to data with more flexible lifetimes: things that need to outlive the function that created them, or that need to be shared across different parts of a program, or whose size isn't known until runtime.&lt;/p&gt;

&lt;p&gt;Most programs use both constantly. A function's local variables live on the stack. Objects created with &lt;code&gt;new&lt;/code&gt; in Java, or allocated with &lt;code&gt;malloc&lt;/code&gt; in C, live on the heap. References or pointers on the stack point into the heap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Misconceptions
&lt;/h2&gt;

&lt;p&gt;A few things that get oversimplified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not every local variable is guaranteed to live on the stack.&lt;/strong&gt; Modern compilers can perform escape analysis: if they determine that a local variable doesn't escape the current function, they might keep it in a register rather than pushing it to memory at all. Conversely, if a variable does escape, the compiler might allocate it on the heap even if you wrote it as a local. The source code doesn't dictate the physical layout; the compiler and runtime do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not every object is guaranteed to live on the heap.&lt;/strong&gt; Some languages and runtimes can allocate short-lived objects on the stack if they can prove it's safe. The distinction in source code between "stack variable" and "heap object" doesn't always map cleanly onto what actually happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack is not simply "fast" and heap is not simply "slow."&lt;/strong&gt; Stack allocation is simpler, but whether that translates to a meaningful performance difference depends entirely on the workload. The more important factor for memory performance is usually cache behaviour, and that has more to do with access patterns than with which region the memory lives in. The stack is not the same thing as CPU cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heap allocation doesn't necessarily mean the program talks to the operating system every time.&lt;/strong&gt; Memory allocators like &lt;code&gt;malloc&lt;/code&gt; typically request larger chunks of memory from the OS and then manage those chunks internally. Individual &lt;code&gt;malloc&lt;/code&gt; calls usually don't cross the kernel boundary; they're served from a pool the allocator already has.&lt;/p&gt;




&lt;p&gt;The mental model worth keeping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stack → function calls, local execution state, automatic lifetime
Heap  → dynamically managed memory with more flexible lifetime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are useful conceptual categories. Where memory physically ends up, and how fast it is to access, depends on the language, the compiler, the runtime, and what the hardware decides to do with it. The stack and heap are real, but they're not rigid physical rules. They're a model for thinking about how programs manage memory, and like most models, they're most useful when you also know where they break down.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>programming</category>
      <category>software</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>What Does `async/await` Actually Do in JavaScript?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Mon, 31 Aug 2026 13:38:37 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-does-asyncawait-actually-do-in-javascript-36i9</link>
      <guid>https://dev.to/aditya_d_sharma/what-does-asyncawait-actually-do-in-javascript-36i9</guid>
      <description>&lt;p&gt;Most developers learn &lt;code&gt;async/await&lt;/code&gt; as syntax that makes asynchronous code look synchronous. You &lt;code&gt;await&lt;/code&gt; something, and the code below it runs after. It reads top to bottom, like normal code. That mental model is mostly fine for writing async functions, but it hides what's actually happening. And once you need to debug a weird execution order or understand why something runs when it does, the surface-level model breaks down.&lt;/p&gt;

&lt;p&gt;The thing most people get wrong: &lt;code&gt;await&lt;/code&gt; looks like it pauses execution. It doesn't. Not in the way you might think.&lt;/p&gt;




&lt;h2&gt;
  
  
  An async Function Is Just a Function That Returns a Promise
&lt;/h2&gt;

&lt;p&gt;Start here, because everything else follows from it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;getData()&lt;/code&gt; doesn't return &lt;code&gt;42&lt;/code&gt;. It returns &lt;code&gt;Promise.resolve(42)&lt;/code&gt;. Every &lt;code&gt;async&lt;/code&gt; function wraps its return value in a Promise, whether you ask for it or not. If the function throws, the returned Promise rejects instead.&lt;/p&gt;

&lt;p&gt;This matters because it means an &lt;code&gt;async&lt;/code&gt; function, from the outside, is just a function that returns a Promise. The &lt;code&gt;async&lt;/code&gt; keyword is doing two things: allowing &lt;code&gt;await&lt;/code&gt; inside the function, and ensuring the return value is always a Promise.&lt;/p&gt;




&lt;h2&gt;
  
  
  What &lt;code&gt;await&lt;/code&gt; Actually Does
&lt;/h2&gt;

&lt;p&gt;When execution reaches an &lt;code&gt;await&lt;/code&gt; expression, a few things happen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;console.log("A")&lt;/code&gt; runs synchronously. Then &lt;code&gt;fetch("/data")&lt;/code&gt; is called, which returns a Promise. At the point of &lt;code&gt;await&lt;/code&gt;, the async function suspends. Execution leaves &lt;code&gt;getData&lt;/code&gt; and returns to whatever called it.&lt;/p&gt;

&lt;p&gt;The key word is suspends, not blocks. The JavaScript thread is not sitting there waiting. It's free to do other things. The rest of the call stack continues. Other code can run. The event loop can process other tasks. &lt;code&gt;getData&lt;/code&gt; is just paused at that line, waiting for the Promise to settle.&lt;/p&gt;

&lt;p&gt;When the Promise from &lt;code&gt;fetch&lt;/code&gt; eventually resolves, the continuation of &lt;code&gt;getData&lt;/code&gt;, everything after the &lt;code&gt;await&lt;/code&gt;- gets scheduled to run. The function picks up where it left off. &lt;code&gt;console.log("B")&lt;/code&gt; runs.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Thread Isn't Paused. The Function Is.
&lt;/h2&gt;

&lt;p&gt;This is the distinction that matters most.&lt;/p&gt;

&lt;p&gt;JavaScript is single-threaded. There is one call stack. When code is running, nothing else runs. &lt;code&gt;await&lt;/code&gt; doesn't change this. It doesn't create a new thread. It doesn't run anything in the background. What it does is hand control back to the event loop while the current async function waits.&lt;/p&gt;

&lt;p&gt;The event loop is part of the mechanism that allows JavaScript to process other scheduled work while an async function is suspended. It looks for work to do: callbacks from timers, I/O results, resolved Promises, user events. When a Promise that was being awaited settles, the continuation of the async function is scheduled as a microtask, and the event loop runs it once the current call stack is empty.&lt;/p&gt;

&lt;p&gt;To see this clearly, look at what happens with an immediately resolved Promise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might expect: C, A, B, D. The actual output is: C, A, D, B.&lt;/p&gt;

&lt;p&gt;Here's why. &lt;code&gt;console.log("C")&lt;/code&gt; runs. &lt;code&gt;test()&lt;/code&gt; is called. Inside &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;console.log("A")&lt;/code&gt; runs. Then &lt;code&gt;await Promise.resolve()&lt;/code&gt; is reached. The Promise is already resolved, but &lt;code&gt;await&lt;/code&gt; still suspends the function. The continuation is scheduled as a microtask, not run immediately. Execution returns to the caller. &lt;code&gt;console.log("D")&lt;/code&gt; runs. The current call stack is now empty. The event loop picks up the microtask. &lt;code&gt;console.log("B")&lt;/code&gt; runs.&lt;/p&gt;

&lt;p&gt;Even though the Promise was resolved instantly, the code after &lt;code&gt;await&lt;/code&gt; didn't run synchronously. The continuation is always scheduled as a microtask rather than continuing inline. That's by design, and it keeps async functions behaviorally consistent regardless of whether the awaited value was ready immediately or not.&lt;/p&gt;




&lt;h2&gt;
  
  
  await Doesn't Make Things Run in Parallel
&lt;/h2&gt;

&lt;p&gt;This is a common misunderstanding worth being explicit about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/endpoint-a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/endpoint-b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two requests don't run at the same time. The second &lt;code&gt;fetch&lt;/code&gt; doesn't start until the first one resolves. You've written sequential async code, not parallel async code. If you want both requests to happen concurrently, you have to start both Promises before awaiting either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/endpoint-a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/endpoint-b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;await&lt;/code&gt; means "suspend this async function until this Promise settles." Whether things run in parallel depends entirely on when you start them, not on how many &lt;code&gt;await&lt;/code&gt; keywords you use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the Asynchronous Work Actually Happens
&lt;/h2&gt;

&lt;p&gt;One more thing worth clarifying: when you &lt;code&gt;await fetch("/data")&lt;/code&gt;, the HTTP request isn't happening inside JavaScript. The &lt;code&gt;fetch&lt;/code&gt; call hands the request to the browser's networking layer, which handles it outside the JavaScript thread. JavaScript just registers interest in the result. When the response arrives, the browser resolves the Promise, and the event loop delivers the continuation back to your async function.&lt;/p&gt;

&lt;p&gt;This is why &lt;code&gt;await&lt;/code&gt; works without blocking the thread. The actual waiting isn't happening in JavaScript at all. It's happening in the host environment. JavaScript just picks up the result when it's ready.&lt;/p&gt;




&lt;p&gt;&lt;code&gt;async/await&lt;/code&gt; is a cleaner way to work with Promises. It makes the control flow easier to follow and the error handling more natural. But the execution model underneath it is the same: Promises, microtasks, and the event loop.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await&lt;/code&gt; doesn't pause JavaScript. It pauses the function you're currently inside, hands control back to the event loop, and lets the thread get on with other work until the Promise settles and your function is ready to continue.&lt;/p&gt;

&lt;p&gt;The code looks like it stops and waits. The runtime doesn't.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What Is a System Call? And Why Does It Cost Time?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Sat, 29 Aug 2026 07:32:22 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/what-is-a-system-call-and-why-does-it-cost-time-2ae8</link>
      <guid>https://dev.to/aditya_d_sharma/what-is-a-system-call-and-why-does-it-cost-time-2ae8</guid>
      <description>&lt;p&gt;Look at this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like the simplest thing a program can do. One function, one string, one line. But between that call and the text appearing somewhere, a surprising amount happens. Your program doesn't just reach out and write characters to a screen. It can't. And understanding why is the interesting part.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Program Doesn't Own the Machine
&lt;/h2&gt;

&lt;p&gt;When your code runs, it runs in what's called user space. The operating system kernel runs separately, with higher privileges. This separation isn't accidental. It exists because applications shouldn't be able to directly control hardware, read arbitrary memory belonging to other processes, or perform privileged operations. If any program could do any of that, the entire system would be one bug away from catastrophe.&lt;/p&gt;

&lt;p&gt;So the kernel sits between applications and the resources they need. Want to write to a file? The kernel handles it. Want to send data over the network? The kernel handles it. Want to allocate memory beyond what you already have? The kernel handles it.&lt;/p&gt;

&lt;p&gt;The mechanism through which a program asks the kernel to do something on its behalf is called a system call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your program
↓
User space
↓
System call
↓
Kernel
↓
OS-managed resource / hardware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  printf() Is Not a System Call
&lt;/h2&gt;

&lt;p&gt;This is where people often get confused. &lt;code&gt;printf()&lt;/code&gt; is a C library function, not a system call. It lives in user space alongside your code. What it does is format the string, handle the conversion specifiers, and manage an output buffer. Eventually, when it actually needs to send data somewhere, the library calls &lt;code&gt;write()&lt;/code&gt;, which is a system call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf()
↓
C library
↓
formatting + buffering
↓
write()
↓
system call
↓
kernel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters. Library functions are just code. They run in user space with no special privileges. A system call is something different: it's a request that crosses the boundary between your program and the kernel.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Happens at the CPU Level
&lt;/h2&gt;

&lt;p&gt;A system call isn't like a normal function call. When you call a regular function, execution stays in the same privilege level. The CPU jumps to another address in memory and comes back. Nothing about the system changes.&lt;/p&gt;

&lt;p&gt;A system call triggers a hardware-supported mechanism that transfers execution from user-mode code to a kernel-controlled entry point at a higher privilege level. On x86-64, the instruction that does this is &lt;code&gt;syscall&lt;/code&gt;. Other architectures have their own mechanisms. The point is that this transition is intentional and hardware-enforced, not just a jump in software.&lt;/p&gt;

&lt;p&gt;The flow looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User program
↓
Prepare syscall number + arguments
↓
CPU executes syscall instruction
↓
Kernel entry point
↓
Kernel validates the request
↓
Kernel performs the operation
↓
Return to user space
↓
Program continues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The kernel validates the request because it can't simply trust what the program says. If your program calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the kernel has to figure out what &lt;code&gt;fd&lt;/code&gt; refers to, whether this process is allowed to use it, whether &lt;code&gt;buffer&lt;/code&gt; points to memory the process actually owns, and what the real operation should be. This is part of the job. The kernel is the gatekeeper, not a passive executor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Costs Time
&lt;/h2&gt;

&lt;p&gt;System calls aren't catastrophically slow, but they aren't free either. Crossing the user/kernel boundary has overhead that a normal function call doesn't.&lt;/p&gt;

&lt;p&gt;Compare the two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal function call:
user code → function → return

System call:
user code
↓
privilege transition
↓
kernel entry
↓
validation + kernel work
↓
return to user space
↓
program continues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The privilege transition itself takes time. The CPU has to switch modes, save state, verify the request, do the actual work, and then restore state to return to user space. Compared to jumping to a function in the same process, that's meaningfully more work.&lt;/p&gt;

&lt;p&gt;One thing worth being clear about: a system call is not the same as a context switch. A context switch is when the operating system stops one process and runs another. A system call doesn't necessarily cause that. Your thread can make a system call, the kernel does its job, and your thread resumes. Context switches and system calls are related in some situations, but they're not the same thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Back to printf()
&lt;/h2&gt;

&lt;p&gt;Here's where the buffering piece becomes practically useful.&lt;/p&gt;

&lt;p&gt;Four calls to &lt;code&gt;printf()&lt;/code&gt; don't necessarily mean four system calls. The C library can accumulate output in a user-space buffer and make one larger &lt;code&gt;write()&lt;/code&gt; call instead of four small ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf()
printf()
printf()
printf()
↓
user-space buffer
↓
single larger write
↓
one system call
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't just an implementation detail. It's a real design choice. If every &lt;code&gt;printf()&lt;/code&gt; crossed into the kernel immediately, programs doing a lot of output would spend a surprising amount of time on privilege transitions. Buffering reduces that by batching work.&lt;/p&gt;

&lt;p&gt;The same principle shows up in other places. Databases, network servers, and high-performance applications that do a lot of I/O often think carefully about how many times they cross the user/kernel boundary and how much work each crossing does. Not because system calls are to be avoided, but because doing thousands of tiny ones when you could do fewer larger ones adds up.&lt;/p&gt;




&lt;p&gt;So when you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;what you're actually setting in motion is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf()
↓
C library formatting
↓
possibly buffered output
↓
write() system call
↓
kernel validates and executes
↓
OS-managed output resource
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your program doesn't directly control the machine. It describes what it wants, and the kernel decides how to make it happen. That request crosses a boundary that exists for good reasons, and crossing it has a cost.&lt;/p&gt;

&lt;p&gt;That's why &lt;code&gt;printf()&lt;/code&gt; is not free. And that's why it's worth knowing what's actually happening when you call it.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>performance</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Is null More Than Just "No Value"? The Problem With null References.</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Fri, 28 Aug 2026 17:08:22 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/why-is-null-more-than-just-no-value-the-problem-with-null-references-4bep</link>
      <guid>https://dev.to/aditya_d_sharma/why-is-null-more-than-just-no-value-the-problem-with-null-references-4bep</guid>
      <description>&lt;p&gt;Look at this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's something quietly strange about it. You have a variable. You have a value assigned to it. And that value is being used to express the idea that there is no value.&lt;/p&gt;

&lt;p&gt;A missing value is being represented as a value. That contradiction is where the problem starts.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a Reference Actually Is
&lt;/h2&gt;

&lt;p&gt;In most languages, when you create an object, your variable doesn't hold the object directly. It holds a reference, something that points to where the object lives in memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable
    ↓
  reference
    ↓
object in memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why multiple variables can refer to the same object. They don't contain separate copies of the object; they hold references to the same underlying thing in memory.&lt;/p&gt;

&lt;p&gt;When a variable is null, that reference points to nothing. The variable exists. The reference exists. But there's no object at the other end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable
    ↓
  null
    ↓
  (nothing)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What null actually is under the hood depends on the language and runtime. In many systems, a null reference is represented as a zero address or some other sentinel value, but that's an implementation detail. The important thing conceptually is that the reference isn't pointing anywhere meaningful.&lt;/p&gt;

&lt;p&gt;Now do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;user&lt;/code&gt; is null, there's nothing to follow. The program tries to dereference a reference that leads nowhere. Depending on the language, this produces a null reference error, a null pointer exception, a crash, or some other runtime failure. The exact behaviour differs, but the cause is the same: you tried to use a reference that led nowhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Hoare Called It a Mistake
&lt;/h2&gt;

&lt;p&gt;Tony Hoare introduced null references in ALGOL W in 1965. He's also the person who invented Quicksort, so he's not someone who made careless decisions. Decades later he called it his "billion-dollar mistake," characterising the enormous cumulative cost that null-related errors had caused across the software industry.&lt;/p&gt;

&lt;p&gt;His reasoning wasn't that null itself is conceptually wrong. It was that introducing null adds an implicit state to every reference in the language.&lt;/p&gt;

&lt;p&gt;Before null, a variable of type &lt;code&gt;User&lt;/code&gt; means: there is a User here.&lt;/p&gt;

&lt;p&gt;After null, a variable of type &lt;code&gt;User&lt;/code&gt; means: there is a User here, or there isn't.&lt;/p&gt;

&lt;p&gt;Every function that accepts a &lt;code&gt;User&lt;/code&gt; now has two possible inputs instead of one. Every caller now has to decide whether to check. Every place that doesn't check is a latent bug. The type said &lt;code&gt;User&lt;/code&gt;, but it was quietly lying.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Null Propagates
&lt;/h2&gt;

&lt;p&gt;The real cost isn't the crash. It's how far absence can travel before anyone notices.&lt;/p&gt;

&lt;p&gt;Imagine a chain like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;address&lt;/code&gt; can be null, this line might fail. But the actual mistake might not be here. The mistake might be several layers back, in a database query that returned nothing, which got assigned to a variable typed as &lt;code&gt;User&lt;/code&gt;, which got passed through two functions that didn't check, which eventually reached this line.&lt;/p&gt;

&lt;p&gt;The crash happens here. The bug lives somewhere else entirely.&lt;/p&gt;

&lt;p&gt;Once null is possible, that possibility has to be tracked everywhere the value travels. Functions that receive a nullable value have to handle absence. Functions that call those functions have to know. The chain looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Function A → might return null
Function B → has to check
Function C → checks again
Function D → forgets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At scale, this becomes exhausting. Not because any single null check is hard, but because the information that a value might be absent has to stay accurate across every function, every layer, every developer who touches the code. The compiler isn't tracking it. Discipline is.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Modern Languages Do Instead
&lt;/h2&gt;

&lt;p&gt;Kotlin handles this by making nullability part of the type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;   &lt;span class="c1"&gt;// cannot be null&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;  &lt;span class="c1"&gt;// might be null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;String&lt;/code&gt; and a &lt;code&gt;String?&lt;/code&gt; are different types. If you try to call a method on a &lt;code&gt;String?&lt;/code&gt; without handling the null case, the compiler complains. You can use the safe call operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which returns null instead of crashing if &lt;code&gt;name&lt;/code&gt; is null. Or you can do an explicit check. The point is that the type now communicates whether absence is possible, and the compiler helps enforce that you've dealt with it.&lt;/p&gt;

&lt;p&gt;Rust goes further with &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than a null value, absence is represented explicitly through the type. A value is either &lt;code&gt;Some(value)&lt;/code&gt; or &lt;code&gt;None&lt;/code&gt;. There's no &lt;code&gt;String&lt;/code&gt; that secretly might be nothing. If a function might not return a value, its return type says so explicitly. And if you want to use the value inside, you have to unwrap it, which means you have to handle the &lt;code&gt;None&lt;/code&gt; case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Some(value) → here's the value
None        → there is no value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't eliminate all bugs related to missing data. You can still write incorrect logic. But the possibility of absence is encoded in the type, not hidden behind a convention that everyone has to remember.&lt;/p&gt;




&lt;p&gt;The deeper problem with null was never just that it causes crashes. It's that it introduces an implicit state that the type system doesn't track. A value that could secretly be nothing is a value the whole program has to silently handle everywhere.&lt;/p&gt;

&lt;p&gt;When Hoare called it a billion-dollar mistake, he wasn't saying the concept of absence is wrong. Absence is real. Data can be missing. The question is whether that possibility should be invisible or explicit.&lt;/p&gt;

&lt;p&gt;A missing value shouldn't pretend to be just another value. When it does, you don't have one kind of thing in your program. You have two. And only one of them is written in the types.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Why Is Composition Often Better Than Inheritance?</title>
      <dc:creator>Aditya Sharma</dc:creator>
      <pubDate>Wed, 26 Aug 2026 04:53:42 +0000</pubDate>
      <link>https://dev.to/aditya_d_sharma/why-is-composition-often-better-than-inheritance-5d67</link>
      <guid>https://dev.to/aditya_d_sharma/why-is-composition-often-better-than-inheritance-5d67</guid>
      <description>&lt;p&gt;Start with something simple. You're modelling birds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Flying&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Eagle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Penguin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eagle looks fine. Penguin doesn't. A penguin is a bird, but it can't fly. So now what? You override &lt;code&gt;fly&lt;/code&gt; in Penguin to do nothing, or raise an exception, or print "I can't fly." All of these feel wrong because they are wrong. You've given Penguin a method it shouldn't have, then tried to undo it.&lt;/p&gt;

&lt;p&gt;This is a small example, but the problem gets worse as the application grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  When the Hierarchy Starts Fighting You
&lt;/h2&gt;

&lt;p&gt;Say the application now has Eagles, Penguins, Ducks, and Ostriches. Each has a different combination of capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eagle: flies, walks&lt;/li&gt;
&lt;li&gt;Penguin: walks, swims&lt;/li&gt;
&lt;li&gt;Duck: flies, walks, swims&lt;/li&gt;
&lt;li&gt;Ostrich: walks
The natural instinct is to keep solving this with inheritance. Maybe you split &lt;code&gt;Bird&lt;/code&gt; into &lt;code&gt;FlyingBird&lt;/code&gt; and &lt;code&gt;NonFlyingBird&lt;/code&gt;. But then Duck needs to swim, and Penguin needs to swim, and they're in different branches of the hierarchy. So you add a &lt;code&gt;SwimmingBird&lt;/code&gt;. But Duck also flies. Now you're either duplicating behaviour or reaching for multiple inheritance, which opens its own set of problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The hierarchy is getting complicated not because the problem is complicated, but because you're trying to encode everything into a parent-child tree.&lt;/p&gt;

&lt;p&gt;The deeper issue is that flying, walking, and swimming are independent capabilities. A bird might have any combination of them. Inheritance forces you to express that through a single chain of parent classes, and a chain can only go in one direction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Inheritance Couples You to the Parent
&lt;/h2&gt;

&lt;p&gt;Inheritance creates a strong relationship between a child class and its parent. The child gets everything the parent has, whether it needs it or not. If the parent changes, children are affected. If the parent grows to accommodate the needs of one subclass, it can start leaking behaviour that other subclasses don't need.&lt;/p&gt;

&lt;p&gt;The problem isn't that inheritance is useless. It's that it bundles two separate ideas together: subtype polymorphism (a Penguin can be used wherever a Bird is expected) and behaviour reuse (a Penguin gets Bird's methods for free). When you only need one of these, inheritance can still make sense. But when the behaviour you're reusing doesn't fit cleanly, the coupling becomes a liability.&lt;/p&gt;




&lt;h2&gt;
  
  
  What If Behaviour Didn't Have to Come From the Parent?
&lt;/h2&gt;

&lt;p&gt;Instead of inheriting capabilities, what if each bird was given the capabilities it actually needs?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlyingMovement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Flying&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WalkingMovement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Walking&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SwimmingMovement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Swimming&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;movement&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;movement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;movement&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;movement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;eagle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FlyingMovement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;penguin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WalkingMovement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Duck needs multiple capabilities, so you'd extend this slightly, but the key idea is already visible: the behaviour lives in a separate object that gets composed into the bird rather than inherited from a parent.&lt;/p&gt;

&lt;p&gt;If you add a new bird tomorrow that can fly but not swim, you don't touch the existing hierarchy. You just compose it differently. The behaviour varies independently from the type.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Makes Change Easier
&lt;/h2&gt;

&lt;p&gt;The practical benefit shows up when requirements change.&lt;/p&gt;

&lt;p&gt;With inheritance, adding a new capability often means modifying a parent class or restructuring the hierarchy. Either can affect classes you didn't intend to touch. Tests that were passing can start failing. The blast radius of a change is hard to predict.&lt;/p&gt;

&lt;p&gt;With composition, you can change one behaviour object without affecting anything else. You can swap out a movement strategy without touching Bird. You can test FlyingMovement in complete isolation. If a new requirement means Penguins should now be able to glide short distances, you can introduce a GlidingMovement without reorganising the entire bird hierarchy.&lt;/p&gt;

&lt;p&gt;Each piece of behaviour is a small, independent unit. You assemble what you need from those units rather than inheriting a bundle and spending effort removing what you don't need.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Inheritance Still Makes Sense
&lt;/h2&gt;

&lt;p&gt;None of this means inheritance is wrong.&lt;/p&gt;

&lt;p&gt;Consider shapes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;3.14&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every Circle genuinely is a Shape. The subtype relationship is real and stable. Polymorphism is genuinely useful here: you can pass a Circle or a Rectangle wherever a Shape is expected, and the right &lt;code&gt;area&lt;/code&gt; method gets called. The hierarchy isn't fighting the design, it's expressing it.&lt;/p&gt;

&lt;p&gt;Inheritance works well when the subtype relationship is meaningful, the shared interface is stable, and the behaviour you're inheriting is actually the behaviour you need. When all of those are true, using it is reasonable.&lt;/p&gt;

&lt;p&gt;The problem is when inheritance gets used as a mechanism for code reuse even when the subtype relationship isn't clean, because it's the first tool that comes to mind.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Broader Lesson
&lt;/h2&gt;

&lt;p&gt;Inheritance says: this object gets behaviour because it belongs to this hierarchy.&lt;/p&gt;

&lt;p&gt;Composition says: this object gets behaviour because we gave it this component.&lt;/p&gt;

&lt;p&gt;That distinction might seem subtle in a small example. In a large codebase with dozens of classes and six levels of inheritance, it becomes the difference between a design that accommodates change and one that resists it.&lt;/p&gt;

&lt;p&gt;When you find yourself adding methods to a parent class to serve one specific child, or overriding methods just to disable inherited behaviour, or drawing an inheritance tree that keeps needing new branches, it's worth pausing.&lt;/p&gt;

&lt;p&gt;What if the object didn't inherit this behaviour at all? What if it was composed from the pieces it actually needs?&lt;/p&gt;

&lt;p&gt;That question, asked early enough, can save a lot of refactoring later.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>python</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
