<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>HAHWUL</title>
    <subtitle>Offensive Security Engineer, Developer and H4cker</subtitle>
    <link href="https://www.hahwul.com/feed.xml" rel="self" type="application/atom+xml"/>
    <link href="https://www.hahwul.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-12-27T00:00:00+00:00</updated>
    <id>https://www.hahwul.com/feed.xml</id>
    <entry xml:lang="en">
        <title>Hello SmuggleX 👋🏼</title>
        <published>2025-12-27T00:00:00+00:00</published>
        <updated>2025-12-27T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/blog/2025/hello-smugglex/" type="text/html"/>
        <id>https://www.hahwul.com/blog/2025/hello-smugglex/</id>
        <summary>Rust-powered HTTP Request Smuggling Scanner.</summary>
        <content type="html">&lt;p&gt;Hello! I&#x27;ve recently released a new tool I&#x27;ve been working on and shared it via &lt;a href=&quot;https:&#x2F;&#x2F;x.com&#x2F;hahwul&#x2F;status&#x2F;2004224255618420976&quot;&gt;𝕏&lt;&#x2F;a&gt;. It&#x27;s called smugglex, a tool designed to detect HTTP Request Smuggling.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;blog&#x2F;2025&#x2F;hello-smugglex&#x2F;top.jpg&quot; alt=&quot;&quot; &#x2F;&gt;
&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;smugglex&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;smugglex&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;&#x2F;h2&gt;
&lt;p&gt;It currently supports installation through various package managers like Homebrew, Snapcraft, Nix, Cargo, and more. Please refer to the &lt;a href=&quot;https:&#x2F;&#x2F;smugglex.hahwul.com&#x2F;getting-started&#x2F;installation&#x2F;&quot;&gt;Installation documentation&lt;&#x2F;a&gt; and install using your preferred method.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# e.g.,
&lt;&#x2F;span&gt;&lt;span&gt;cargo install smugglex
&lt;&#x2F;span&gt;&lt;span&gt;brew install hahwul&#x2F;smugglex&#x2F;smugglex
&lt;&#x2F;span&gt;&lt;span&gt;nix profile install github:hahwul&#x2F;smugglex
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For reference, I plan to align support for these package managers across all the tools I develop. Nix and others are already supported in recently updated tools (noir, smugglex), and dalfox (already applied in the v3 codebase), urx, and other tools will provide similar installation methods in the future.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;detect-smuggling&quot;&gt;Detect Smuggling&lt;&#x2F;h2&gt;
&lt;p&gt;This tool primarily aims to detect HTTP Request Smuggling. You can perform scans as shown below, targeting either a single target or multiple targets.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# Single target
&lt;&#x2F;span&gt;&lt;span&gt;smugglex https:&#x2F;&#x2F;target.com
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# Multiple targets
&lt;&#x2F;span&gt;&lt;span&gt;cat urls.txt &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;smugglex
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If smuggling is detected, it provides a sample request that you can use for testing, as shown below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;blog&#x2F;2025&#x2F;hello-smugglex&#x2F;sample.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exploiting&quot;&gt;Exploiting&lt;&#x2F;h2&gt;
&lt;p&gt;smugglex currently provides 2 exploit methods. When using this feature, it performs additional tests upon detecting smuggling.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;EXPLOIT:
&lt;&#x2F;span&gt;&lt;span&gt;  -e, --exploit &amp;lt;EXPLOIT&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          Exploit types to run after detection (comma-separated: localhost-access,path-fuzz)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For localhost-access, it performs functions to access various localhost information through smuggled requests.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;[*] Testing localhost access on port 80...
&lt;&#x2F;span&gt;&lt;span&gt;[*] Generated localhost payload for port 80
&lt;&#x2F;span&gt;&lt;span&gt;--- REQUEST ---
&lt;&#x2F;span&gt;&lt;span&gt;POST &#x2F; HTTP&#x2F;1.1
&lt;&#x2F;span&gt;&lt;span&gt;Host: 0a1100f0035b6b6380e0c60e005c00c4.web-security-academy.net
&lt;&#x2F;span&gt;&lt;span&gt;Connection: keep-alive
&lt;&#x2F;span&gt;&lt;span&gt;Content-Length: 6
&lt;&#x2F;span&gt;&lt;span&gt;Transfer-Encoding: chunked
&lt;&#x2F;span&gt;&lt;span&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;X
&lt;&#x2F;span&gt;&lt;span&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;GET &#x2F; HTTP&#x2F;1.1
&lt;&#x2F;span&gt;&lt;span&gt;Host: 127.0.0.1:80
&lt;&#x2F;span&gt;&lt;span&gt;Connection: close
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;path-fuzz explores backend paths using smuggled requests with a predefined list or a user-provided list (&lt;code&gt;--exploit-wordlist&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-plan&quot;&gt;Next Plan&lt;&#x2F;h2&gt;
&lt;p&gt;In fact, after sharing it, I&#x27;ve received a lot of interest and feedback via DMs. While features are important, for now, my priority is improving smuggling detection performance (increasing true positives and reducing false positives). I&#x27;ll continue expanding the exploit section, and ultimately, I hope it evolves beyond a simple scanner into a tool that covers the entire process of testing HTTP Request Smuggling.&lt;&#x2F;p&gt;
&lt;p&gt;If you try it out and have any suggestions for improvements, please feel free to let me know.
The year is already coming to an end! Wishing everyone a happy new year with lots of good fortune!&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Red, Blue, Purple in Offensive Security</title>
        <published>2025-10-05T00:00:00+00:00</published>
        <updated>2025-10-05T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/blog/2025/red-blue-purple/" type="text/html"/>
        <id>https://www.hahwul.com/blog/2025/red-blue-purple/</id>
        <summary>An honest reflection on the realities I’ve faced working as part of a Red Team</summary>
        <content type="html">&lt;p&gt;I work as a security engineer in a corporate Red Team position. What most people imagine is &quot;people who hack systems in flashy ways,” but the reality of working inside a company is much more complex. Is a corporate Red Team truly able to operate as a Red Team? To be blunt, I don’t think so. Today, I’d like to share my thoughts on the relationship between Red, Blue, and Purple Teams, and the struggles that come with it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;red-blue-purple&quot;&gt;Red, Blue, Purple&lt;&#x2F;h2&gt;
&lt;p&gt;Red, Blue, and Purple are the most common categories used in cybersecurity. Teams that perform penetration testing or offensive operations are called Red Teams. Departments responsible for defense, like CERT or internal monitoring, are called Blue Teams. When these two collaborate closely, the result is the concept of a Purple Team. Some also expand this idea into Yellow, Green, or Orange Teams to define other security functions.&lt;&#x2F;p&gt;
&lt;p&gt;I belong to a Red Team, but within the boundaries of a company, it’s not easy to operate in a truly &quot;Red” way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;red-team-in-company&quot;&gt;Red Team in Company&lt;&#x2F;h2&gt;
&lt;p&gt;The purpose of having a security team in a company is ultimately to strengthen security. Naturally, this tends to lean toward Blue objectives. Even when we use offensive techniques, the end result looks more Purple—or even closer to Indigo.&lt;&#x2F;p&gt;
&lt;p&gt;For example, even if we find a vulnerability, it doesn’t just end with &quot;This can be exploited.” We have to write reports, suggest mitigations tailored to the service, and even discuss architectural adjustments. Before long, this work feels closer to that of a general security engineer rather than a pure Red Teamer. That’s why I think it’s difficult to maintain a truly pure Red Team in a corporate environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;from-red-to-indigo&quot;&gt;From Red to Indigo&lt;&#x2F;h2&gt;
&lt;p&gt;Our skills may be Red, but the environment pushes us toward Purple, and eventually into Indigo. Red Team members—Offensive Security Engineers—are faced with this reality. If we simply adapt, we’ll gradually become more Blue, but it’s possible to maintain balance. On a personal level, that means consistently researching and studying to avoid losing touch with offensive skills. On an organizational or cultural level, it means creating opportunities to do more genuinely &quot;Red” work.&lt;&#x2F;p&gt;
&lt;p&gt;Recently, due to various security incidents in Korea, Red Teams are receiving more recognition. Some companies are even creating positions dedicated to genuine Red Team functions. I see this as a positive sign.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;go-purple&quot;&gt;Go Purple?&lt;&#x2F;h2&gt;
&lt;p&gt;By default, security engineers within a company inevitably lean toward the Blue Team side. In that situation, aiming for Purple often feels like heading further into Indigo. That’s why I believe that &quot;to truly become a Purple Teamer, you first need to go deeper into Red.&quot; A solid Red foundation is the only way to create a proper Purple balance.&lt;&#x2F;p&gt;
&lt;p&gt;After more than 10 years in this field, I feel that the offensive side of my work has gradually been shrinking each year. Of course, my ability to find vulnerabilities and my penetration skills have improved compared to before—but still, something feels lacking.&lt;&#x2F;p&gt;
&lt;p&gt;I have plenty to do until the end of the year (mostly development work, of course), but these thoughts will probably stay with me. There isn’t a simple answer, but for me, it comes down to one thing: never letting go of Red.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;blog&#x2F;2025&#x2F;red-blue-purple&#x2F;colors.jpg&quot; alt=&quot;&quot; &#x2F;&gt;
&lt;em&gt;Red, Blue, Purple by Gemini&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Beginner’s Guide: Creating Your First Ruby Project</title>
        <published>2025-09-21T00:00:00+00:00</published>
        <updated>2025-09-21T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/dev/ruby/noob-beginners-guide-in-ruby/" type="text/html"/>
        <id>https://www.hahwul.com/dev/ruby/noob-beginners-guide-in-ruby/</id>
        <summary>A step-by-step tutorial to install Ruby and create, run, test, and build your first gem project.</summary>
        <content type="html">&lt;p&gt;Getting started with Ruby is quick if you follow a few simple steps. In this guide, you’ll install Ruby, scaffold a new gem project, write a tiny program, run tests, and produce a gem build.&lt;&#x2F;p&gt;
&lt;p&gt;I’ll keep it minimal and practical so you can get productive fast.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-install-ruby&quot;&gt;1) Install Ruby&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Using RVM (Ruby Version Manager, macOS&#x2F;Linux):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;curl -sSL https:&#x2F;&#x2F;get.rvm.io &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;bash -s stable
&lt;&#x2F;span&gt;&lt;span&gt;source ~&#x2F;.rvm&#x2F;scripts&#x2F;rvm
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Install Ruby:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;rvm install ruby --latest
&lt;&#x2F;span&gt;&lt;span&gt;rvm use ruby --default
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Verify:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;ruby --version
&lt;&#x2F;span&gt;&lt;span&gt;gem --version
&lt;&#x2F;span&gt;&lt;span&gt;bundler --version
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;2-create-a-new-project&quot;&gt;2) Create a new project&lt;&#x2F;h2&gt;
&lt;p&gt;Ruby uses &lt;code&gt;bundler&lt;&#x2F;code&gt; for dependency management and project scaffolding.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Gem library:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;bundle gem my_gem
&lt;&#x2F;span&gt;&lt;span&gt;cd my_gem
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a basic structure:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;my_gem&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├─ Gemfile
&lt;&#x2F;span&gt;&lt;span&gt;├─ lib&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;│   └─ my_gem.rb
&lt;&#x2F;span&gt;&lt;span&gt;├─ my_gem.gemspec
&lt;&#x2F;span&gt;&lt;span&gt;└─ spec&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    └─ my_gem_spec.rb
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;3-write-your-first-program&quot;&gt;3) Write your first program&lt;&#x2F;h2&gt;
&lt;p&gt;Open &lt;code&gt;lib&#x2F;my_gem.rb&lt;&#x2F;code&gt; and define a simple module:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span&gt;MyGem
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span&gt;self.greet(name = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Ruby&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, &lt;&#x2F;span&gt;&lt;span&gt;#{name}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;ruby -e &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;require_relative &amp;#39;lib&#x2F;my_gem.rb&amp;#39;; puts MyGem.greet&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;4-manage-dependencies&quot;&gt;4) Manage dependencies&lt;&#x2F;h2&gt;
&lt;p&gt;If you add dependencies to &lt;code&gt;Gemfile&lt;&#x2F;code&gt;, install them:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;bundle install
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You generally won’t need this for the very first “hello world,” but it’s essential once you pull in libraries like HTTP clients or web frameworks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-format-your-code&quot;&gt;5) Format your code&lt;&#x2F;h2&gt;
&lt;p&gt;Install and use RuboCop:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;gem install rubocop
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Lint:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;rubocop
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Auto-fix:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;rubocop -a
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;6-add-a-simple-test-rspec&quot;&gt;6) Add a simple test (RSpec)&lt;&#x2F;h2&gt;
&lt;p&gt;Edit &lt;code&gt;spec&#x2F;my_gem_spec.rb&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;my_gem&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;RSpec.describe MyGem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;greets with default name&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    expect(MyGem.greet).to eq &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, Ruby!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;greets a custom name&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    expect(MyGem.greet(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;World&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).to eq &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, World!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run tests:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;bundle exec rspec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# or verbose:
&lt;&#x2F;span&gt;&lt;span&gt;bundle exec rspec -v
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;7-build-a-gem&quot;&gt;7) Build a gem&lt;&#x2F;h2&gt;
&lt;p&gt;Build your gem:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;gem build my_gem.gemspec
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Test install:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;gem install .&#x2F;my_gem-0.1.0.gem
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;8-check-dependencies-graph-nice-debugging-tool&quot;&gt;8) Check dependencies graph (nice debugging tool)&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;bundle viz
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;9-optional-lint-with-rubocop&quot;&gt;9) Optional: Lint with RuboCop&lt;&#x2F;h2&gt;
&lt;p&gt;As mentioned, for more config:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;rubocop --auto-gen-config
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;visual-overview&quot;&gt;Visual overview&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s a simple flow of your first project:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 flowchart LR
  A[Install Ruby] --&amp;gt; B[bundle gem my_gem]
  B --&amp;gt; C[Edit lib&amp;#x2F;my_gem.rb]
  C --&amp;gt; D[ruby run]
  D --&amp;gt; E[bundle exec rspec]
  E --&amp;gt; F[rubocop]
  F --&amp;gt; G[gem build]
  G --&amp;gt; H[gem install]
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What’s next?&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Explore popular gems:
&lt;ul&gt;
&lt;li&gt;Web frameworks: Sinatra, Rails&lt;&#x2F;li&gt;
&lt;li&gt;Testing: RSpec&lt;&#x2F;li&gt;
&lt;li&gt;HTTP client: Faraday&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Build a gem, a CLI tool, or a Rails app and publish it on RubyGems.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You’ve just created, tested, and built your first Ruby gem. Keep it small, iterate quickly, and enjoy the flexibility of Ruby.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Beginner’s Guide: Creating Your First Rust Project</title>
        <published>2025-09-21T00:00:00+00:00</published>
        <updated>2025-09-21T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/dev/rust/noob-beginners-guide-in-rust/" type="text/html"/>
        <id>https://www.hahwul.com/dev/rust/noob-beginners-guide-in-rust/</id>
        <summary>A step-by-step tutorial to install Rust and create, run, test, and build your first project using Cargo.</summary>
        <content type="html">&lt;p&gt;Getting started with Rust is quick if you follow a few simple steps. In this guide, you’ll install Rust, scaffold a new project, write a tiny program, run tests, and produce a release build.&lt;&#x2F;p&gt;
&lt;p&gt;I’ll keep it minimal and practical so you can get productive fast.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-install-rust&quot;&gt;1) Install Rust&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Official installer (works on all platforms):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;curl --proto &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;=https&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; --tlsv1.2 -sSf https:&#x2F;&#x2F;sh.rustup.rs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;sh
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Verify&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;rustc --version
&lt;&#x2F;span&gt;&lt;span&gt;cargo --version
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;2-create-a-new-project&quot;&gt;2) Create a new project&lt;&#x2F;h2&gt;
&lt;p&gt;Rust uses &lt;code&gt;cargo&lt;&#x2F;code&gt; for building, testing, and dependency management.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Binary app (recommended for CLI apps and services):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo new hello_rust
&lt;&#x2F;span&gt;&lt;span&gt;cd hello_rust
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a basic structure:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;hello_rust&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├─ Cargo.toml
&lt;&#x2F;span&gt;&lt;span&gt;└─ src&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;   └─ main.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;3-write-your-first-program&quot;&gt;3) Write your first program&lt;&#x2F;h2&gt;
&lt;p&gt;Open &lt;code&gt;src&#x2F;main.rs&lt;&#x2F;code&gt; and print a greeting:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span&gt;main() {
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, Rust!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo run
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;4-manage-dependencies-when-you-need-them&quot;&gt;4) Manage dependencies (when you need them)&lt;&#x2F;h2&gt;
&lt;p&gt;If you add dependencies to &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt;, build to install them:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo build
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You generally won’t need this for the very first “hello world,” but it’s essential once you pull in crates like HTTP clients or web frameworks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-format-your-code&quot;&gt;5) Format your code&lt;&#x2F;h2&gt;
&lt;p&gt;Rust ships a formatter:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo fmt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run it from the project root to format all sources.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;6-add-a-simple-test&quot;&gt;6) Add a simple test&lt;&#x2F;h2&gt;
&lt;p&gt;For a beginner-friendly pattern, wrap your code in a module and test it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; src&#x2F;main.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span&gt;hello_rust {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span&gt;greet(name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;&amp;amp;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;        format!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b4cea8;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, name)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span&gt;main() {
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b4cea8;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, hello_rust::greet(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Rust&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[cfg(test)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span&gt;tests {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;use super&lt;&#x2F;span&gt;&lt;span&gt;::*;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    #[test]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span&gt;test_greet_default() {
&lt;&#x2F;span&gt;&lt;span&gt;        assert_eq!(hello_rust::greet(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Rust&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, Rust!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    #[test]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span&gt;test_greet_custom() {
&lt;&#x2F;span&gt;&lt;span&gt;        assert_eq!(hello_rust::greet(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;World&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, World!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run tests:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# or verbose:
&lt;&#x2F;span&gt;&lt;span&gt;cargo test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt; --&lt;&#x2F;span&gt;&lt;span&gt; --nocapture
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;7-build-a-binary&quot;&gt;7) Build a binary&lt;&#x2F;h2&gt;
&lt;p&gt;Two common ways to build:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Debug build:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo build
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Release build:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo build --release
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run your binary:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;.&#x2F;target&#x2F;debug&#x2F;hello_rust  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# debug
&lt;&#x2F;span&gt;&lt;span&gt;.&#x2F;target&#x2F;release&#x2F;hello_rust  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# release
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;8-check-dependencies-graph-nice-debugging-tool&quot;&gt;8) Check dependencies graph (nice debugging tool)&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo tree
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;9-optional-lint-with-clippy&quot;&gt;9) Optional: Lint with Clippy&lt;&#x2F;h2&gt;
&lt;p&gt;Clippy is Rust’s official linter.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Lint:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo clippy
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Auto-fix:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo clippy --fix
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;visual-overview&quot;&gt;Visual overview&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s a simple flow of your first project:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 flowchart LR
  A[Install Rust] --&amp;gt; B[cargo new hello_rust]
  B --&amp;gt; C[Edit src&amp;#x2F;main.rs]
  C --&amp;gt; D[cargo run]
  D --&amp;gt; E[cargo test]
  E --&amp;gt; F[cargo fmt]
  F --&amp;gt; G[cargo build --release]
  G --&amp;gt; H[Run .&amp;#x2F;target&amp;#x2F;release&amp;#x2F;hello_rust]
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What’s next?&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Explore popular crates:
&lt;ul&gt;
&lt;li&gt;Web frameworks: Rocket, Actix-Web&lt;&#x2F;li&gt;
&lt;li&gt;CLI: clap&lt;&#x2F;li&gt;
&lt;li&gt;HTTP client: reqwest&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Build a CLI tool, a web service, or a small library and publish it on crates.io.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You’ve just created, tested, and built your first Rust project. Keep it small, iterate quickly, and enjoy the safety and performance of Rust.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Beginner’s Guide: Creating Your First Crystal Project</title>
        <published>2025-09-16T00:00:00+00:00</published>
        <updated>2025-09-16T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/dev/crystal/noob-beginners-guide-in-crystal/" type="text/html"/>
        <id>https://www.hahwul.com/dev/crystal/noob-beginners-guide-in-crystal/</id>
        <summary>A step-by-step tutorial to install Crystal and create, run, test, and build your first project using shards.</summary>
        <content type="html">&lt;p&gt;Getting started with Crystal is quick if you follow a few simple steps. In this guide, you’ll install Crystal, scaffold a new project, write a tiny program, run tests, and produce a release build.&lt;&#x2F;p&gt;
&lt;p&gt;I’ll keep it minimal and practical so you can get productive fast.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-install-crystal&quot;&gt;1) Install Crystal&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;macOS (Homebrew)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;brew install crystal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Linux&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Check the official installation page for distro-specific instructions:
https:&#x2F;&#x2F;crystal-lang.org&#x2F;install&#x2F;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Verify&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;crystal --version
&lt;&#x2F;span&gt;&lt;span&gt;shards --version
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Optional tools you’ll probably want soon:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;VS Code extension (Crystal + LSP): https:&#x2F;&#x2F;marketplace.visualstudio.com&#x2F;items?itemName=crystal-lang-tools.crystal-lang&lt;&#x2F;li&gt;
&lt;li&gt;Linter (Ameba): https:&#x2F;&#x2F;marketplace.visualstudio.com&#x2F;items?itemName=veelenga.crystal-ameba&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;2-create-a-new-project&quot;&gt;2) Create a new project&lt;&#x2F;h2&gt;
&lt;p&gt;Crystal uses &lt;code&gt;shards&lt;&#x2F;code&gt; (like Ruby’s Bundler) for dependency management and project scaffolding.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;App template (recommended for CLI apps and services):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;mkdir hello_crystal &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span&gt;cd hello_crystal
&lt;&#x2F;span&gt;&lt;span&gt;shards init app hello_crystal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Library template (if you’re building a shard&#x2F;gem-like package):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;mkdir my_lib &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span&gt;cd my_lib
&lt;&#x2F;span&gt;&lt;span&gt;shards init lib my_lib
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a basic structure (for an app):&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;hello_crystal&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├─ shard.yml
&lt;&#x2F;span&gt;&lt;span&gt;└─ src&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;   └─ hello_crystal.cr
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;3-write-your-first-program&quot;&gt;3) Write your first program&lt;&#x2F;h2&gt;
&lt;p&gt;Open &lt;code&gt;src&#x2F;hello_crystal.cr&lt;&#x2F;code&gt; and print a greeting:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# src&#x2F;hello_crystal.cr
&lt;&#x2F;span&gt;&lt;span&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, Crystal!&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;crystal run src&#x2F;hello_crystal.cr
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;4-manage-dependencies-when-you-need-them&quot;&gt;4) Manage dependencies (when you need them)&lt;&#x2F;h2&gt;
&lt;p&gt;If you add dependencies to &lt;code&gt;shard.yml&lt;&#x2F;code&gt;, install them with:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;shards install
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You generally won’t need this for the very first “hello world,” but it’s essential once you pull in libraries like HTTP clients or web frameworks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-format-your-code&quot;&gt;5) Format your code&lt;&#x2F;h2&gt;
&lt;p&gt;Crystal ships a formatter:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;crystal tool format
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run it from the project root to format all sources.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;6-add-a-simple-test-spec&quot;&gt;6) Add a simple test (spec)&lt;&#x2F;h2&gt;
&lt;p&gt;Create a spec file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# spec&#x2F;hello_crystal_spec.cr
&lt;&#x2F;span&gt;&lt;span&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;spec&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;..&#x2F;src&#x2F;hello_crystal&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;hello_crystal&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; do
&lt;&#x2F;span&gt;&lt;span&gt;  it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;runs without raising&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; do
&lt;&#x2F;span&gt;&lt;span&gt;    expect { Crystal.main }.not_to raise_error
&lt;&#x2F;span&gt;&lt;span&gt;  end
&lt;&#x2F;span&gt;&lt;span&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If your app doesn’t define &lt;code&gt;Crystal.main&lt;&#x2F;code&gt;, you can test a function you write instead. For a beginner-friendly pattern, wrap your app code in a module:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# src&#x2F;hello_crystal.cr
&lt;&#x2F;span&gt;&lt;span&gt;module HelloCrystal
&lt;&#x2F;span&gt;&lt;span&gt;  def self.greet(name : String = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Crystal&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, #{name}!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  end
&lt;&#x2F;span&gt;&lt;span&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;puts HelloCrystal.greet
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And test it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# spec&#x2F;hello_crystal_spec.cr
&lt;&#x2F;span&gt;&lt;span&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;spec&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;..&#x2F;src&#x2F;hello_crystal&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;describe HelloCrystal do
&lt;&#x2F;span&gt;&lt;span&gt;  it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;greets with default name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; do
&lt;&#x2F;span&gt;&lt;span&gt;    HelloCrystal.greet.should eq &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, Crystal!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;greets a custom name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; do
&lt;&#x2F;span&gt;&lt;span&gt;    HelloCrystal.greet(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;World&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).should eq &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello, World!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  end
&lt;&#x2F;span&gt;&lt;span&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run tests:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;crystal spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# or verbose:
&lt;&#x2F;span&gt;&lt;span&gt;crystal spec -v
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;7-build-a-binary&quot;&gt;7) Build a binary&lt;&#x2F;h2&gt;
&lt;p&gt;Two common ways to build:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Using crystal directly:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;crystal build --no-debug --release src&#x2F;hello_crystal.cr -o bin&#x2F;hello_crystal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Using shards (works when &lt;code&gt;shard.yml&lt;&#x2F;code&gt; defines targets):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;shards build --no-debug --release
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run your binary:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;.&#x2F;bin&#x2F;hello_crystal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For production-like builds (strict dependencies and no dev deps):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;shards build --release --no-debug --production
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;8-check-dependencies-graph-nice-debugging-tool&quot;&gt;8) Check dependencies graph (nice debugging tool)&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;crystal tool dependencies .&#x2F;src&#x2F;hello_crystal.cr
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;9-optional-lint-with-ameba&quot;&gt;9) Optional: Lint with Ameba&lt;&#x2F;h2&gt;
&lt;p&gt;Ameba is Crystal’s code style linter (similar to RuboCop).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Install (macOS example):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;brew tap crystal-ameba&#x2F;ameba
&lt;&#x2F;span&gt;&lt;span&gt;brew install ameba
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Lint:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;ameba
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Auto-fix:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;ameba --fix
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;visual-overview&quot;&gt;Visual overview&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s a simple flow of your first project:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 flowchart LR
  A[Install Crystal] --&amp;gt; B[shards init app hello_crystal]
  B --&amp;gt; C[Edit src&amp;#x2F;hello_crystal.cr]
  C --&amp;gt; D[crystal run]
  D --&amp;gt; E[crystal spec]
  E --&amp;gt; F[crystal tool format]
  F --&amp;gt; G[shards build --release]
  G --&amp;gt; H[Run .&amp;#x2F;bin&amp;#x2F;hello_crystal]
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What’s next?&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Explore popular shards:
&lt;ul&gt;
&lt;li&gt;Web frameworks: Kemal (Sinatra-like), Lucky and Amber (Rails-like)&lt;&#x2F;li&gt;
&lt;li&gt;Linting: Ameba&lt;&#x2F;li&gt;
&lt;li&gt;HTTP client: Crest&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Build a CLI tool, a web service, or a small library and publish it as a shard.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You’ve just created, tested, and built your first Crystal project. Keep it small, iterate quickly, and enjoy the speed and expressiveness of Crystal.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>How to Hack Web Assembly</title>
        <published>2025-09-12T00:00:00+00:00</published>
        <updated>2025-09-12T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/sec/how-to-hack/web-assembly/" type="text/html"/>
        <id>https://www.hahwul.com/sec/how-to-hack/web-assembly/</id>
        <content type="html">&lt;p&gt;WebAssembly is a low-level assembly language that can process binary formats on the web. It runs on the web but feels like native execution, and can work together with JavaScript to achieve both high-performance processing and rapid implementation.&lt;&#x2F;p&gt;
&lt;p&gt;This document outlines methods for testing and finding security vulnerabilities in WebAssembly applications.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;structure&quot;&gt;Structure&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;webassembly-overview&quot;&gt;WebAssembly Overview&lt;&#x2F;h4&gt;
&lt;p&gt;WebAssembly is a low-level assembly language that can process binary formats on the web. It works together with JavaScript and has the following characteristics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Near-native performance&lt;&#x2F;li&gt;
&lt;li&gt;Interoperability with JavaScript&lt;&#x2F;li&gt;
&lt;li&gt;Safe execution in browser sandbox environments&lt;&#x2F;li&gt;
&lt;li&gt;Compilable from various languages like C, C++, Rust, etc.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;3.bp.blogspot.com&#x2F;-XnKxEJq5EeU&#x2F;W7j6RNjT7pI&#x2F;AAAAAAAADdI&#x2F;mcfmbNwF03YaaSWyfDBEzSnQi71sGtY-ACLcBGAs&#x2F;s1600&#x2F;1415.png&quot; alt=&quot;WebAssembly Structure&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;basic-example&quot;&gt;Basic Example&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;c&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span style=&quot;color:#9b9b9b;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;lt;stdio.h&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b9b9b;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;lt;sys&#x2F;uio.h&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b9b9b;&quot;&gt;#define WASM_EXPORT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;__attribute__&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b9b9b;&quot;&gt;((visibility(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;default&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b9b9b;&quot;&gt;)))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;WASM_EXPORT
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;int &lt;&#x2F;span&gt;&lt;span&gt;main(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  printf(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;Hello World&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e3bbab;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;* External function that is implemented in JavaScript. *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;extern void &lt;&#x2F;span&gt;&lt;span&gt;putc_js(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;char &lt;&#x2F;span&gt;&lt;span&gt;c);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;* Basic implementation of the writev sys call. *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;WASM_EXPORT
&lt;&#x2F;span&gt;&lt;span&gt;size_t writev_c(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;int &lt;&#x2F;span&gt;&lt;span&gt;fd, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const struct&lt;&#x2F;span&gt;&lt;span&gt; iovec &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;iov, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;int &lt;&#x2F;span&gt;&lt;span&gt;iovcnt) {
&lt;&#x2F;span&gt;&lt;span&gt;  size_t cnt = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; i = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; i &amp;lt; iovcnt; i++) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; j = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; j &amp;lt; iov[i].iov_len; j++) {
&lt;&#x2F;span&gt;&lt;span&gt;      putc_js(((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;char *&lt;&#x2F;span&gt;&lt;span&gt;)iov[i].iov_base)[j]);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    cnt += iov[i].iov_len;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; cnt;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;api-documents&quot;&gt;API Documents&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;WebAssembly: &lt;a href=&quot;https:&#x2F;&#x2F;webassembly.org&#x2F;docs&#x2F;&quot;&gt;https:&#x2F;&#x2F;webassembly.org&#x2F;docs&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;MDN WebAssembly: &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;WebAssembly&quot;&gt;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;WebAssembly&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;WebAssembly Text Format: &lt;a href=&quot;https:&#x2F;&#x2F;webassembly.github.io&#x2F;spec&#x2F;core&#x2F;text&#x2F;index.html&quot;&gt;https:&#x2F;&#x2F;webassembly.github.io&#x2F;spec&#x2F;core&#x2F;text&#x2F;index.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;hack-mechanism&quot;&gt;Hack Mechanism&lt;&#x2F;h2&gt;
&lt;p&gt;WebAssembly operates the same way in each browser, creating an environment where both traditional binary attacks and web hacking techniques can be applied.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;security-testing-points&quot;&gt;Security Testing Points&lt;&#x2F;h3&gt;
&lt;p&gt;WebAssembly applications have the following security testing points:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Traditional binary-related attacks&lt;&#x2F;strong&gt;: Known techniques like BOF, FSB, OOB can be applied, but WebAssembly has built-in security logic making techniques like ROP difficult.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Web attacks&lt;&#x2F;strong&gt;: The difference from existing web attacks is that web payloads or attack code can pass through the C layer.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Indirect function calls&lt;&#x2F;strong&gt;: When gaining JS control through XSS, the attack vectors expand significantly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;code-audit&quot;&gt;Code audit&lt;&#x2F;h3&gt;
&lt;p&gt;WebAssembly files typically have a &lt;code&gt;.wasm&lt;&#x2F;code&gt; extension and are provided in binary format. For analysis, you need to decompile them and examine the related JavaScript code together.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;find-wasm-files&quot;&gt;Find WASM files&lt;&#x2F;h4&gt;
&lt;p&gt;Methods to find WebAssembly files:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Finding WebAssembly instances in JavaScript&lt;&#x2F;strong&gt;: Like SWF and ActiveX, since they&#x27;re ultimately handled in JS, function or address information remains in the code.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tracking WebAssembly loading functions&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Using instantiateStreaming
&lt;&#x2F;span&gt;&lt;span&gt;WebAssembly.instantiateStreaming(fetch(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;simple.wasm&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;), importObject)
&lt;&#x2F;span&gt;&lt;span&gt;.then(results &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Do something with the results!
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Using XMLHttpRequest
&lt;&#x2F;span&gt;&lt;span&gt;request = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#4ec9b0;&quot;&gt;XMLHttpRequest&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;request.open(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;GET&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;simple.wasm&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;request.responseType = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;arraybuffer&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;request.send();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;request.onload = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;bytes = request.response;
&lt;&#x2F;span&gt;&lt;span&gt;  WebAssembly.instantiate(bytes, importObject).then(results &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    results.instance.exports.exported_func();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Browser Developer Tools&lt;&#x2F;strong&gt;: You can directly check wasm files in the Sources tab.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;wasm-file-structure&quot;&gt;WASM File Structure&lt;&#x2F;h4&gt;
&lt;p&gt;WASM file headers start with &lt;code&gt;.asm&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;hexdump -C fail.wasm
&lt;&#x2F;span&gt;&lt;span&gt;00000000  00 61 73 6d 01 00 00 00  01 85 80 80 80 00 01 60  |.asm...........`|
&lt;&#x2F;span&gt;&lt;span&gt;00000010  00 01 7f 03 82 80 80 80  00 01 00 06 81 80 80 80  |................|
&lt;&#x2F;span&gt;&lt;span&gt;00000020  00 00 07 8b 80 80 80 00  01 07 66 61 69 6c 5f 6d  |..........fail_m|
&lt;&#x2F;span&gt;&lt;span&gt;00000030  65 00 00 0a 8d 80 80 80  00 01 87 80 80 80 00 00  |e...............|
&lt;&#x2F;span&gt;&lt;span&gt;00000040  41 01 41 00 6d 0b                                 |A.A.m.|
&lt;&#x2F;span&gt;&lt;span&gt;00000046
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;decompile-wasm-find-info&quot;&gt;Decompile WASM &amp;amp; find info&lt;&#x2F;h4&gt;
&lt;p&gt;Installing tools for WASM decompilation:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;git clone --recursive https:&#x2F;&#x2F;github.com&#x2F;WebAssembly&#x2F;wabt
&lt;&#x2F;span&gt;&lt;span&gt;apt install clang     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# (if clang is not available)
&lt;&#x2F;span&gt;&lt;span&gt;make
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Object dump&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;.&#x2F;wasm-objdump -xd fail.wasm
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;fail.wasm:    file format wasm 0x1
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Section Details:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Type[1]:
&lt;&#x2F;span&gt;&lt;span&gt;- type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; () -&amp;gt; i32
&lt;&#x2F;span&gt;&lt;span&gt;Function[1]:
&lt;&#x2F;span&gt;&lt;span&gt;- func&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; sig=0 &amp;lt;fail_me&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Global[0]:
&lt;&#x2F;span&gt;&lt;span&gt;Export[1]:
&lt;&#x2F;span&gt;&lt;span&gt;- func&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;] &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;fail_me&amp;gt; -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;fail_me&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Code Disassembly:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;00003a &amp;lt;fail_me&amp;gt;:
&lt;&#x2F;span&gt;&lt;span&gt;000040: 41 01                      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;i32.const 1
&lt;&#x2F;span&gt;&lt;span&gt;000042: 41 00                      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;i32.const 0
&lt;&#x2F;span&gt;&lt;span&gt;000044: 6d                         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;i32.div_s
&lt;&#x2F;span&gt;&lt;span&gt;000045: 0b                         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Decompile to C&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;.&#x2F;wasm2c fail.wasm
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;live-audit&quot;&gt;Live audit&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;test-with-devtools&quot;&gt;Test with devtools&lt;&#x2F;h4&gt;
&lt;p&gt;You can access WebAssembly objects in the browser&#x27;s developer tools:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Check WebAssembly object
&lt;&#x2F;span&gt;&lt;span&gt;WebAssembly
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; CompileError: function CompileError()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Global: function Global()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Instance: function Instance()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; LinkError: function LinkError()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Memory: function Memory()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Module: function Module()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; RuntimeError: function RuntimeError()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Table: function Table()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; compile: function compile()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; compileStreaming: function compileStreaming()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; instantiate: function instantiate()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; instantiateStreaming: function instantiateStreaming()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; validate: function validate()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;function-testing&quot;&gt;Function testing&lt;&#x2F;h4&gt;
&lt;p&gt;You can directly call WASM functions from JavaScript for testing. This is identical to SWF or ActiveX analysis methods.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;4.bp.blogspot.com&#x2F;-8-VATL857N8&#x2F;W7j6bL8UhWI&#x2F;AAAAAAAADdM&#x2F;6L4q2M1SdBQZDlNEIMQ5om9H3eiFDYVoACLcBGAs&#x2F;s1600&#x2F;Selection_026.png&quot; alt=&quot;Function testing&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-major-weakness&quot;&gt;A major weakness&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;memory-safety-issues&quot;&gt;Memory Safety Issues&lt;&#x2F;h4&gt;
&lt;p&gt;While WebAssembly provides memory safety, the following vulnerabilities can still occur:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Buffer overflow in linear memory&lt;&#x2F;li&gt;
&lt;li&gt;Integer overflow&#x2F;underflow&lt;&#x2F;li&gt;
&lt;li&gt;Out-of-bounds memory access&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;javascript-interface-vulnerabilities&quot;&gt;JavaScript Interface Vulnerabilities&lt;&#x2F;h4&gt;
&lt;p&gt;Vulnerabilities that can occur at the interface between WASM and JavaScript:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Type confusion in parameter passing&lt;&#x2F;li&gt;
&lt;li&gt;Improper input validation&lt;&#x2F;li&gt;
&lt;li&gt;Memory corruption through shared memory&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;sop-same-origin-policy-issues&quot;&gt;SOP (Same-Origin Policy) Issues&lt;&#x2F;h4&gt;
&lt;p&gt;When fetching WASM files using fetch, etc., the browser&#x27;s SOP is forcibly applied:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;fetch(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;https:&#x2F;&#x2F;www.hahwul.com&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Cross-origin request blocked: Same-origin policy blocks remote resources
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, since WASM can pass data to JavaScript:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Check CORS application scope for potential SOP bypass&lt;&#x2F;li&gt;
&lt;li&gt;Verify if file upload is possible on the same domain (possibility of loading attacker-reconstructed WASM files)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;tools-environment&quot;&gt;🛠 Environment&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;essential-tools&quot;&gt;Essential Tools&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;WABT (WebAssembly Binary Toolkit)&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;git clone --recursive https:&#x2F;&#x2F;github.com&#x2F;WebAssembly&#x2F;wabt
&lt;&#x2F;span&gt;&lt;span&gt;make
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Included tools:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wasm-objdump&lt;&#x2F;code&gt;: WASM file structure analysis&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;wasm2c&lt;&#x2F;code&gt;: Decompile WASM to C code&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;wasm2wat&lt;&#x2F;code&gt;: Convert WASM to text format&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;wat2wasm&lt;&#x2F;code&gt;: Convert text format to WASM&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Emscripten&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;emscripten-core&#x2F;emsdk.git
&lt;&#x2F;span&gt;&lt;span&gt;cd emsdk
&lt;&#x2F;span&gt;&lt;span&gt;.&#x2F;emsdk install latest
&lt;&#x2F;span&gt;&lt;span&gt;.&#x2F;emsdk activate latest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Browser DevTools&lt;&#x2F;strong&gt;: All major browsers support WebAssembly debugging&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-methodology&quot;&gt;Testing Methodology&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Static Analysis&lt;&#x2F;strong&gt;: WASM file analysis using WABT tools&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Analysis&lt;&#x2F;strong&gt;: Runtime analysis through browser developer tools&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Interface Testing&lt;&#x2F;strong&gt;: Data flow analysis between JavaScript-WASM&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Memory Analysis&lt;&#x2F;strong&gt;: Linear memory access pattern verification&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;articles&quot;&gt;Articles&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;2018&#x2F;10&#x2F;06&#x2F;hacking-security-analysis-web-assembly&#x2F;&quot;&gt;How to analyze security vulnerabilities in WebAssembly?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;webassembly.org&#x2F;docs&#x2F;security&#x2F;&quot;&gt;WebAssembly Security&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;i.blackhat.com&#x2F;us-18&#x2F;Thu-August-9&#x2F;us-18-Lukasiewicz-WebAssembly-A-New-World-of-Native_Exploits-On-The-Web-wp.pdf&quot;&gt;BlackHat WebAssembly Paper&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;WebAssembly&quot;&gt;MDN WebAssembly&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WebAssembly&#x2F;wabt&quot;&gt;WebAssembly Binary Toolkit&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>OWASP Seoul Meetup</title>
        <published>2025-09-07T00:00:00+00:00</published>
        <updated>2025-09-07T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/blog/2025/owasp-seoul-meetup/" type="text/html"/>
        <id>https://www.hahwul.com/blog/2025/owasp-seoul-meetup/</id>
        <summary>2025.08.26 OWASP Seoul Meetup Recap</summary>
        <content type="html">&lt;p&gt;At the recent &lt;a href=&quot;https:&#x2F;&#x2F;owasp.org&#x2F;www-chapter-seoul&#x2F;&quot;&gt;OWASP Seoul&lt;&#x2F;a&gt; Meetup, I had the opportunity to speak on the topic of open source. I was a bit worried that it might be a familiar topic and lead to a mundane discussion, but thanks to the enthusiastic attendees, it turned out to be a truly memorable time.&lt;&#x2F;p&gt;
&lt;p&gt;Both work and personal life have been so busy that time flew by quickly. Though a bit late, I’m jotting down some memories from that day.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1&quot;&gt;#1&lt;&#x2F;h2&gt;
&lt;p&gt;First, the passion at the venue was truly impressive. Despite the hot weather and the evening timing, I was deeply moved by everyone who took the time to attend. After the event, someone in the open chat room said, &lt;code&gt;I&#x27;m super pumped! Gonna head home and jump straight into GitHub!&lt;&#x2F;code&gt; which actually gave me the spark to get fired up. Thanks to that, I’ve been making a lot of commits and releases lately. :D&lt;&#x2F;p&gt;
&lt;div class=&quot;gallery-box&quot;&gt;
  &lt;div class=&quot;gallery&quot;&gt;
    &lt;img src=&quot;images&#x2F;burn.jpg&quot; alt=&quot;Burning&quot;&gt;
    &lt;img src=&quot;images&#x2F;noir.jpg&quot; alt=&quot;Noir&quot;&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Though there wasn’t much time after my presentation, I had the chance to chat with various people. To be honest, it was a time where I gained a lot myself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2&quot;&gt;#2&lt;&#x2F;h2&gt;
&lt;p&gt;Personally, I felt my presentation left something to be desired, but listening to the next talk, “Gather in the Forest of Translation,” completely washed away those thoughts. I thoroughly enjoyed the presentation, and I could relate to the pain points about reviews (though in my case, it’s not people but Agents 🤣). I picked up a few useful tricks from it. Also, I’m cheering for the ongoing &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;OWASP&#x2F;ASVS&#x2F;pull&#x2F;3216&quot;&gt;PR&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3&quot;&gt;#3&lt;&#x2F;h2&gt;
&lt;p&gt;Lastly, I want to express my heartfelt gratitude to the OWASP Seoul Chapter for organizing this wonderful opportunity and to everyone who attended. I also want to give another big thank you to &lt;a href=&quot;https:&#x2F;&#x2F;x.com&#x2F;psiinon&quot;&gt;Simon&lt;&#x2F;a&gt; for leaving such great comments for the Korean security&#x2F;open-source community.&lt;&#x2F;p&gt;
&lt;p&gt;I’ll make sure to attend again from time to time, so let’s meet at the next one!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;blog&#x2F;2025&#x2F;owasp-seoul-meetup&#x2F;images&#x2F;owasp.jpg&quot; alt=&quot;&quot; &#x2F;&gt;
&lt;em&gt;Thank you for the gift!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Search in Zola: Fuse.js vs. Elasticlunr.js</title>
        <published>2025-07-26T00:00:00+00:00</published>
        <updated>2025-07-26T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/dev/zola/search-in-zola/" type="text/html"/>
        <id>https://www.hahwul.com/dev/zola/search-in-zola/</id>
        <summary>Comparing Fuse.js and Elasticlunr.js for Zola’s client-side search. Learn which library suits your static site’s needs.</summary>
        <content type="html">&lt;p&gt;Zola is a fast and lightweight static site generator. To implement client-side search functionality without a server, you need a suitable JavaScript library. Zola supports generating index formats based on Fuse and Elasticlunr for search, making it easy to implement search features.&lt;&#x2F;p&gt;
&lt;p&gt;Both are lightweight and operate without dependencies, but they differ in Korean language support and ease of use. In this article, I&#x27;ll discuss the features, performance, Korean support of these two libraries, and why I chose Fuse.js.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-fuse-js-and-elasticlunr-js&quot;&gt;What is Fuse.js and Elasticlunr.js?&lt;&#x2F;h2&gt;
&lt;p&gt;To add search functionality in Zola, you need a client-side JavaScript library. Fuse.js and Elasticlunr.js are popular options, each strong in fuzzy search and full-text search, respectively.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Library&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Features&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Pros&#x2F;Cons&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Fuse.js&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Library specialized in fuzzy search&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Pros: Strong in handling typos or partial matches, simple setup for quick implementation&lt;br&gt;Cons: Does not support complex search features&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Elasticlunr.js&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Supports full-text search&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Pros: Provides advanced features like Boolean queries, TF&#x2F;IDF ranking&lt;br&gt;Cons: Requires additional setup for Korean search&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;why-compare-these-two&quot;&gt;Why Compare These Two?&lt;&#x2F;h2&gt;
&lt;p&gt;Search functionality for blogs or documents should be simple yet support multiple languages well. Both libraries are appealing because they work without dependencies and can be easily integrated with Zola&#x27;s Tera templates and JavaScript. However, for me, the key factors in choosing were Korean search performance, ease of setup, and how well it aligns with Zola&#x27;s simple philosophy.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fuse-js-simple-and-powerful-fuzzy-search&quot;&gt;Fuse.js: Simple and Powerful Fuzzy Search&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Features: Easily indexes arrays or object lists by specifying keys like &lt;code&gt;title&lt;&#x2F;code&gt;, &lt;code&gt;content&lt;&#x2F;code&gt;. Flexible query adjustments are possible with search threshold or weights.&lt;&#x2F;li&gt;
&lt;li&gt;Korean Support: Naturally handles Korean fuzzy search without separate plugins. For example, searching for &quot;한글&quot; will find results like &quot;한글 검색&quot;, &quot;한글서치&quot;.&lt;&#x2F;li&gt;
&lt;li&gt;Performance: Operates quickly and lightly on small to medium-sized datasets (hundreds to thousands of documents).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;e.g.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;fuse = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#4ec9b0;&quot;&gt;Fuse&lt;&#x2F;span&gt;&lt;span&gt;(list, { keys: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;title&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;content&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], threshold: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;0.4 &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;results = fuse.search(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;한글&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;elasticlunr-js-advanced-search-option&quot;&gt;Elasticlunr.js: Advanced Search Option&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Features: Supports field-specific indexing, stop word customization, Boolean queries, TF&#x2F;IDF ranking. Precise search is possible with field-specific boosting (e.g., &lt;code&gt;title:검색&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Korean Support: Default settings are English-centric, so Korean search performance is poor. You need to add the &lt;code&gt;lunr-languages&lt;&#x2F;code&gt; plugin for proper Korean tokenization.&lt;&#x2F;li&gt;
&lt;li&gt;Performance: Fast even on large datasets, but limited by browser memory, and setup is relatively complex.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;e.g.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;index = elasticlunr();
&lt;&#x2F;span&gt;&lt;span&gt;index.addField(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;title&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;index.addDoc(doc);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;results = index.search(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;한글&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { fields: { title: { boost: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;} } });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;how-to-integrate-with-zola&quot;&gt;How to Integrate with Zola?&lt;&#x2F;h2&gt;
&lt;p&gt;Zola allows easy configuration of search UI with Tera templates and JavaScript. Depending on the &lt;code&gt;config.toml&lt;&#x2F;code&gt; settings, a JSON format index file is generated during build, which can be loaded in JavaScript to add search functionality.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fuse&quot;&gt;Fuse&lt;&#x2F;h3&gt;
&lt;p&gt;Set &lt;code&gt;index_format&lt;&#x2F;code&gt; to &lt;code&gt;fuse_javascript&lt;&#x2F;code&gt; or &lt;code&gt;fuse_json&lt;&#x2F;code&gt;, and during build, an index file in a format usable by Fuse.js, like &lt;code&gt;search_index.en.json&lt;&#x2F;code&gt;, will be generated.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# config.toml
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;search&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;index_format &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;fuse_javascript&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# or &amp;quot;fuse_json&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;elasticlunr&quot;&gt;Elasticlunr&lt;&#x2F;h3&gt;
&lt;p&gt;Similarly, set it to &lt;code&gt;elasticlunr_javascript&lt;&#x2F;code&gt; or &lt;code&gt;elasticlunr_json&lt;&#x2F;code&gt; to generate an index file in Elasticlunr format.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# config.toml
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;search&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;index_format &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;elasticlunr_javascript&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# or &amp;quot;elasticlunr_json&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;my-case&quot;&gt;My Case&lt;&#x2F;h3&gt;
&lt;p&gt;I added more weight to &lt;code&gt;title&lt;&#x2F;code&gt; and included &lt;code&gt;body&lt;&#x2F;code&gt; and &lt;code&gt;tags&lt;&#x2F;code&gt; in the search targets as follows. You can adjust the &lt;code&gt;threshold&lt;&#x2F;code&gt; value to set search sensitivity.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;options = {
&lt;&#x2F;span&gt;&lt;span&gt;  keys: [
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;title&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, weight: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;body&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, weight: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;tags&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, weight: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  includeScore: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  ignoreLocation: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  threshold: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;0.4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Adjust as needed for search sensitivity
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;currentTerm = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;documents = Object.values(window.searchIndex.documentStore.docs);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span&gt;fuse = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#4ec9b0;&quot;&gt;Fuse&lt;&#x2F;span&gt;&lt;span&gt;(documents, options);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;goyo&#x2F;blob&#x2F;main&#x2F;static&#x2F;goyo.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;why-i-chose-fuse-js&quot;&gt;Why I Chose Fuse.js&lt;&#x2F;h2&gt;
&lt;p&gt;Initially, since Zola didn&#x27;t directly support Korean indexes, I was using Elasticlunr, which only worked for English searches. However, in May of this year (2025.05), the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;pull&#x2F;2881&quot;&gt;PR&lt;&#x2F;a&gt; I submitted was merged, officially enabling Korean index support, and it was included in a recent release.&lt;&#x2F;p&gt;
&lt;p&gt;This led me to reconsider search libraries, and ultimately, I leaned towards Fuse.js because it aligns well with Zola&#x27;s simple philosophy. The fact that the Fuse.js community is much more active was also appealing. Additionally, while &lt;code&gt;lunr-language&lt;&#x2F;code&gt; supports Korean, integrating it with Elasticlunr.js requires a lot of effort, whereas Fuse.js can be used immediately without separate setup, making it convenient.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;re implementing search functionality in Zola, I recommend Fuse.js as a simple and fast solution. It&#x27;s especially optimal for small to medium-sized sites, providing flexible fuzzy search for Korean without additional setup. Elasticlunr.js is useful where advanced search features are needed, but in most cases, I think Fuse offers better value for the investment.&lt;&#x2F;p&gt;
&lt;p&gt;Both libraries are excellent, but in my experience, Fuse.js fits better with Zola&#x27;s conciseness. If you&#x27;re running a blog or documentation page, I hope you&#x27;ll compare various search features and create an even better search experience.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Goyo</title>
        <published>2025-07-12T00:00:00+00:00</published>
        <updated>2025-07-12T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/projects/goyo/" type="text/html"/>
        <id>https://www.hahwul.com/projects/goyo/</id>
        <summary>A simplicity and clean documentation theme</summary>
        <content type="html">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;projects&#x2F;goyo&#x2F;goyo.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Goyo is a Zola theme that aims for simplicity and clean documentation.&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;goyo&quot;&gt;Github&lt;&#x2F;a&gt;
  &lt;a href=&quot;https:&#x2F;&#x2F;goyo.hahwul.com&quot;&gt;Demo &amp; Docs&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Dalfox 2.12 Released ⚡︎</title>
        <published>2025-07-06T00:00:00+00:00</published>
        <updated>2025-07-06T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/blog/2025/dalfox-2-12-released/" type="text/html"/>
        <id>https://www.hahwul.com/blog/2025/dalfox-2-12-released/</id>
        <summary>More powerful XSS scanning, Integration</summary>
        <content type="html">&lt;p&gt;Dalfox v2.12.0 has been released. It&#x27;s been about three months since the previous version, 2.11, and this update includes various feature enhancements. Today, I&#x27;ll briefly go over the new features that have been added.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;added-advanced-xss-scanning-features&quot;&gt;Added advanced XSS scanning features&lt;&#x2F;h2&gt;
&lt;p&gt;Four new flags have been added to improve the convenience and results of XSS scanning. I&#x27;d like to thank &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ibrahmsql&quot;&gt;@ibrahmsql&lt;&#x2F;a&gt; for contributing these features.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;--detailed-analysis: Performs deeper parameter analysis to uncover more XSS vectors&lt;&#x2F;li&gt;
&lt;li&gt;--fast-scan: Enables faster scanning, useful in CI&#x2F;CD pipelines&lt;&#x2F;li&gt;
&lt;li&gt;--magic-char-test: Generates magic character payloads for manual analysis&lt;&#x2F;li&gt;
&lt;li&gt;--context-aware: Selects payloads intelligently based on the response context&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;improve-help-message-grouping&quot;&gt;Improve help message (grouping)&lt;&#x2F;h2&gt;
&lt;p&gt;The readability of the &lt;code&gt;--help&lt;&#x2F;code&gt; message has been improved. Options are now grouped by function, making it much cleaner and easier to read.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;194f67a2-d274-475c-ad2d-9c1227d44b73&quot; alt=&quot;help&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;custom-blind-xss-payload-flag&quot;&gt;--custom-blind-xss-payload flag&lt;&#x2F;h2&gt;
&lt;p&gt;Previously, only predefined values could be used for BSS (Blind XSS) payloads. Now, the &lt;code&gt;--custom-blind-xss-payload&lt;&#x2F;code&gt; flag allows users to specify their own custom payloads. This is useful in environments that require a private C2 or a specific format.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;dalfox url http:&#x2F;&#x2F;test.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;q=1 --custom-blind-xss-payload &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;payloads.txt&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;new-payload-patterns&quot;&gt;New payload patterns&lt;&#x2F;h2&gt;
&lt;p&gt;Recently shared payloads have been added, such as those for codebase-based XSS.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;object &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#d69d85;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;codebase&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#d69d85;&quot;&gt;javascript:alert(document.domain)&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;embed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#d69d85;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;codebase&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#d69d85;&quot;&gt;javascript:alert(document.domain)&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;add-markdown-report-format&quot;&gt;Add markdown report format&lt;&#x2F;h2&gt;
&lt;p&gt;A &lt;code&gt;markdown&lt;&#x2F;code&gt; (or &lt;code&gt;md&lt;&#x2F;code&gt;) format has been added to the &lt;code&gt;--report-format&lt;&#x2F;code&gt; option. You can now save scan results as a clean markdown report, making it easy to integrate into other documents.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;dalfox url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;xss-game.appspot.com&#x2F;level1&#x2F;frame?query=AB&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span&gt;    --report \
&lt;&#x2F;span&gt;&lt;span&gt;    --report-format md
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This feature is especially useful when connecting to Caido. Please refer to the &lt;a href=&quot;https:&#x2F;&#x2F;dalfox.hahwul.com&#x2F;page&#x2F;running&#x2F;caido&#x2F;&quot;&gt;Dalfox Caido Integration&lt;&#x2F;a&gt; documentation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;server-mode&quot;&gt;Server mode&lt;&#x2F;h2&gt;
&lt;p&gt;Here are the improvements for Server mode.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;api-key-based-authentication&quot;&gt;API Key based authentication&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;--api-key&lt;&#x2F;code&gt; flag has been added to Server mode. By setting an API Key with this flag, you can operate the scanner more securely by only allowing requests that include the key in the &lt;code&gt;X-API-Key&lt;&#x2F;code&gt; header.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# Start Server
&lt;&#x2F;span&gt;&lt;span&gt;dalfox server --api-key=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;SECRET&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# Scan
&lt;&#x2F;span&gt;&lt;span&gt;curl --request POST \
&lt;&#x2F;span&gt;&lt;span&gt;  --url http:&#x2F;&#x2F;localhost:6664&#x2F;scan \
&lt;&#x2F;span&gt;&lt;span&gt;  --header &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;Content-Type: application&#x2F;json&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span&gt;  --header &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;X-API-Key: SECRET&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span&gt;  --data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;    &amp;quot;options&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;        &amp;quot;use-headless&amp;quot;: true,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;        &amp;quot;use-deepdxss&amp;quot;: true,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;        &amp;quot;output-all&amp;quot;: true,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;        &amp;quot;output-request&amp;quot;: false,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;        &amp;quot;output-response&amp;quot;: false,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;        &amp;quot;follow-redirects&amp;quot;: true
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;    },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;    &amp;quot;url&amp;quot;: &amp;quot;http:&#x2F;&#x2F;testphp.vulnweb.com&#x2F;listproducts.php?artist=123&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;cors-jsonp-support&quot;&gt;CORS, JSONP support&lt;&#x2F;h3&gt;
&lt;p&gt;Server mode now supports Cross-Origin Resource Sharing (CORS) and JSONP. The &lt;code&gt;--allow-origins&lt;&#x2F;code&gt; and &lt;code&gt;--jsonp&lt;&#x2F;code&gt; flags allow for more flexible integration of the Dalfox API with web applications from other domains.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;dalfox server \
&lt;&#x2F;span&gt;&lt;span&gt;    --allow-origins &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;www.hahwul.com&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span&gt;    --jsonp
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;pipe-mode&quot;&gt;Pipe mode&lt;&#x2F;h2&gt;
&lt;p&gt;Here are the improvements for Pipe mode.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;add-raw-http-request-support-to-pipe-mode&quot;&gt;Add raw HTTP request support to pipe mode&lt;&#x2F;h3&gt;
&lt;p&gt;Pipe mode now supports Raw HTTP Request as a direct input, just like File mode. This significantly improves interoperability, as you can directly pipe requests copied from proxy tools like Burp, Caido, and ZAP for testing.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;echo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;POST https:&#x2F;&#x2F;www.hahwul.com&#x2F;?q=dalfox HTTP&#x2F;1.1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;User-Agent: Mozilla&#x2F;5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko&#x2F;20100101 Firefox&#x2F;56.0
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;Accept: text&#x2F;html,application&#x2F;xhtml+xml,application&#x2F;xml;q=0.9,*&#x2F;*;q=0.8
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;Connection: keep-alive
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;Cookie: _ga=GA1.2.1102548207.1555467144; _gid=GA1.2.1362788908.1563875038
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;Upgrade-Insecure-Requests: 1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;Host: www.hahwul.com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;asdf=asdf&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span&gt;.&#x2F;dalfox pipe --rawdata
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bug-fixes-dependency-updates&quot;&gt;Bug Fixes &amp;amp; Dependency Updates&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Updated to a version of &lt;code&gt;chromedp&lt;&#x2F;code&gt; where an upstream bug has been fixed. (It&#x27;s recommended to update, as it was causing some errors).&lt;&#x2F;li&gt;
&lt;li&gt;Other minor bugs have been fixed, and dependency libraries have been updated.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;&#x2F;h2&gt;
&lt;p&gt;The documentation has been improved in line with the updates. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fabaff&quot;&gt;@fabaff&lt;&#x2F;a&gt;, who maintains dalfox in Nixpkgs, also contributed to the documentation updates. I&#x27;d like to thank him again.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-plan&quot;&gt;Next Plan&lt;&#x2F;h2&gt;
&lt;p&gt;In the future, Dalfox is planned to be improved in the following ways:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Docs: We plan to overhaul the documentation to improve readability and accessibility.&lt;&#x2F;li&gt;
&lt;li&gt;Features from Issues: We will review various feature suggestions reported as GitHub issues and incorporate them into development.&lt;&#x2F;li&gt;
&lt;li&gt;New Mode: We are conceptualizing new scan modes, such as an Interactive mode that utilizes AI or proceeds with scans through user interaction.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Thank you to everyone who contributed to this release. This summer in korea is unusually hot, so I&#x27;ll get to the next phase of work when the weather cools down a bit!&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Understanding DevSecOps</title>
        <published>2025-06-25T00:00:00+00:00</published>
        <updated>2025-06-25T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/sec/secure-sdlc/devsecops/" type="text/html"/>
        <id>https://www.hahwul.com/sec/secure-sdlc/devsecops/</id>
        <summary>Sharing thoughts and approaches on DevSecOps, which integrates development (Dev), security (Sec), and operations (Ops) to embed security throughout the development lifecycle.</summary>
        <content type="html">&lt;h2 id=&quot;what-is-devsecops&quot;&gt;What is DevSecOps?&lt;&#x2F;h2&gt;
&lt;p&gt;DevSecOps is a culture and methodology that integrates security throughout the entire development cycle while maintaining DevOps speed. As a Red Team, the most ideal scenario is a service built securely from the start, with no vulnerabilities to exploit. DevSecOps offers a practical solution to achieve this.&lt;&#x2F;p&gt;
&lt;p&gt;In the past, security was often applied after development was complete, creating inefficiencies similar to tearing down walls in a finished building. DevSecOps solves this problem through the &#x27;Shift Left&#x27; concept, moving security activities to earlier stages of development.&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 graph TD
    A[Plan] --&amp;gt; B(Code);
    B --&amp;gt; C{Build};
    C --&amp;gt; D[Test];
    D --&amp;gt; E(Release);
    E --&amp;gt; F[Deploy];
    F --&amp;gt; G(Operate);
    G --&amp;gt; H{Monitor};
    H --&amp;gt; A;

    subgraph Security in All Phases
        direction LR
        S1[Threat Modeling] -- in --&amp;gt; A;
        S2[SAST &amp;#x2F; SCA] -- in --&amp;gt; B;
        S3[SAST] -- in --&amp;gt; C;
        S4[DAST &amp;#x2F; IAST] -- in --&amp;gt; D;
        S5[Config Scan] -- in --&amp;gt; F;
        S6[Runtime Security] -- in --&amp;gt; G;
    end
&lt;&#x2F;pre&gt;
&lt;p&gt;The key is automating and integrating security into every phase, from threat modeling at the planning stage to SAST during coding and DAST during testing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-devsecops&quot;&gt;Why DevSecOps?&lt;&#x2F;h2&gt;
&lt;p&gt;Speed is the essence. In CI&#x2F;CD environments with multiple deployments per day, security performed after development completion becomes a bottleneck. DevSecOps automates security verification and integrates it into the pipeline, maintaining development speed while ensuring security. Here are examples of security activities and tools that can be applied at each SDLC stage:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Design&lt;&#x2F;strong&gt;: Threat modeling (e.g., OWASP Threat Dragon)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Develop&lt;&#x2F;strong&gt;: Real-time IDE scanning (e.g., SonarLint)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Build&lt;&#x2F;strong&gt;: SAST (Static Analysis), SCA (Open Source Analysis) (e.g., SonarQube, dependency-check)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Test&lt;&#x2F;strong&gt;: DAST (Dynamic Analysis) (e.g., OWASP ZAP)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Deploy&lt;&#x2F;strong&gt;: IaC and container image scanning (e.g., Trivy, tfsec)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;how-to-measure-maturity&quot;&gt;How to measure maturity?&lt;&#x2F;h2&gt;
&lt;p&gt;After implementing DevSecOps, measuring maturity and continuously improving is essential. The &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;OWASP&#x2F;www-project-devsecops-verification-standard&quot;&gt;OWASP DevSecOps Verification Standard (DSOVS)&lt;&#x2F;a&gt; provides a useful framework for this purpose.&lt;&#x2F;p&gt;
&lt;p&gt;DSOVS offers specific maturity assessment criteria across various domains including organization, design, and implementation, helping to diagnose current levels and design improvement roadmaps. For example, you can check items like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Do all team members receive regular security training? (ORG-002)&lt;&#x2F;li&gt;
&lt;li&gt;Are security requirements defined and tracked during feature development? (REQ-002)&lt;&#x2F;li&gt;
&lt;li&gt;Is threat modeling performed during architectural changes? (DES-002)&lt;&#x2F;li&gt;
&lt;li&gt;Are security scans automated in the CI&#x2F;CD pipeline? (IMP-003)&lt;&#x2F;li&gt;
&lt;li&gt;Are penetration tests conducted regularly? (VER-002)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;projects&#x2F;devsecops&#x2F;devsecops.jpg&quot; alt=&quot;&quot; &#x2F;&gt;
&lt;em&gt;I maintain the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;DevSecOps&quot;&gt;hahwul&#x2F;DevSecOps&lt;&#x2F;a&gt; project to organize and manage various related materials for personal reference.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;The success of DevSecOps goes beyond tool adoption; it depends on cultural change. True value emerges when security becomes a shared responsibility of everyone creating the product, not just the job of a specific team.&lt;&#x2F;p&gt;
&lt;p&gt;The offensive perspective of a Red Team and the defensive approach of DevSecOps ultimately converge toward the same goal: creating secure services. I hope this article provides a small help in starting your DevSecOps journey.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Understanding Content Security Policy (CSP)</title>
        <published>2025-06-23T00:00:00+00:00</published>
        <updated>2025-06-23T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/sec/web-security/csp/" type="text/html"/>
        <id>https://www.hahwul.com/sec/web-security/csp/</id>
        <summary>Learn about Content Security Policy (CSP), its importance, how it works, and how it enhances web security.</summary>
        <content type="html">&lt;h2 id=&quot;what-is-content-security-policy-csp&quot;&gt;What is Content Security Policy (CSP)?&lt;&#x2F;h2&gt;
&lt;p&gt;Content Security Policy (CSP) is a security mechanism that controls which resources a web page can load, preventing attacks like Cross-Site Scripting (XSS) and data injection.&lt;&#x2F;p&gt;
&lt;p&gt;CSP uses HTTP headers to specify trusted content sources, blocking execution of malicious scripts in the browser.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-is-csp-important&quot;&gt;Why is CSP Important?&lt;&#x2F;h2&gt;
&lt;p&gt;Modern web applications use resources from multiple sources, creating attack opportunities. CSP mitigates these threats by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Prevents XSS Attacks: By specifying trusted script sources, CSP blocks malicious scripts from being injected and executed.&lt;&#x2F;li&gt;
&lt;li&gt;Prevents Data Exfiltration: It can restrict data transmission to specific domains, preventing sensitive information from being leaked.&lt;&#x2F;li&gt;
&lt;li&gt;Prevents Clickjacking: The &lt;code&gt;frame-ancestors&lt;&#x2F;code&gt; directive controls how other sites can frame the page, thus preventing clickjacking attacks.&lt;&#x2F;li&gt;
&lt;li&gt;Prevents Mixed Content: It helps maintain a secure connection by preventing HTTP content from being loaded on HTTPS pages.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;how-does-csp-work&quot;&gt;How Does CSP Work?&lt;&#x2F;h2&gt;
&lt;p&gt;The web server sends the &lt;code&gt;Content-Security-Policy&lt;&#x2F;code&gt; HTTP header to the browser with directives that control resource loading:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;http&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-http &quot;&gt;&lt;code class=&quot;language-http&quot; data-lang=&quot;http&quot;&gt;&lt;span&gt;Content-Security-Policy: script-src &amp;#39;self&amp;#39;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The browser enforces this policy, blocking non-compliant resources and optionally reporting violations.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;key-csp-directives&quot;&gt;Key CSP Directives&lt;&#x2F;h3&gt;
&lt;p&gt;CSP provides these key directives:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Directive&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;default-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Sets a default policy for many other directives. If a specific directive is not set, this value is used.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;script-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid sources for JavaScript code.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;style-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid sources for CSS stylesheets.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;img-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid sources for images.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;font-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid sources for fonts.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;media-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid sources for media files like audio and video.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;connect-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Restricts origins that can be connected to using &lt;code&gt;fetch&lt;&#x2F;code&gt;, &lt;code&gt;XMLHttpRequest&lt;&#x2F;code&gt;, &lt;code&gt;WebSocket&lt;&#x2F;code&gt;, &lt;code&gt;EventSource&lt;&#x2F;code&gt;, etc.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;frame-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid sources that can be embedded as frames. (Deprecated, &lt;code&gt;child-src&lt;&#x2F;code&gt; is recommended).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;child-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid sources for nested browsing contexts, such as web workers and frames.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;object-src&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Controls valid sources for &lt;code&gt;&amp;lt;object&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;&amp;lt;embed&amp;gt;&lt;&#x2F;code&gt;, and &lt;code&gt;&amp;lt;applet&amp;gt;&lt;&#x2F;code&gt; tags. It&#x27;s recommended to set this to &lt;code&gt;&#x27;none&#x27;&lt;&#x2F;code&gt; for security reasons.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;frame-ancestors&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies valid parent origins that can embed the current page using frames, iframes, objects, embeds, or applets. This is crucial for defending against clickjacking attacks.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;report-uri&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;report-to&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specifies a URL where the browser will send reports when a CSP violation occurs. &lt;code&gt;report-to&lt;&#x2F;code&gt; is a newer directive that supports JSON-formatted reports and supersedes &lt;code&gt;report-uri&lt;&#x2F;code&gt;.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;source-values&quot;&gt;Source Values&lt;&#x2F;h3&gt;
&lt;p&gt;Each directive can have source values such as:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Source Value&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&#x27;self&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Current origin only (same scheme, host, port).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&#x27;unsafe-inline&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Inline JavaScript and CSS (security risk).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&#x27;unsafe-eval&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Allows &lt;code&gt;eval()&lt;&#x2F;code&gt; and similar (security risk).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&#x27;none&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;No sources allowed.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;https:&#x2F;&#x2F;example.com&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specific domain.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;*.example.com&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;All subdomains of example.com.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;data:&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Data URI scheme resources.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;nonce-&amp;lt;base64-value&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Scripts&#x2F;styles with specific nonce.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sha256-&amp;lt;base64-value&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Scripts&#x2F;styles with matching hash.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;how-to-apply-csp&quot;&gt;How to Apply CSP&lt;&#x2F;h2&gt;
&lt;p&gt;Apply CSP in one of two ways:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;HTTP Header (recommended):&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;Nginx: &lt;code&gt;add_header Content-Security-Policy &quot;default-src &#x27;self&#x27;; script-src &#x27;self&#x27; https:&#x2F;&#x2F;trusted-cdn.com&quot;;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Apache: &lt;code&gt;Header set Content-Security-Policy &quot;default-src &#x27;self&#x27;; script-src &#x27;self&#x27; https:&#x2F;&#x2F;trusted-cdn.com&quot;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;meta&amp;gt;&lt;&#x2F;code&gt; Tag (limited support):&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;meta &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;http-equiv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c8c8c8;&quot;&gt;&amp;quot;Content-Security-Policy&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c8c8c8;&quot;&gt;&amp;quot;default-src &amp;#39;self&amp;#39;; script-src &amp;#39;self&amp;#39; https:&#x2F;&#x2F;trusted-cdn.com;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
Note: &lt;code&gt;&amp;lt;meta&amp;gt;&lt;&#x2F;code&gt; doesn&#x27;t support &lt;code&gt;frame-ancestors&lt;&#x2F;code&gt;, &lt;code&gt;report-uri&lt;&#x2F;code&gt;, or &lt;code&gt;sandbox&lt;&#x2F;code&gt; directives.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;csp-implementation-strategy&quot;&gt;CSP Implementation Strategy&lt;&#x2F;h2&gt;
&lt;p&gt;Implement CSP gradually to avoid breaking functionality:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Report-Only Mode: Test without enforcement&lt;&#x2F;li&gt;
&lt;li&gt;Start lenient, then tighten: Begin with permissive rules, gradually remove unsafe directives&lt;&#x2F;li&gt;
&lt;li&gt;Use nonces&#x2F;hashes instead of &lt;code&gt;&#x27;unsafe-inline&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Monitor and update: Regularly review violation reports and adjust as needed&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;http&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-http &quot;&gt;&lt;code class=&quot;language-http&quot; data-lang=&quot;http&quot;&gt;&lt;span&gt;  Content-Security-Policy-Report-Only: default-src &amp;#39;self&amp;#39;; report-uri &#x2F;csp-violation-report-endpoint;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;benefits-of-csp&quot;&gt;Benefits of CSP&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Strong XSS Defense: Blocks untrusted script execution&lt;&#x2F;li&gt;
&lt;li&gt;Reduced Data Injection: Limits malicious data insertion&lt;&#x2F;li&gt;
&lt;li&gt;Clickjacking Protection: Controls framing via &lt;code&gt;frame-ancestors&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Enhanced Security Awareness: Improves resource visibility&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;CSP is essential for modern web application security, protecting against XSS and other common attacks. Despite implementation complexity, its security benefits are substantial. Gradual implementation and ongoing monitoring will significantly improve your site&#x27;s security posture.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>How to Securing GraphQL</title>
        <published>2025-06-23T00:00:00+00:00</published>
        <updated>2025-06-23T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/sec/web-security/graphql/" type="text/html"/>
        <id>https://www.hahwul.com/sec/web-security/graphql/</id>
        <summary>A summary of common security vulnerabilities in GraphQL and their mitigation strategies.</summary>
        <content type="html">&lt;p&gt;GraphQL provides superior flexibility and efficiency compared to traditional REST APIs by allowing clients to request exactly the data they need. However, this flexibility introduces unique security challenges. If not addressed properly, these can lead to data leaks, denial of service vulnerabilities, and privilege escalation issues.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I&#x27;ll explain the major security threats related to GraphQL and provide practical strategies to protect GraphQL-based applications. The core principle is implementing security controls not only at the network boundary but deep within the application logic as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-graphql-request-lifecycle&quot;&gt;The GraphQL Request Lifecycle&lt;&#x2F;h2&gt;
&lt;p&gt;A secure GraphQL request processing flow includes several layers of validation and verification.&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 graph TD
    A[Client Request] --&amp;gt; B{HTTP Middleware};
    B --&amp;gt; C{Authentication};
    C -- Authenticated --&amp;gt; D[Parse Query];
    C -- Failed --&amp;gt; Z[Reject Request];
    D --&amp;gt; E{Validation};
    E -- Cost&amp;#x2F;Depth OK --&amp;gt; F[Execute Resolvers];
    E -- Invalid Query --&amp;gt; Z;
    F -- For each field --&amp;gt; G{Authorization Check};
    G -- Authorized --&amp;gt; H[Fetch Data];
    G -- Unauthorized --&amp;gt; I[Return Null&amp;#x2F;Error];
    H --&amp;gt; J[Format Response];
    I --&amp;gt; J;
    J --&amp;gt; K[Return to Client];

    subgraph &amp;quot;Pre-Execution&amp;quot;
        B
        C
        D
        E
    end

    subgraph &amp;quot;Execution&amp;quot;
        F
        G
        H
        I
    end
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;abusing-introspection&quot;&gt;Abusing Introspection&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Problem: Introspection allows clients to query the GraphQL schema itself, exposing all types, fields, queries, and mutations. While useful for development tools, it gives attackers a map of your API&#x27;s entire structure.&lt;&#x2F;li&gt;
&lt;li&gt;Mitigation: Disable introspection in production environments. This is typically available as a configuration flag in your GraphQL server library. For example, in &lt;code&gt;apollo-server&lt;&#x2F;code&gt;, you can set it when instantiating the server:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;server = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#4ec9b0;&quot;&gt;ApolloServer&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  typeDefs,
&lt;&#x2F;span&gt;&lt;span&gt;  resolvers,
&lt;&#x2F;span&gt;&lt;span&gt;  introspection: process.env.NODE_ENV !== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;production&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;denial-of-service-dos&quot;&gt;Denial of Service (DoS)&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Problem: Attackers can craft deeply nested or complex queries that consume excessive server resources, causing denial of service for legitimate users.&lt;&#x2F;li&gt;
&lt;li&gt;Mitigations:
&lt;ul&gt;
&lt;li&gt;Query Depth Limiting: Restrict the maximum nesting level of queries. For example, reject queries nested more than 10 levels deep.&lt;&#x2F;li&gt;
&lt;li&gt;Query Cost Analysis: Assign numeric &quot;costs&quot; to each field based on computational complexity. Calculate the total cost before executing queries and reject them if they exceed a predefined threshold.&lt;&#x2F;li&gt;
&lt;li&gt;Timeouts: Set enforced timeouts on query execution to prevent long-running queries from indefinitely occupying server resources.&lt;&#x2F;li&gt;
&lt;li&gt;Amount Limiting (Pagination): Always limit the number of records that can be returned from a list. Never allow clients to request unlimited items.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;authorization-flaws&quot;&gt;Authorization Flaws&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Problem: GraphQL resolvers fetch data for specific fields. If permission checks aren&#x27;t performed at the resolver level for each object, attackers may access data they shouldn&#x27;t have permission to see. This is a classic Insecure Direct Object Reference (IDOR) vulnerability path.&lt;&#x2F;li&gt;
&lt;li&gt;Mitigation: Implement permission checks within each relevant resolver or as middleware layers. For every piece of data requested, the application should verify that the currently authenticated user has permission to view or modify that data. It&#x27;s not sufficient to check authentication only at the endpoint level.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;resolvers = {
&lt;&#x2F;span&gt;&lt;span&gt;  Query: {
&lt;&#x2F;span&gt;&lt;span&gt;    user: (parent, { id }, context) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Permission check: Does the logged-in user have rights to view this profile?
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(context.user.id !== id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;&amp;amp;&amp;amp; !&lt;&#x2F;span&gt;&lt;span&gt;context.user.isAdmin) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;throw new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#4ec9b0;&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;You do not have permission to view this user&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;db.users.find({ id: id });
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;insufficient-error-handling&quot;&gt;Insufficient Error Handling&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Problem: Detailed error messages such as stack traces or database errors can leak sensitive information about your backend infrastructure, libraries used, and database schema.&lt;&#x2F;li&gt;
&lt;li&gt;Mitigation: Implement global error handlers that catch all exceptions. Log detailed errors for internal debugging purposes but return sanitized, generic error messages to clients. Libraries like &lt;code&gt;graphql-errors&lt;&#x2F;code&gt; can help formalize this process.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;authentication&quot;&gt;Authentication&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Problem: GraphQL is transport-layer independent and doesn&#x27;t enforce any particular authentication mechanism. It&#x27;s up to developers to implement this correctly.&lt;&#x2F;li&gt;
&lt;li&gt;Mitigation: Treat GraphQL endpoints like any other sensitive API endpoint. Implement standard authentication mechanisms such as OAuth 2.0 or JWTs. Tokens should be passed via the &lt;code&gt;Authorization&lt;&#x2F;code&gt; HTTP header and validated in a middleware layer before queries are processed. The validated user information should be stored in the GraphQL &lt;code&gt;context&lt;&#x2F;code&gt; object for resolvers to use.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Securing GraphQL APIs requires a shift in thinking from traditional endpoint-based security models. The flexible nature of GraphQL means that security must be an integral part of the core application logic.&lt;&#x2F;p&gt;
&lt;p&gt;By disabling introspection in production environments, implementing strong controls against resource exhaustion attacks, enforcing object-level authorization within resolvers, and carefully managing error responses, developers can build robust and secure GraphQL applications. Security is not a feature to add at the end, but a fundamental requirement to consider throughout the development lifecycle.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Integrating Mermaid.js in Zola</title>
        <published>2025-06-17T00:00:00+00:00</published>
        <updated>2025-07-06T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/dev/zola/mermaid-in-zola/" type="text/html"/>
        <id>https://www.hahwul.com/dev/zola/mermaid-in-zola/</id>
        <summary>Applying and optimizing Mermaid.js in Zola, a Rust-based static site generator</summary>
        <content type="html">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mermaid.js.org&quot;&gt;Mermaid.js&lt;&#x2F;a&gt; is a powerful library that lets you create various diagrams using Markdown-like syntax. In this post, I&#x27;ll walk you through the steps to integrate Mermaid.js into a Zola static site generator and how to optimize its performance.&lt;&#x2F;p&gt;
&lt;div class=&quot;gallery-box&quot;&gt;
  &lt;div class=&quot;gallery&quot;&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;quadrantChart
&lt;&#x2F;span&gt;&lt;span&gt;    title Reach and engagement of campaigns
&lt;&#x2F;span&gt;&lt;span&gt;    x-axis Low Reach --&amp;gt; High Reach
&lt;&#x2F;span&gt;&lt;span&gt;    y-axis Low Engagement --&amp;gt; High Engagement
&lt;&#x2F;span&gt;&lt;span&gt;    quadrant-1 We should expand
&lt;&#x2F;span&gt;&lt;span&gt;    quadrant-2 Need to promote
&lt;&#x2F;span&gt;&lt;span&gt;    quadrant-3 Re-evaluate
&lt;&#x2F;span&gt;&lt;span&gt;    quadrant-4 May be improved
&lt;&#x2F;span&gt;&lt;span&gt;    Campaign A: [0.3, 0.6]
&lt;&#x2F;span&gt;&lt;span&gt;    Campaign B: [0.45, 0.23]
&lt;&#x2F;span&gt;&lt;span&gt;    Campaign C: [0.57, 0.69]
&lt;&#x2F;span&gt;&lt;span&gt;    Campaign D: [0.78, 0.34]
&lt;&#x2F;span&gt;&lt;span&gt;    Campaign E: [0.40, 0.34]
&lt;&#x2F;span&gt;&lt;span&gt;    Campaign F: [0.35, 0.78]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 quadrantChart
      title Reach and engagement of campaigns
      x-axis Low Reach --&amp;gt; High Reach
      y-axis Low Engagement --&amp;gt; High Engagement
      quadrant-1 We should expand
      quadrant-2 Need to promote
      quadrant-3 Re-evaluate
      quadrant-4 May be improved
      Campaign A: [0.3, 0.6]
      Campaign B: [0.45, 0.23]
      Campaign C: [0.57, 0.69]
      Campaign D: [0.78, 0.34]
      Campaign E: [0.40, 0.34]
      Campaign F: [0.35, 0.78]
&lt;&#x2F;pre&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;1-creating-a-shortcode&quot;&gt;1. Creating a Shortcode&lt;&#x2F;h2&gt;
&lt;p&gt;The first step to using Mermaid.js in Zola is to create a shortcode. I created mermaid.html in the templates&#x2F;shortcodes directory.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;pre &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c8c8c8;&quot;&gt;&amp;quot;mermaid&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt; {{ body }}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;pre&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This shortcode is very simple; it just generates a &lt;code&gt;&amp;lt;pre&amp;gt;&lt;&#x2F;code&gt; tag with the mermaid class. However, Mermaid.js identifies this mermaid class to convert the content into a graph, making it incredibly easy to create diagrams.&lt;&#x2F;p&gt;
&lt;p&gt;You can use it like this code in markdown.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;jinja2&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-jinja2 &quot;&gt;&lt;code class=&quot;language-jinja2&quot; data-lang=&quot;jinja2&quot;&gt;&lt;span&gt;{% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;mermaid&lt;&#x2F;span&gt;&lt;span&gt;() %}
&lt;&#x2F;span&gt;&lt;span&gt;sequenceDiagram
&lt;&#x2F;span&gt;&lt;span&gt;    Alice-&amp;gt;&amp;gt;+John: Hello John, how are you?
&lt;&#x2F;span&gt;&lt;span&gt;    Alice-&amp;gt;&amp;gt;+John: John, can you hear me?
&lt;&#x2F;span&gt;&lt;span&gt;    John--&amp;gt;&amp;gt;-Alice: Hi Alice, I can hear you!
&lt;&#x2F;span&gt;&lt;span&gt;    John--&amp;gt;&amp;gt;-Alice: I feel great!
&lt;&#x2F;span&gt;&lt;span&gt;{% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;end &lt;&#x2F;span&gt;&lt;span&gt;%}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;2-adding-the-mermaid-js-library&quot;&gt;2. Adding the Mermaid.js Library&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;cdn-approach-initial-method&quot;&gt;CDN Approach (Initial Method)&lt;&#x2F;h3&gt;
&lt;p&gt;Initially, I used a CDN to load Mermaid.js. I added the code to load mermaid.js in my overall layout, &lt;code&gt;base.html&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&amp;lt;!-- mermaid --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;script &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c8c8c8;&quot;&gt;&amp;quot;module&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#569cd6;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;mermaid &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#569cd6;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#d69d85;&quot;&gt;&amp;#39;https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;npm&#x2F;mermaid@11&#x2F;dist&#x2F;mermaid.esm.min.mjs&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;   mermaid.initialize({ startOnLoad: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#569cd6;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;, theme: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#d69d85;&quot;&gt;&amp;#39;dark&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This method was simple to implement but came with the downsides of external dependency and potential network latency.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;internalizing-with-local-files-optimization&quot;&gt;Internalizing with Local Files (Optimization)&lt;&#x2F;h3&gt;
&lt;p&gt;To improve performance and remove external dependencies, I internalized Mermaid.js by saving it as a local file.&lt;&#x2F;p&gt;
&lt;p&gt;First, I found that .mjs files were difficult to handle as they often load multiple chunks. So, I opted to download the &lt;code&gt;.js&lt;&#x2F;code&gt; file instead.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;curl -s https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;npm&#x2F;mermaid@11&#x2F;dist&#x2F;mermaid.min.js -o static&#x2F;js&#x2F;mermaid.min.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Afterward, I loaded it in base.html by referencing the local path:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&amp;lt;!-- mermaid --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;script &lt;&#x2F;span&gt;&lt;span style=&quot;color:#92caf4;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c8c8c8;&quot;&gt;&amp;quot;&#x2F;js&#x2F;mermaid.min.js&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;mermaid.initialize({ startOnLoad: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#569cd6;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;, theme: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#d69d85;&quot;&gt;&amp;#39;dark&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#282828;color:#dcdcdc;&quot;&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#808080;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;3-optimizing-the-rendering-method&quot;&gt;3. Optimizing the Rendering Method&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;startonload-method-initial-method&quot;&gt;startOnLoad Method (Initial Method)&lt;&#x2F;h3&gt;
&lt;p&gt;Initially, I used Mermaid.js&#x27;s default rendering method, &lt;code&gt;startOnLoad: true&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;mermaid.initialize({ startOnLoad: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;, theme: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;dark&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While this method automatically rendered all diagrams upon page load, it caused a delay in rendering because it waited for all other elements, like images, to fully load on the page.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;manual-rendering-method-optimization&quot;&gt;Manual Rendering Method (Optimization)&lt;&#x2F;h3&gt;
&lt;p&gt;I realized it would be better to proceed immediately once the Mermaid code was fully loaded. So, I switched to manually specifying the rendering time. I set &lt;code&gt;startOnLoad: false&lt;&#x2F;code&gt; to prevent automatic rendering and then changed it to manually render only the necessary elements after the DOM was loaded.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;mermaid.initialize({ startOnLoad: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;, theme: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;dark&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;document.addEventListener(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;DOMContentLoaded&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;mermaidElements = document.querySelectorAll(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;.mermaid&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(mermaidElements.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span&gt;mermaid.run({
&lt;&#x2F;span&gt;&lt;span&gt;          nodes: mermaidElements
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As a result, as soon as objects with the mermaid class are identified, rendering starts almost simultaneously with page access.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s test with a simple example to see if it works well&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 mindmap
  root((mindmap))
    Origins
      Long history
      ::icon(fa fa-book)
      Popularisation
        British popular psychology author Tony Buzan
    Research
      On effectiveness&amp;lt;br&amp;#x2F;&amp;gt;and features
      On Automatic creation
        Uses
            Creative techniques
            Strategic planning
            Argument mapping
    Tools
      Pen and paper
      Mermaid
&lt;&#x2F;pre&gt;
&lt;p&gt;It works perfectly!&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>The Art of Agentic Coding</title>
        <published>2025-06-14T00:00:00+00:00</published>
        <updated>2025-07-14T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/blog/2025/agentic-coding/" type="text/html"/>
        <id>https://www.hahwul.com/blog/2025/agentic-coding/</id>
        <summary>My Agentic Coding Rule and Thoughts</summary>
        <content type="html">&lt;p&gt;The coding paradigm is rapidly changing with AI. I&#x27;ve heard the term &#x27;Vibe Coding&#x27; being thrown around, but honestly, I&#x27;m not quite sure what to make of it. The idea of generating code based on just a feeling or atmosphere doesn&#x27;t sit right with me, as it seems to undermine the essence of the coding experience I value.&lt;&#x2F;p&gt;
&lt;p&gt;However, Agentic Coding is a bit different. It goes beyond simply requesting code generation; it&#x27;s about delegating specific tasks to an AI agent. It&#x27;s much closer to having a pair programmer by my side. This approach has allowed me to write more code faster without losing my coding intuition or my grip on the project&#x27;s direction. For me, Agentic Coding is closer to the ideal model of collaboration.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I want to share the practices and experiences that have helped me get better at using Agentic Coding.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;agentic-coding&quot;&gt;Agentic Coding&lt;&#x2F;h2&gt;
&lt;p&gt;Agentic Coding isn&#x27;t fundamentally different from writing code with existing AI-based IDEs. The key difference is that instead of just instructing an AI agent to write a specific piece of code, you give it a degree of autonomy and delegate entire tasks for it to solve. The core of this process is an iterative loop of generating, modifying, verifying, and testing code to complete the given task.&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 ---
title: Agentic Coding Workflow @hahwul
---
graph LR
    A(Task) --&amp;gt; B[Planning];
    B --&amp;gt; C[Code Generation];
    C --&amp;gt; D[Verification &amp;amp; Testing];
    D --&amp;gt; E[Request review];
    E --&amp;gt; F{Solve?};
    F -- No --&amp;gt; B;
    F -- Yes --&amp;gt; G(End);
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;keys-to-effective-agentic-coding&quot;&gt;Keys to Effective Agentic Coding&lt;&#x2F;h2&gt;
&lt;p&gt;After working on several projects with AI agents, I&#x27;ve realized there are a few key things that make the collaboration more effective. While you can handle simple tasks without much setup, the results are vastly different when you have a well-configured environment. Here are some of the points I&#x27;ve noted.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;know-your-project&quot;&gt;Know Your Project&lt;&#x2F;h3&gt;
&lt;p&gt;The most important thing is that the developer—you—must be the captain of the ship. The AI is a capable crew member, but making the final decisions and seeing the big picture is the captain&#x27;s job. You need a clear understanding of the project&#x27;s architecture, goals, and constraints to give the AI precise instructions and to properly evaluate and integrate its output.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;parallel-friendly-code-structure&quot;&gt;Parallel-Friendly Code Structure&lt;&#x2F;h3&gt;
&lt;p&gt;AI agents aren&#x27;t like humans. When you consider using multiple agents simultaneously or having one agent handle several tasks in parallel, it&#x27;s far more effective if the code is logically and clearly separated. This might conflict with some traditional programming concepts, but in AI collaboration, having clearly divided units of work has proven more efficient. I&#x27;ve personally found it much easier to delegate tasks in a structure where features are broken down into packages or libraries.&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
 graph TD
    A[Monolithic Application] --&amp;gt; B{Core Logic};
    B --&amp;gt; C[Feature A Integration];
    B --&amp;gt; D[Feature B Integration];
    B --&amp;gt; E[Feature C Integration];
    C --&amp;gt; F(UI Module);
    D --&amp;gt; G(Database Module);
    E --&amp;gt; H(API Module);

    subgraph &amp;quot;Tightly Coupled&amp;quot;
        B --- C;
        B --- D;
        B --- E;
    end
&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;Traditional Code Structure (Less Parallel-Friendly)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;pre class=&quot;mermaid&quot;&gt;
 graph TD
    A[Main Orchestrator Agent] --&amp;gt; B{Task Delegation};
    B --&amp;gt; C[Agent Feature A Package];
    B --&amp;gt; D[Agent Feature B Package];
    B --&amp;gt; E[Agent Feature C Library];

    C --&amp;gt; F(Sub-Task 1.1);
    C --&amp;gt; G(Sub-Task 1.2);
    D --&amp;gt; H(Sub-Task 2.1);
    E --&amp;gt; I(Sub-Task 3.1);
    E --&amp;gt; J(Sub-Task 3.2);

    subgraph &amp;quot;Loosely Coupled&amp;quot;
        C;
        D;
        E;
    end
&lt;&#x2F;pre&gt;

&lt;em&gt;Parallel-Friendly Code Structure (Optimized for AI Agents)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-importance-of-tooling&quot;&gt;The Importance of Tooling&lt;&#x2F;h3&gt;
&lt;p&gt;AI makes mistakes. But well-established tooling can significantly reduce those mistakes and help correct them quickly when they do happen. Linters, formatters, and testers are no longer optional; they&#x27;re essential.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;fb20ceb3-8957-4efc-93cc-29c18754008e&quot; alt=&quot;AI&amp;#39;s Test&quot; &#x2F;&gt;
&lt;em&gt;Verifying that the generated code has no issues.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been reminded once again of the importance of TDD (Test-Driven Development). In projects with thorough test coverage, I&#x27;ve had many impressive experiences where the AI generated faulty code, discovered the issue during the CI&#x2F;CD pipeline&#x27;s test phase, and then corrected itself.&lt;&#x2F;p&gt;
&lt;p&gt;An interesting side note, however, is that AI also uses various tricks to pass tests. If you look closely, you&#x27;ll sometimes see it deleting test cases or finding shortcuts to get a pass. This is something that needs to be caught during the code review phase.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;language-choice-clear-errors&quot;&gt;Language Choice &amp;amp; Clear Errors&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ve often been asked, &quot;If you&#x27;re coding with AI, what language is best?&quot; The language itself might not be the most critical factor. However, from my personal experience, languages that provide detailed and friendly error messages are more advantageous for AI collaboration. When the compiler or interpreter clearly states &quot;what&quot; went wrong and &quot;why,&quot; the AI&#x27;s chances of fixing the problem increase. For that reason, I chose Rust, and I&#x27;ve been quite satisfied with the results.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;docs-for-ai-ai-readme&quot;&gt;Docs for AI (AI-README)&lt;&#x2F;h3&gt;
&lt;p&gt;We usually write a &lt;code&gt;README.md&lt;&#x2F;code&gt; file for our projects. I take it a step further and create a separate document, like &lt;code&gt;AGENTS.md&lt;&#x2F;code&gt; ,&lt;code&gt;AI.md&lt;&#x2F;code&gt;, specifically for the AI agent to read. In this document, I outline the project&#x27;s overall structure, the role of each module, a list of key files to reference, and the rules that must be followed during coding. The presence of this document has made a huge difference in the AI&#x27;s ability to understand the project and perform its tasks.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;clear-granular-tasks&quot;&gt;Clear, Granular Tasks&lt;&#x2F;h3&gt;
&lt;p&gt;The more abstract and larger the task, the more the AI struggles. Of course, the same could be said for humans. As I&#x27;ve used AI agents more, I&#x27;ve found myself breaking down tasks into much smaller and more clearly defined pieces than I used to. Instead of a large task like &#x27;Implement login feature,&#x27; providing specific instructions like &#x27;1. Define user model, 2. Create database schema, 3. Implement password hashing logic...&#x27; yields much better results.&lt;&#x2F;p&gt;
&lt;p&gt;Even if you have to assign a large task, it&#x27;s best to provide a detailed plan for the AI to follow. In this respect, Jules, which I&#x27;ll discuss in the workflow section below, has a distinct advantage.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;b082fb5d-4144-4c81-a19a-20f6ec2c4259&quot; alt=&quot;Jules&quot; &#x2F;&gt;
&lt;em&gt;In Jules, a plan is created and first reviewed by the user before the task is executed.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-current-workflow&quot;&gt;My Current Workflow&lt;&#x2F;h2&gt;
&lt;p&gt;Locally, I usually delegate tasks from the Agent Panel in &lt;a href=&quot;https:&#x2F;&#x2F;zed.dev&quot;&gt;Zed&lt;&#x2F;a&gt;, close the tab, and get on with my other work. It&#x27;s convenient because it uses macOS notifications to let me know when it&#x27;s done. In a cloud environment, I frequently use Google&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;jules.google&quot;&gt;Jules&lt;&#x2F;a&gt;. Jules has given me a great experience of being able to assign tasks continuously, regardless of my location or situation. I often find myself giving it instructions from my phone when I&#x27;m out.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;0a560b65-379d-4c4d-a0d0-7c7bf1ed6b4c&quot; alt=&quot;Zed and Jules&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m also very interested in terminal-based tools like Codex and Claude Code. However, what I&#x27;m most excited about is the &lt;a href=&quot;https:&#x2F;&#x2F;github.blog&#x2F;news-insights&#x2F;product-news&#x2F;github-copilot-the-agent-awakens&#x2F;&quot;&gt;GitHub Copilot Coding Agent (the one previously introduced as Padawan)&lt;&#x2F;a&gt;. I manage most of my tasks as GitHub Issues, and this Agent is expected to understand and execute tasks based on those issues. This would streamline the process of organizing and delegating work significantly. While I could try some features now with the GitHub Pro Plus plan, I&#x27;m currently receiving the Pro plan for free from GitHub, which makes switching a bit awkward. For now, I plan to stick with my current environment and think it over slowly.&lt;&#x2F;p&gt;
&lt;p&gt;Since Gemini CLI was released in June 2025, I have primarily been using Zed, Jules, and the Gemini CLI. I also use Gemini and Grok in parallel for research.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Of course, what I&#x27;ve shared today won&#x27;t be a one-size-fits-all solution. I hope you&#x27;ll take it as the personal experience and opinion of one developer coding with AI.&lt;&#x2F;p&gt;
&lt;p&gt;I believe the change brought by AI is an unstoppable tide of our times. There&#x27;s no need to be afraid of a changing era. We just need to ride the wave and go with the flow. What&#x27;s clear is that the way we develop has changed significantly, and we too must change to some extent.&lt;&#x2F;p&gt;
&lt;p&gt;But as always, the fundamentals are important. AI is a tool and a companion that you can leverage only as much as you know. To make better use of this new colleague, we must continue to train and grow ourselves. Ultimately, in the age of AI, your competitive edge as a developer will be determined by how well you can handle AI and how solid your own fundamental skills are.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>DevSecOps</title>
        <published>2025-06-08T00:00:00+00:00</published>
        <updated>2025-06-08T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/projects/devsecops/" type="text/html"/>
        <id>https://www.hahwul.com/projects/devsecops/</id>
        <summary>Roadmap for everyone who wants DevSecOps</summary>
        <content type="html">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;projects&#x2F;devsecops&#x2F;devsecops.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;DevSecOps is a culture and practice that aims to integrate security into every phase of the software development lifecycle (SDLC). It emphasizes collaboration between Development, Security, and Operations teams. The goal is to build secure software from the ground up, reduce vulnerabilities, and ensure faster, safer deployments. This roadmap provides a curated list of resources and tools to help individuals and organizations implement DevSecOps practices.&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;DevSecOps&quot;&gt;Github&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Jwt-Hack: Reborn in Rust</title>
        <published>2025-06-06T00:00:00+00:00</published>
        <updated>2025-06-06T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/blog/2025/jwt-hack-v2/" type="text/html"/>
        <id>https://www.hahwul.com/blog/2025/jwt-hack-v2/</id>
        <summary>jwt-hack v2 is a complete Rust rewrite, boosting performance, safety, and stability.</summary>
        <content type="html">&lt;p&gt;Back in October 2020, I created a tool called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;jwt-hack&quot;&gt;jwt-hack&lt;&#x2F;a&gt; to make security testing for JSON Web Tokens (JWTs) a bit more convenient. At the time, this Go-based tool was a project born out of my own necessity, focusing on its core &lt;code&gt;crack&lt;&#x2F;code&gt; feature to find JWT signing secrets using wordlists or brute force.&lt;&#x2F;p&gt;
&lt;p&gt;Truth be told, for the nearly five years since, the project has been somewhat neglected compared to my other work. While I made small improvements here and there, a part of me always felt it deserved more. That&#x27;s why I finally decided to take the plunge and give the project a complete reboot, leading to the release of a new major version: jwt-hack v2.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;projects&#x2F;jwt-hack&#x2F;jwt-hack.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I&#x27;d like to briefly share what the key changes in jwt-hack v2 are and where the project is headed next.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;from-go-to-rust-the-core-change&quot;&gt;From Go to Rust: The Core Change&lt;&#x2F;h2&gt;
&lt;p&gt;The biggest change in v2 is the complete switch from Go to Rust. Rewriting all the existing Go code in Rust took a fair amount of effort, but I believe it was a worthwhile endeavor.&lt;&#x2F;p&gt;
&lt;p&gt;The primary reason was stability. Frankly, v1 would occasionally run into unexpected memory-related issues. Thanks to Rust&#x27;s Ownership system and its strict compile-time checks, I was able to prevent these kinds of bugs at the source, resulting in a much more stable tool.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, a performance boost came along as a bonus. Even a simple benchmark shows a pretty satisfying speed improvement.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Metric&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;jwt-hack &lt;code&gt;v1&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;jwt-hack &lt;code&gt;v2&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Time (mean ± σ)&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.874 s ± 0.033 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;678.6 ms ± 165.8 ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Range (min … max)&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.834 s … 1.935 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;623.5 ms … 1150.4 ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;User Time&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;7.312 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;3516.7 ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;System Time&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;1.985 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;491.1 ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Runs&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;10&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;10&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;em&gt;Performance comparison of the &lt;code&gt;crack&lt;&#x2F;code&gt; feature under identical conditions (&lt;code&gt;$ jwt-hack crack eyJ0e... --mode brute --max 4&lt;&#x2F;code&gt;)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;strengthening-the-foundation&quot;&gt;Strengthening the Foundation&lt;&#x2F;h2&gt;
&lt;p&gt;This wasn&#x27;t just about changing the language; I also focused on strengthening the project&#x27;s foundation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;test-coverage&quot;&gt;Test Coverage&lt;&#x2F;h3&gt;
&lt;p&gt;One of the areas I paid the most attention to during v2&#x27;s development was the test code. I wrote far more thorough tests compared to the previous version, creating a solid base to minimize side effects from future code changes and maintain stability as new features are added.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;b7178cc9-37d4-40a3-b83c-29b88a833f2f&quot; alt=&quot;Codecov result&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;compatibility&quot;&gt;Compatibility&lt;&#x2F;h3&gt;
&lt;p&gt;Even though it&#x27;s a major version release, all the command-line options and usage patterns from v1 are fully inherited. This means you can update and apply it to your existing scripts or pipelines without any migration work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;expanded-features&quot;&gt;Expanded Features&lt;&#x2F;h2&gt;
&lt;p&gt;Building on the stability and development convenience gained from the switch to Rust, I&#x27;ve added several features that I&#x27;d long felt were necessary.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;support-for-more-algorithms&quot;&gt;Support for More Algorithms&lt;&#x2F;h3&gt;
&lt;p&gt;While it previously focused on HMAC-based algorithms (HS256, HS384, HS512), jwt-hack now supports asymmetric encryption algorithms like RSA (RS*) and ECDSA (ES*).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;new-key-based-functionality&quot;&gt;New Key-Based Functionality&lt;&#x2F;h3&gt;
&lt;p&gt;Going beyond simple secret-based testing, it&#x27;s now possible to generate, verify, and test tokens using private&#x2F;public keys. For example, you can easily create a JWT signed with a private key like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;ssh-keygen -t rsa -b 4096 -E SHA256 -m PEM -P &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; -f RS256.key
&lt;&#x2F;span&gt;&lt;span&gt;jwt-hack encode &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d69d85;&quot;&gt;&amp;#39;{&amp;quot;a&amp;quot;:&amp;quot;z&amp;quot;}&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; --private-key RS256.key --algorithm=RS256
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;21a2c1b4-f4da-4f7b-abfa-89a0c90c5ad9&quot; alt=&quot;Make JWT with Private key&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;new-verify-mode&quot;&gt;New Verify Mode&lt;&#x2F;h3&gt;
&lt;p&gt;Truth be told, the previous version lacked even the basic functionality to verify if a JWT&#x27;s signature was valid. This update introduces a &lt;code&gt;verify&lt;&#x2F;code&gt; mode that allows you to check a token&#x27;s signature with a secret or a public key, making the tool more complete for token testing.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;broader-attack-vector-support&quot;&gt;Broader Attack Vector Support&lt;&#x2F;h3&gt;
&lt;p&gt;One of jwt-hack&#x27;s core features is generating tokens for known attack vectors. In addition to the existing &lt;code&gt;none&lt;&#x2F;code&gt; algorithm and &lt;code&gt;jku&#x2F;x5u&lt;&#x2F;code&gt; attacks, it now supports a wider range of vectors and will continue to expand in the future:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Algorithm Confusion (tampering with the &lt;code&gt;alg&lt;&#x2F;code&gt; header to HS256)&lt;&#x2F;li&gt;
&lt;li&gt;SQL Injection in &lt;code&gt;kid&lt;&#x2F;code&gt; (inserting an SQLi payload into the &lt;code&gt;kid&lt;&#x2F;code&gt; header)&lt;&#x2F;li&gt;
&lt;li&gt;Other known attack techniques&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;cargo&quot;&gt;Cargo&lt;&#x2F;h3&gt;
&lt;p&gt;If you&#x27;re a Rust developer, you can install it directly via &lt;code&gt;cargo&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;cargo install jwt-hack
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;homebrew&quot;&gt;Homebrew&lt;&#x2F;h3&gt;
&lt;p&gt;And for one of the most exciting pieces of news: jwt-hack is now included in the official &lt;code&gt;homebrew-core&lt;&#x2F;code&gt;! You can install and update it with a simple command, no need to tap a separate repository.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;brew install jwt-hack
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;more&quot;&gt;More&lt;&#x2F;h3&gt;
&lt;p&gt;You can also get jwt-hack through various other channels, including the &lt;a href=&quot;https:&#x2F;&#x2F;snapcraft.io&#x2F;jwt-hack&quot;&gt;Snapcraft Package&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;repository&#x2F;docker&#x2F;hahwul&#x2F;jwt-hack&#x2F;general&quot;&gt;Docker Hub&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;jwt-hack&#x2F;pkgs&#x2F;container&#x2F;jwt-hack&quot;&gt;GHCR&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Breathing new life into a long-neglected project was more enjoyable than I expected. It was great to see the tool improve, but what made it particularly fun was the process of getting to know the Rust language more deeply.&lt;&#x2F;p&gt;
&lt;p&gt;Going forward, I plan to build jwt-hack into a comprehensive tool packed with various features for JWT security testing. If you have any ideas for necessary features or improvements, please don&#x27;t hesitate to leave a comment on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;jwt-hack&#x2F;issues&quot;&gt;GitHub Issues&lt;&#x2F;a&gt;. Your interest is the driving force behind this project.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>JWT-HACK</title>
        <published>2025-06-06T00:00:00+00:00</published>
        <updated>2025-06-06T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/projects/jwt-hack/" type="text/html"/>
        <id>https://www.hahwul.com/projects/jwt-hack/</id>
        <summary>JSON Web Token Hack Toolkit</summary>
        <content type="html">&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# Cargo
&lt;&#x2F;span&gt;&lt;span&gt;cargo install jwt-hack
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;# Brew
&lt;&#x2F;span&gt;&lt;span&gt;brew tap hahwul&#x2F;jwt-hack
&lt;&#x2F;span&gt;&lt;span&gt;brew install jwt-hack
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.hahwul.com&#x2F;projects&#x2F;jwt-hack&#x2F;jwt-hack.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;JWT-HACK is a CLI tool for analyzing JSON Web Tokens (JWTs). It supports &lt;code&gt;encode&lt;&#x2F;code&gt;, &lt;code&gt;decode&lt;&#x2F;code&gt;, &lt;code&gt;verify&lt;&#x2F;code&gt;, &lt;code&gt;crack&lt;&#x2F;code&gt;, and &lt;code&gt;payload&lt;&#x2F;code&gt; modes to help you analyze JWTs and discover security vulnerabilities.&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hahwul&#x2F;jwt-hack&quot;&gt;Github&lt;&#x2F;a&gt; •
  &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;jwt-hack&quot;&gt;Crates&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;ul class=&quot;timeline w-full justify-center&quot;&gt;
  &lt;li&gt;
    &lt;div class=&quot;timeline-start&quot;&gt;2020.10&lt;&#x2F;div&gt;
    &lt;div class=&quot;timeline-middle&quot;&gt;&lt;&#x2F;div&gt;
    &lt;div class=&quot;timeline-end timeline-box&quot;&gt;Started as my personal project with golang&lt;&#x2F;div&gt;
    &lt;hr &#x2F;&gt;
  &lt;&#x2F;li&gt;
  &lt;li&gt;
    &lt;hr &#x2F;&gt;
    &lt;div class=&quot;timeline-start&quot;&gt;2025.06&lt;&#x2F;div&gt;
    &lt;div class=&quot;timeline-middle&quot;&gt;&lt;&#x2F;div&gt;
    &lt;div class=&quot;timeline-end timeline-box&quot;&gt;Released v2, migrated from Go to Rust&lt;&#x2F;div&gt;
  &lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>Jekyll to Zola</title>
        <published>2025-05-31T00:00:00+00:00</published>
        <updated>2025-05-31T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/blog/2025/jekyll-to-zola/" type="text/html"/>
        <id>https://www.hahwul.com/blog/2025/jekyll-to-zola/</id>
        <summary>My journey migrating from Jekyll to Zola, a Rust-based SSG.</summary>
        <content type="html">&lt;p&gt;It&#x27;s been nearly three years since I switched back from &lt;a href=&quot;&#x2F;blog&#x2F;2022&#x2F;hugo-to-jekyll&#x2F;&quot;&gt;Hugo to Jekyll&lt;&#x2F;a&gt; in August 2022 (see that post here), and now I&#x27;m making another move – this time to a tool called Zola. I wanted to share this journey in a post. While Zola isn&#x27;t as mainstream or as large a project as Hugo or Jekyll, it has its own unique charm. I&#x27;ll briefly introduce Zola and explain why I decided to make the switch.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-zola&quot;&gt;What is Zola?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&quot;&gt;Zola&lt;&#x2F;a&gt; is a Static Site Generator (SSG) built with Rust. It uses its own template engine, Tera, and much like Hugo, it&#x27;s distributed as a single binary. This means it&#x27;s incredibly fast and doesn&#x27;t require installing a bunch of separate dependencies. Furthermore, it comes with many convenient built-in features like Sass&#x2F;SCSS compilation, syntax highlighting, table of contents generation, shortcodes, and internationalization (i18n) support, all without needing extra plugins, which makes for excellent usability.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-zola&quot;&gt;Why Zola?&lt;&#x2F;h2&gt;
&lt;p&gt;Over the past few years, my development toolkit has increasingly filled up with Rust-based tools like &lt;a href=&quot;https:&#x2F;&#x2F;caido.io&#x2F;&quot;&gt;Caido&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;helix-editor.com&#x2F;&quot;&gt;Helix&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;zed.dev&#x2F;&quot;&gt;Zed&lt;&#x2F;a&gt;. Starting this year, Rust has also become my primary programming language, and I&#x27;ve been diving deep into it. Naturally, this trend led me to explore other tools within the Rust ecosystem, and Zola was one of the appealing projects I discovered this way.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This tool and its template engine tera were born from an intense dislike of the (insane) Golang template engine and therefore of Hugo that I was using before for 6+ sites.
&lt;cite&gt;Vincent Prouillet&lt;&#x2F;cite&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;One of the main reasons I switched back from Hugo to Jekyll in the past was the complexity and, frankly, the awkwardness of Go&#x27;s template engine. It seems Vincent Prouillet, Zola&#x27;s developer, felt similarly. This shared sentiment about development philosophy played a significant role in my decision to choose Zola.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, its technical appeal was a big factor too. After considering various factors, I decided to migrate to Zola, and I&#x27;m currently very satisfied with it. Especially compared to Jekyll, where build times could be a bit disappointing and its characteristic live reloading was often slow, Zola&#x27;s live reloading is incredibly fast, which has significantly improved my writing experience. Additionally, the ability to handle internationalization (i18n) easily without complex configurations was another major factor in choosing Zola.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;design&quot;&gt;Design&lt;&#x2F;h2&gt;
&lt;p&gt;Migrating to Zola, I kept the core design philosophy of my existing Jekyll theme but rewrote the code to make it cleaner and more polished overall. Zola&#x27;s template engine, Tera, is conceptually similar to Jekyll&#x27;s Liquid but also resembles Jinja2 or Django templates, offering a more intuitive syntax. This made migrating and improving the existing logic relatively straightforward. A key difference I noticed is that with Jekyll, frequent page referencing can significantly impact build times. Zola doesn&#x27;t seem to suffer from this to the same extent, which allowed me to enhance template reusability with macros without a noticeable performance penalty. Thanks to this, I managed to shorten the migration period considerably, more so than I initially anticipated.&lt;&#x2F;p&gt;
&lt;p&gt;Visually, I stuck with the existing black-based (&lt;code&gt;#000000&lt;&#x2F;code&gt;) theme but made the white accent points a bit more eye-catching. Good examples are the interactive elements on the main page and the 404 page.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;632a5603-34bf-41c1-9cb3-19fceca82002&quot; alt=&quot;HAHWUL Main&quot; &#x2F;&gt;
&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.instagram.com&#x2F;p&#x2F;DJUO-64xTSC&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.instagram.com&#x2F;p&#x2F;DJUO-64xTSC&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This sort of feel and the way it&#x27;s expressed is something I came up with after getting inspired by a scene from the drama Loki season 2.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;user-attachments&#x2F;assets&#x2F;5ce4abb4-e870-4797-a287-9ec297c113b0&quot; alt=&quot;Loki season 2&amp;#39;s ending&quot; &#x2F;&gt;
&lt;em&gt;Loki season 2&#x27;s ending scene&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Changing the underlying tool of a blog can sometimes be arduous and lead to unexpected challenges, but it has always given me the opportunity to learn new technologies and create better outcomes. This migration to Zola was no different.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, who knows, a few years down the line, another whim might lead me to seek out a new tool. But for now, I&#x27;m extremely satisfied with Zola&#x27;s simplicity, speed, and the sense of stability and development philosophy that comes from it being Rust-based. Looking ahead, I plan to continue cultivating my blog while also contributing, even in small ways, to the Zola project, hoping to help this wonderful tool continue to evolve.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
    <entry xml:lang="en">
        <title>How to Secure Cookies</title>
        <published>2025-05-28T00:00:00+00:00</published>
        <updated>2025-06-07T00:00:00+00:00</updated>
        <link href="https://www.hahwul.com/sec/web-security/cookies/" type="text/html"/>
        <id>https://www.hahwul.com/sec/web-security/cookies/</id>
        <summary>Web Cookie Explained and Secured</summary>
        <content type="html">&lt;p&gt;Cookies play a crucial role in web applications, but at the same time, they require careful attention to security settings. In this post, we&#x27;ll take an in-depth look at various attributes to manage cookies more securely. We&#x27;ll particularly explore how attributes like &lt;code&gt;SameSite&lt;&#x2F;code&gt;, along with &lt;code&gt;HttpOnly&lt;&#x2F;code&gt;, &lt;code&gt;Secure&lt;&#x2F;code&gt;, &lt;code&gt;Path&lt;&#x2F;code&gt;, &lt;code&gt;Domain&lt;&#x2F;code&gt;, &lt;code&gt;Expires&lt;&#x2F;code&gt;, and &lt;code&gt;Max-Age&lt;&#x2F;code&gt;, each contribute to bolstering cookie security.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-importance-of-cookie-security&quot;&gt;The Importance of Cookie Security&lt;&#x2F;h2&gt;
&lt;p&gt;Cookies can store sensitive information, such as session IDs that identify users. If these cookies are stolen by an attacker, there&#x27;s a risk of session hijacking, allowing unauthorized access to user accounts. Furthermore, cookies can be exploited in Cross-Site Request Forgery (CSRF) attacks, leading users to perform unintended actions on a website. Therefore, it&#x27;s critically important to understand each cookie attribute and configure them correctly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;key-cookie-attributes-for-enhanced-security&quot;&gt;Key Cookie Attributes for Enhanced Security&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s delve into the core security attributes that will help make your cookies robust.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;httponly-protecting-against-xss&quot;&gt;HttpOnly: Protecting Against XSS&lt;&#x2F;h3&gt;
&lt;p&gt;When the &lt;code&gt;HttpOnly&lt;&#x2F;code&gt; attribute is set, the cookie cannot be accessed via JavaScript&#x27;s &lt;code&gt;document.cookie&lt;&#x2F;code&gt; API. This is effective in preventing attackers from stealing user cookie values (especially session IDs) through malicious scripts injected via Cross-Site Scripting (XSS) vulnerabilities on a website.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Example (HTTP Header):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;Set-Cookie: session_id=verysecretvalue; HttpOnly
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s advisable to set the &lt;code&gt;HttpOnly&lt;&#x2F;code&gt; attribute for sensitive cookies, particularly those related to user authentication. While it&#x27;s a fundamental defense mechanism, its effectiveness is significant.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;secure-ensuring-encrypted-transmission&quot;&gt;Secure: Ensuring Encrypted Transmission&lt;&#x2F;h3&gt;
&lt;p&gt;A cookie with the &lt;code&gt;Secure&lt;&#x2F;code&gt; attribute is only transmitted over an HTTPS connection. This means the cookie won&#x27;t be sent over unencrypted HTTP connections, thereby protecting it from Man-in-the-Middle (MITM) attacks where an attacker might intercept the cookie on the network.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Example (HTTP Header):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;Set-Cookie: user_preference=dark_mode; Secure
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As most modern websites use HTTPS by default, applying the &lt;code&gt;Secure&lt;&#x2F;code&gt; attribute is also recommended. It&#x27;s especially crucial to remember that if you use the &lt;code&gt;SameSite=None&lt;&#x2F;code&gt; policy, the &lt;code&gt;Secure&lt;&#x2F;code&gt; attribute must be set concomitantly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;samesite-the-csrf-shield-in-depth&quot;&gt;SameSite: The CSRF Shield (In-Depth)&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;SameSite&lt;&#x2F;code&gt; attribute plays a pivotal role in defending against Cross-Site Request Forgery (CSRF) attacks. This attribute instructs the browser whether a cookie should be sent with requests initiated from other origins (cross-site). In simpler terms, it controls whether a cookie from your website (&lt;code&gt;siteA.com&lt;&#x2F;code&gt;) will be included in a request sent from an unrelated website (&lt;code&gt;evil.com&lt;&#x2F;code&gt;) to &lt;code&gt;siteA.com&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;SameSite&lt;&#x2F;code&gt; attribute can have one of three values:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Strict&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: This provides the strongest level of protection. Cookies set with &lt;code&gt;SameSite=Strict&lt;&#x2F;code&gt; are only sent if the request originates from the same site (a first-party context) where the user is currently interacting. For example, if a user clicks a link from an external site to yours, cookies set to &lt;code&gt;Strict&lt;&#x2F;code&gt; will not be sent. Consequently, if a login session cookie is set to &lt;code&gt;Strict&lt;&#x2F;code&gt;, the user might appear logged out when arriving via an external link and may need to log in again.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to use&lt;&#x2F;strong&gt;: Suitable for cookies related to actions that change state, such as password changes, content creation, or order processing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Example&lt;&#x2F;strong&gt;: &lt;code&gt;Set-Cookie: session_id=verysecretvalue; SameSite=Strict&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Lax&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: This is a slightly more relaxed setting than &lt;code&gt;Strict&lt;&#x2F;code&gt; but still offers excellent security. &lt;code&gt;Lax&lt;&#x2F;code&gt; generally behaves like &lt;code&gt;Strict&lt;&#x2F;code&gt; and doesn&#x27;t send cookies on most cross-site requests. However, it does send cookies on cross-site requests if it&#x27;s a top-level navigation using a safe HTTP method (GET, HEAD, OPTIONS, TRACE), such as when a user clicks a link to navigate to your site from an external one. This allows users to maintain their logged-in state when arriving from external links without compromising on CSRF protection for most scenarios. Many modern browsers have adopted &lt;code&gt;Lax&lt;&#x2F;code&gt; as the default for cookies where no &lt;code&gt;SameSite&lt;&#x2F;code&gt; attribute is specified.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to use&lt;&#x2F;strong&gt;: Well-suited for general session management cookies, offering a good balance between security and user experience.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Example&lt;&#x2F;strong&gt;: &lt;code&gt;Set-Cookie: tracking_id=randomstring; SameSite=Lax&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;None&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: With this setting, cookies are sent on all requests, both same-site and cross-site, similar to how cookies behaved before the &lt;code&gt;SameSite&lt;&#x2F;code&gt; attribute was introduced. However, a critical requirement is that if &lt;code&gt;SameSite=None&lt;&#x2F;code&gt; is used, the &lt;strong&gt;&lt;code&gt;Secure&lt;&#x2F;code&gt; attribute must also be specified&lt;&#x2F;strong&gt; (&lt;code&gt;SameSite=None; Secure&lt;&#x2F;code&gt;). This enforces that the cookie only operates over HTTPS connections, a measure to mitigate security risks.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to use&lt;&#x2F;strong&gt;: Used sparingly, for cases where cookies are explicitly needed in a cross-site context, such as when your service is embedded within an iframe on an external service and requires cookie-based authentication, for Single Sign-On (SSO) scenarios spanning multiple domains, or for ad and analytics scripts.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Important&lt;&#x2F;strong&gt;: As this is the most flexible option, it also carries potential risks. Use it cautiously and only when absolutely necessary, and never omit the &lt;code&gt;Secure&lt;&#x2F;code&gt; attribute.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Example&lt;&#x2F;strong&gt;: &lt;code&gt;Set-Cookie: third_party_widget_session=externalvalue; SameSite=None; Secure&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Which &lt;code&gt;SameSite&lt;&#x2F;code&gt; value should you choose?&lt;&#x2F;strong&gt; If there&#x27;s no specific reason otherwise, using &lt;strong&gt;&lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt; as a default&lt;&#x2F;strong&gt; is a good practice. For cookies handling highly sensitive operations, &lt;code&gt;SameSite=Strict&lt;&#x2F;code&gt; can be considered. If cookies are absolutely required for cross-site requests, then &lt;code&gt;SameSite=None; Secure&lt;&#x2F;code&gt; is the appropriate approach.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;path-defining-cookie-scope-by-url-path&quot;&gt;Path: Defining Cookie Scope by URL Path&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;Path&lt;&#x2F;code&gt; attribute restricts the URL path on the server to which the cookie will be sent. For instance, a cookie set with &lt;code&gt;Path=&#x2F;admin&lt;&#x2F;code&gt; will only be sent for requests to &lt;code&gt;&#x2F;admin&lt;&#x2F;code&gt; and its subdirectories (e.g., &lt;code&gt;&#x2F;admin&#x2F;users&lt;&#x2F;code&gt;). It will not be sent for requests to other paths like &lt;code&gt;&#x2F;dashboard&lt;&#x2F;code&gt; or the root (&lt;code&gt;&#x2F;&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Example (HTTP Header):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;Set-Cookie: admin_session_token=secretadminstuff; Path=&#x2F;admin; HttpOnly; Secure
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This helps minimize the cookie&#x27;s exposure by preventing it from being unnecessarily transmitted to other application contexts. If not specified, the default path is the path of the document that set the cookie.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;domain-defining-cookie-scope-by-host&quot;&gt;Domain: Defining Cookie Scope by Host&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;Domain&lt;&#x2F;code&gt; attribute specifies the host(s) to which the cookie will be sent. If set to &lt;code&gt;Domain=example.com&lt;&#x2F;code&gt;, the cookie will be sent for requests to &lt;code&gt;example.com&lt;&#x2F;code&gt; and all its subdomains (e.g., &lt;code&gt;www.example.com&lt;&#x2F;code&gt;, &lt;code&gt;api.example.com&lt;&#x2F;code&gt;). If the &lt;code&gt;Domain&lt;&#x2F;code&gt; attribute is omitted, the cookie is sent only to the exact host that set it (excluding subdomains).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Example (HTTP Header):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot;&gt;&lt;code&gt;&lt;span&gt;Set-Cookie: site_wide_preference=blue_theme; Domain=example.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Overly broad &lt;code&gt;Domain&lt;&#x2F;code&gt; settings (e.g., to a TLD like &lt;code&gt;.com&lt;&#x2F;code&gt;) are a security risk and are rejected by browsers. Also, the &lt;code&gt;Domain&lt;&#x2F;code&gt; attribute can only be set to the current host&#x27;s parent domain; it cannot be set to a completely different domain.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;expires-max-age-managing-cookie-lifetime&quot;&gt;Expires &amp;amp; Max-Age: Managing Cookie Lifetime&lt;&#x2F;h3&gt;
&lt;p&gt;It&#x27;s important to manage cookie persistence by setting an expiration time. Two attributes are used for this: &lt;code&gt;Expires&lt;&#x2F;code&gt; and &lt;code&gt;Max-Age&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Expires&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: Specifies the exact date and time (in UTC) when the cookie will expire.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Example&lt;&#x2F;strong&gt;: &lt;code&gt;Set-Cookie: legacy_cookie=data; Expires=Fri, 31 Dec 2025 23:59:59 GMT&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Max-Age&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: Specifies the duration, in seconds, until the cookie expires. For example, &lt;code&gt;Max-Age=3600&lt;&#x2F;code&gt; means the cookie is valid for one hour. &lt;code&gt;Max-Age&lt;&#x2F;code&gt; takes precedence over &lt;code&gt;Expires&lt;&#x2F;code&gt;. If &lt;code&gt;Max-Age&lt;&#x2F;code&gt; is set to 0 or a negative value, the cookie is deleted immediately.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Example&lt;&#x2F;strong&gt;: &lt;code&gt;Set-Cookie: short_lived_token=tempdata; Max-Age=3600&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If neither &lt;code&gt;Expires&lt;&#x2F;code&gt; nor &lt;code&gt;Max-Age&lt;&#x2F;code&gt; is set, the cookie is treated as a &lt;strong&gt;session cookie&lt;&#x2F;strong&gt; and is deleted when the browser is closed. However, as browser settings might restore sessions (and thus session cookies), it&#x27;s a safer practice to set an explicit, short expiration time for sensitive information.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;advanced-cookie-theft-detection-and-mitigation&quot;&gt;Advanced Cookie Theft Detection and Mitigation&lt;&#x2F;h2&gt;
&lt;p&gt;Even with all the security attributes correctly configured, there remains a risk of cookie theft through malware or phishing attacks. Therefore, implementing detection mechanisms for identifying potentially stolen cookies is crucial for comprehensive security.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;environment-based-detection&quot;&gt;Environment-Based Detection&lt;&#x2F;h3&gt;
&lt;p&gt;By storing user environment information when a session is established, you can detect suspicious changes that might indicate session hijacking:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IP Address Change&lt;&#x2F;strong&gt;: Significant changes in IP address or geo-location might indicate session hijacking. For instance, if a user who typically logs in from New York suddenly appears to be accessing your site from a different country.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;User-Agent Change&lt;&#x2F;strong&gt;: Different browsers or devices sending requests with the same cookie.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Language Preference Change&lt;&#x2F;strong&gt;: Sudden changes in the &lt;code&gt;Accept-Language&lt;&#x2F;code&gt; header.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Access Time Patterns&lt;&#x2F;strong&gt;: Activity during unusual hours for a particular user.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Example Implementation (Server-side):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1e1e1e;color:#dcdcdc;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; When creating a session
&lt;&#x2F;span&gt;&lt;span&gt;session.save({
&lt;&#x2F;span&gt;&lt;span&gt;  ip: request.clientIP,
&lt;&#x2F;span&gt;&lt;span&gt;  userAgent: request.headers.userAgent,
&lt;&#x2F;span&gt;&lt;span&gt;  language: request.headers.acceptLanguage,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Additional environment information
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; On subsequent requests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(hasSignificantEnvironmentChange(session, request)) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#608b4e;&quot;&gt;&#x2F;&#x2F; Take action - trigger additional verification
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;risk-based-session-validation&quot;&gt;Risk-Based Session Validation&lt;&#x2F;h3&gt;
&lt;p&gt;When suspicious activity is detected, implement graduated response mechanisms:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Low Risk&lt;&#x2F;strong&gt;: Display a CAPTCHA to verify human interaction&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Medium Risk&lt;&#x2F;strong&gt;: Request additional verification (security question, email verification code)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;High Risk&lt;&#x2F;strong&gt;: Force re-authentication with full credentials and potentially 2FA&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Critical Risk&lt;&#x2F;strong&gt;: Immediately invalidate the session and notify the user via alternate channels&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It&#x27;s important to balance security with user experience—too many verification requests can frustrate legitimate users. Consider implementing risk-based verification only for sensitive operations like payment processing, account settings changes, or password updates.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;device-bound-session-credentials&quot;&gt;Device-Bound Session Credentials&lt;&#x2F;h3&gt;
&lt;p&gt;A promising approach to mitigating cookie theft is using device-bound session credentials. This technique binds session cookies to the specific device through cryptographic means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The browser generates a public-private key pair during login&lt;&#x2F;li&gt;
&lt;li&gt;The session is cryptographically tied to this key pair&lt;&#x2F;li&gt;
&lt;li&gt;Even if the cookie is stolen, the attacker cannot use it without the private key stored securely on the original device&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While this technology is still evolving and not yet widely supported, it represents the future direction for secure session management as it changes cookies from simple bearer tokens to sender-constrained tokens.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion-layered-security-is-key&quot;&gt;Conclusion: Layered Security is Key&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve explored various attributes for cookie security in detail: &lt;code&gt;HttpOnly&lt;&#x2F;code&gt;, &lt;code&gt;Secure&lt;&#x2F;code&gt;, &lt;code&gt;SameSite&lt;&#x2F;code&gt;, &lt;code&gt;Path&lt;&#x2F;code&gt;, &lt;code&gt;Domain&lt;&#x2F;code&gt;, &lt;code&gt;Expires&lt;&#x2F;code&gt;, and &lt;code&gt;Max-Age&lt;&#x2F;code&gt;, as well as advanced techniques for detecting and mitigating cookie theft. Understanding how each security measure works and its benefits is crucial.&lt;&#x2F;p&gt;
&lt;p&gt;The core of cookie security lies in using these protections appropriately in combination. No single setting can defend against all threats, so always consider a defense-in-depth strategy to protect your web applications and users. In addition to configuring cookie attributes correctly, implementing server-side detection mechanisms provides an essential additional layer of security.&lt;&#x2F;p&gt;
</content>
        <author>
          <name>hahwul</name>
        </author>
    </entry>
</feed>
