<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Dalfox (한국어)</title>
    <link>https://dalfox.hahwul.com/ko/</link>
    <description>Powerful open-source XSS scanner and automation utility for reflected, stored, and DOM-based XSS with AST-level verification.</description>
    <language>ko</language>
    <atom:link href="https://dalfox.hahwul.com/ko/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>설정</title>
      <link>https://dalfox.hahwul.com/ko/getting-started/configuration/</link>
      <guid>https://dalfox.hahwul.com/ko/getting-started/configuration/</guid>
      <description>Dalfox 스캔 플래그를 TOML 또는 JSON 설정 파일에 저장하고 기본값, 우선순위, CLI 재정의를 이해합니다.</description>
      <content:encoded><![CDATA[<p>Dalfox는 시작할 때 설정 파일을 읽으므로 매번 같은 플래그를 붙일 필요가 없습니다. 명시적인 CLI 플래그가 설정 파일의 값을 항상 덮어쓰니, 여기에 &quot;기본값&quot;을 두어도 안전합니다.</p>
<h2 id="파일-위치">파일 위치</h2>
<p>Dalfox는 <code>XDG_CONFIG_HOME</code>이 설정되어 있고 비어 있지 않으면 <code>$XDG_CONFIG_HOME/dalfox/config.toml</code>을, 그렇지 않으면 <code>$HOME/.config/dalfox/config.toml</code>을 읽습니다. 같은 디렉터리에 <code>config.toml</code>이 없으면 <code>config.json</code>을 읽습니다.</p>
<p><code>--config</code>로 다른 위치를 지정할 수 있습니다.</p>
<pre><code class="language-bash hljs">dalfox --config ./dalfox.toml scan https://target.app
</code></pre>
<p>파일이 없으면 Dalfox는 처음 실행할 때 기본 경로에 템플릿을 만듭니다. 모든 줄이 주석 처리되어 있으므로 직접 고치기 전까지는 아무것도 바꾸지 않습니다.</p>
<h2 id="최소-설정">최소 설정</h2>
<pre><code class="language-toml hljs"><span class="hljs-punctuation">[</span>scan<span class="hljs-punctuation">]</span>
format <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;json&quot;</span>
output <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;results.json&quot;</span>
timeout <span class="hljs-punctuation">=</span> <span class="hljs-number">15</span>
workers <span class="hljs-punctuation">=</span> <span class="hljs-number">100</span>
encoders <span class="hljs-punctuation">=</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;url&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;html&quot;</span><span class="hljs-punctuation">]</span>
</code></pre>
<p>스캔을 실행하면 해당 플래그가 자동으로 적용됩니다.</p>
<pre><code class="language-bash hljs">dalfox <span class="hljs-string">&#39;https://target.app/?q=test&#39;</span>
<span class="hljs-comment"># → workers=100으로 스캔하고 JSON 결과를 results.json에 씀</span>
</code></pre>
<h2 id="우선순위">우선순위</h2>
<pre><code>CLI 플래그  &gt;  설정 파일  &gt;  내장 기본값
</code></pre>
<p>명령줄에 지정한 것이 우선합니다. 그래서 설정 파일에는 무난한 기본값을 두고, 스캔할 때마다 필요한 값만 덮어쓰면 됩니다.</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># 설정 파일은 workers=100이지만, 이번 빠른 스캔은 20으로</span>
dalfox scan --workers <span class="hljs-number">20</span> https://target.app
</code></pre>
<p>스캔 플래그를 쓰려면 <code>scan</code> 서브커맨드를 명시해야 합니다. <code>dalfox &lt;TARGET&gt;</code> 축약형은 대상과 전역 플래그만 받습니다. <code>deep_scan = true</code>처럼 설정 파일에서 켠 스위치는 끄는 명령줄 플래그가 없어서 모든 실행에 그대로 적용됩니다. 자세한 내용은 <a href="https://dalfox.hahwul.com/ko/reference/config/#%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84">우선순위</a>를 참고하세요.</p>
<h2 id="형식">형식</h2>
<p>Dalfox는 TOML과 JSON을 모두 지원합니다. TOML이 기본값이며, 도구나 UI에서 파일을 생성하는 경우 JSON이 편리합니다.</p>
<pre><code class="language-toml hljs"><span class="hljs-comment"># ~/.config/dalfox/config.toml</span>
<span class="hljs-punctuation">[</span>scan<span class="hljs-punctuation">]</span>
format <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;sarif&quot;</span>
silence <span class="hljs-punctuation">=</span> <span class="hljs-literal">true</span>
</code></pre>
<pre><code class="language-json hljs"><span class="hljs-punctuation">{</span>
  <span class="hljs-name">&quot;scan&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-name">&quot;format&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;sarif&quot;</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;silence&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">true</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h2 id="무엇을-설정할-수-있나요">무엇을 설정할 수 있나요?</h2>
<p><code>dalfox scan</code> 아래에 CLI 플래그가 있는 모든 항목은 <code>[scan]</code> 테이블에 넣을 수 있습니다(<code>--blind</code>의 키 이름은 <code>blind_callback_url</code>). 이 파일은 CLI 스캔에만 적용되며 <code>dalfox server</code>와 <code>dalfox mcp</code>는 읽지 않습니다. 자주 쓰는 키 몇 가지입니다.</p>
<table>
<thead>
<tr>
<th>키</th>
<th>예시</th>
<th>기능</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>format</code></td>
<td><code>&quot;json&quot;</code></td>
<td>출력 형식(<code>plain</code>, <code>json</code>, <code>jsonl</code>, <code>markdown</code>, <code>sarif</code>, <code>toml</code>)</td>
</tr>
<tr>
<td><code>output</code></td>
<td><code>&quot;report.json&quot;</code></td>
<td>기본 출력 파일</td>
</tr>
<tr>
<td><code>silence</code></td>
<td><code>true</code></td>
<td>로그를 억제하고 탐지 결과만 출력</td>
</tr>
<tr>
<td><code>timeout</code></td>
<td><code>15</code></td>
<td>요청 타임아웃(초)</td>
</tr>
<tr>
<td><code>delay</code></td>
<td><code>200</code></td>
<td>요청 간 지연(ms)</td>
</tr>
<tr>
<td><code>workers</code></td>
<td><code>100</code></td>
<td>대상당 동시 워커 수</td>
</tr>
<tr>
<td><code>encoders</code></td>
<td><code>[&quot;url&quot;,&quot;html&quot;,&quot;base64&quot;]</code></td>
<td>페이로드 인코더</td>
</tr>
<tr>
<td><code>remote_payloads</code></td>
<td><code>[&quot;portswigger&quot;]</code></td>
<td>원격 페이로드 소스</td>
</tr>
<tr>
<td><code>remote_wordlists</code></td>
<td><code>[&quot;burp&quot;]</code></td>
<td>원격 파라미터 워드리스트</td>
</tr>
<tr>
<td><code>headers</code></td>
<td><code>[&quot;Accept: text/html&quot;]</code></td>
<td>추가 요청 헤더</td>
</tr>
<tr>
<td><code>user_agent</code></td>
<td><code>&quot;Dalfox Scanner&quot;</code></td>
<td>기본 User-Agent</td>
</tr>
<tr>
<td><code>waf_bypass</code></td>
<td><code>&quot;auto&quot;</code></td>
<td>WAF 우회 모드(<code>auto</code>, 탐지만 하려면 <code>off</code>)</td>
</tr>
<tr>
<td><code>insecure</code></td>
<td><code>true</code></td>
<td>TLS 인증서 검증 건너뛰기(<code>false</code>면 검증 수행)</td>
</tr>
<tr>
<td><code>follow_redirects</code></td>
<td><code>true</code></td>
<td>3xx 응답 따라가기</td>
</tr>
</tbody>
</table>
<p>모든 키는 <a href="https://dalfox.hahwul.com/ko/reference/config/">설정 파일 레퍼런스</a>를 참고하세요.</p>
<h2 id="시크릿">시크릿</h2>
<p>설정 파일을 커밋한다면 API 키, 베어러 토큰, blind-XSS 콜백 호스트명은 파일에서 빼세요. Dalfox가 환경 변수에서 읽는 시크릿은 REST 서버의 API 키 하나뿐입니다.</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># .env 또는 셸 프로필</span>
<span class="hljs-built_in">export</span> <span class="hljs-variable">DALFOX_API_KEY</span><span class="hljs-operator">=</span><span class="hljs-string">&quot;...&quot;</span>
</code></pre>
<p>나머지(<code>-H &quot;Authorization: …&quot;</code>, <code>--cookies</code>, <code>-b</code>)는 명령줄에서만 넘기고 파일에는 남기지 마세요.</p>
<h2 id="다음-단계">다음 단계</h2>
<ul>
<li><a href="https://dalfox.hahwul.com/ko/getting-started/quick-start/">첫 스캔 실행하기</a></li>
<li><a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/">스캐닝 모드 살펴보기</a></li>
<li><a href="https://dalfox.hahwul.com/ko/reference/cli/">전체 CLI 레퍼런스 보기</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>설치</title>
      <link>https://dalfox.hahwul.com/ko/getting-started/installation/</link>
      <guid>https://dalfox.hahwul.com/ko/getting-started/installation/</guid>
      <description>macOS, Linux, Windows, NixOS, Arch Linux에 오픈소스 XSS 스캐너 Dalfox를 설치하거나 소스에서 빌드합니다.</description>
      <content:encoded><![CDATA[<p>플랫폼에 맞는 설치 방법을 고르세요. Dalfox는 별도로 관리할 런타임 없이 단일 실행 파일 하나로 배포됩니다.</p>
<h2 id="homebrew-macos-linux">Homebrew (macOS &amp; Linux)</h2>
<pre><code class="language-bash hljs">brew install dalfox
</code></pre>
<p>Homebrew formula는 최신 안정 버전을 따라갑니다. 출처: <a href="https://formulae.brew.sh/formula/dalfox">formulae.brew.sh/formula/dalfox</a>.</p>
<p>프로젝트 자체 tap은 같은 릴리스를 소스에서 빌드하며(그래서 Rust도 함께 설치됩니다), man 페이지와 셸 자동완성까지 설치합니다.</p>
<pre><code class="language-bash hljs">brew install hahwul/dalfox/dalfox
</code></pre>
<h2 id="snap-ubuntu-linux">Snap (Ubuntu / Linux)</h2>
<pre><code class="language-bash hljs">sudo snap install dalfox
</code></pre>
<h2 id="arch-linux-aur">Arch Linux (AUR)</h2>
<p>AUR 헬퍼 사용(권장):</p>
<pre><code class="language-bash hljs">yay -S dalfox
<span class="hljs-comment"># 또는</span>
paru -S dalfox
</code></pre>
<p><a href="https://aur.archlinux.org/packages/dalfox">AUR 패키지</a>에서 직접 빌드:</p>
<pre><code class="language-bash hljs">git clone https://aur.archlinux.org/dalfox.git
<span class="hljs-built_in">cd</span> dalfox
makepkg -si
</code></pre>
<h2 id="nix-nixos">Nix &amp; NixOS</h2>
<pre><code class="language-bash hljs"><span class="hljs-comment"># 설치 없이 한 번만 실행</span>
nix-shell -p dalfox

<span class="hljs-comment"># Nix flakes: GitHub에서 최신 버전 실행</span>
nix run github:hahwul/dalfox -- scan https://example.com

<span class="hljs-comment"># 프로필에 설치</span>
nix profile install github:hahwul/dalfox
</code></pre>
<p>Dalfox는 nixpkgs에 등록되어 있습니다. 최신 릴리스는 먼저 <code>unstable</code>에 올라오므로, 릴리스 사이 기간에는 위 flake 쪽이 <code>nixpkgs</code>보다 앞서 있습니다.</p>
<p>flake가 지원하는 시스템은 <code>x86_64-linux</code>, <code>aarch64-linux</code>, <code>aarch64-darwin</code>입니다. nixpkgs <code>unstable</code>이 <code>x86_64-darwin</code>을 제외했기 때문에 Intel macOS는 빠져 있으니, 아래의 <code>macos-x86_64</code> 릴리스 아카이브를 쓰세요.</p>
<p>이 flake가 고정한 nixpkgs 대신 사용자의 <code>nixpkgs</code>로 Dalfox를 빌드하려면 오버레이를 쓰세요. 시스템 flake에서는 이렇게 선언합니다.</p>
<pre><code class="language-nix hljs"><span class="hljs-comment"># flake.nix</span>
<span class="hljs-punctuation">{</span>
  inputs<span class="hljs-operator">.</span>dalfox<span class="hljs-operator">.</span>url <span class="hljs-operator">=</span> <span class="hljs-string">&quot;github:hahwul/dalfox&quot;</span><span class="hljs-punctuation">;</span>

  outputs <span class="hljs-operator">=</span> <span class="hljs-punctuation">{</span> self<span class="hljs-operator">,</span> nixpkgs<span class="hljs-operator">,</span> dalfox<span class="hljs-operator">,</span> <span class="hljs-operator">...</span> <span class="hljs-punctuation">}</span><span class="hljs-operator">@</span>inputs<span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    nixosConfigurations<span class="hljs-operator">.</span>myhost <span class="hljs-operator">=</span> nixpkgs<span class="hljs-operator">.</span>lib<span class="hljs-operator">.</span>nixosSystem <span class="hljs-punctuation">{</span>
      specialArgs <span class="hljs-operator">=</span> <span class="hljs-punctuation">{</span> <span class="hljs-keyword">inherit</span> inputs<span class="hljs-punctuation">;</span> <span class="hljs-punctuation">};</span>
      modules <span class="hljs-operator">=</span> <span class="hljs-punctuation">[</span> <span class="hljs-regexp">./configuration.nix</span> <span class="hljs-punctuation">];</span>
    <span class="hljs-punctuation">};</span>
  <span class="hljs-punctuation">};</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>그리고 <code>inputs</code>를 전달받는 모듈에서 적용합니다.</p>
<pre><code class="language-nix hljs"><span class="hljs-comment"># configuration.nix</span>
<span class="hljs-punctuation">{</span> pkgs<span class="hljs-operator">,</span> inputs<span class="hljs-operator">,</span> <span class="hljs-operator">...</span> <span class="hljs-punctuation">}:</span>
<span class="hljs-punctuation">{</span>
  nixpkgs<span class="hljs-operator">.</span>overlays <span class="hljs-operator">=</span> <span class="hljs-punctuation">[</span> inputs<span class="hljs-operator">.</span>dalfox<span class="hljs-operator">.</span>overlays<span class="hljs-operator">.</span>default <span class="hljs-punctuation">];</span>
  environment<span class="hljs-operator">.</span>systemPackages <span class="hljs-operator">=</span> <span class="hljs-punctuation">[</span> pkgs<span class="hljs-operator">.</span>dalfox <span class="hljs-punctuation">];</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>flake는 Dalfox 자체를 개발할 때 쓰는 셸도 제공합니다. Rust 툴체인과 <a href="https://github.com/casey/just"><code>just</code></a>, 테스트 하네스가 필요로 하는 Crystal 런타임이 들어 있고 <code>dalfox</code> 바이너리는 포함하지 않습니다.</p>
<pre><code class="language-bash hljs">git clone https://github.com/hahwul/dalfox <span class="hljs-operator">&amp;&amp;</span> <span class="hljs-built_in">cd</span> dalfox
nix develop
</code></pre>
<p><a href="https://direnv.net">direnv</a>를 설치했다면 저장소의 <code>.envrc</code> 덕분에 <code>direnv allow</code> 한 번으로 같은 셸이 자동으로 활성화됩니다.</p>
<h2 id="docker">Docker</h2>
<p>멀티 아키텍처 이미지(<code>linux/amd64</code>, <code>linux/arm64</code>)가 Docker Hub와 GitHub Container Registry에 올라갑니다. 바이너리는 <code>/app/dalfox</code>에 있고 이미지에 엔트리포인트가 없으므로 명령에 바이너리를 직접 지정하세요.</p>
<pre><code class="language-bash hljs">docker run --rm hahwul/dalfox:latest ./dalfox scan https://example.com

<span class="hljs-comment"># 또는 GHCR에서</span>
docker run --rm ghcr.io/hahwul/dalfox:latest ./dalfox scan https://example.com
</code></pre>
<p><code>/app</code>이 이미지의 <code>PATH</code>에 없으므로 <code>dalfox</code>만 쓰면 실행되지 않습니다. URL 목록을 스캔하려면 파일을 컨테이너에 마운트하거나 <code>-i</code>로 파이프하세요.</p>
<pre><code class="language-bash hljs">docker run --rm -v <span class="hljs-string">&quot;</span><span class="hljs-variable">$PWD</span><span class="hljs-string">:/data&quot;</span> hahwul/dalfox:latest ./dalfox scan /data/urls.txt
cat urls.txt <span class="hljs-punctuation">|</span> docker run --rm -i hahwul/dalfox:latest ./dalfox scan
</code></pre>
<p><code>latest</code>는 최신 릴리스입니다. 릴리스마다 <code>v&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code> 태그가 붙고, Docker Hub에는 <code>v&lt;major&gt;.&lt;minor&gt;</code>와 <code>v&lt;major&gt;</code> 태그도 붙습니다. <code>ghcr.io/hahwul/dalfox:main</code>은 <code>main</code> 브랜치를 따라갑니다.</p>
<h2 id="cargo-cratesio">Cargo (crates.io)</h2>
<pre><code class="language-bash hljs">cargo install dalfox
</code></pre>
<p>Rust 1.93 이상이 필요합니다(크레이트의 <code>rust-version</code>). <code>~/.cargo/bin/dalfox</code>에 빌드됩니다.</p>
<h2 id="사전-빌드된-바이너리">사전 빌드된 바이너리</h2>
<p><a href="https://github.com/hahwul/dalfox/releases">github.com/hahwul/dalfox/releases</a>에서 OS/아키텍처에 맞는 릴리스 아카이브를 내려받아 압축을 풀고, 바이너리를 <code>PATH</code>에 있는 경로(<code>/usr/local/bin</code>, <code>~/.local/bin</code> 등)에 두면 됩니다.</p>
<p>릴리스마다 다음 빌드가 함께 올라갑니다.</p>
<ul>
<li><code>macos-x86_64</code>, <code>macos-aarch64</code></li>
<li><code>linux-x86_64</code> (glibc), <code>linux-x86_64-musl</code> (정적 링크, Alpine·Docker·CI에 권장)</li>
<li><code>linux-aarch64</code> (glibc), <code>linux-aarch64-musl</code> (정적 링크)</li>
<li><code>windows-x86_64</code></li>
</ul>
<p>아카이브 이름은 <code>dalfox-v&lt;version&gt;-&lt;platform&gt;</code> 형식이며, macOS와 Linux는 <code>.tar.gz</code>, Windows는 <code>.zip</code>입니다. Linux는 두 아키텍처 모두 <code>.deb</code>와 <code>.rpm</code> 패키지도 나오고, 모든 아카이브와 패키지에는 <code>.sha256</code>이 붙으며 <code>checksum.txt</code>도 함께 올라갑니다. 릴리스마다 CycloneDX SBOM(<code>dalfox.cdx.xml</code>)도 포함됩니다.</p>
<h2 id="소스에서-빌드">소스에서 빌드</h2>
<pre><code class="language-bash hljs">git clone https://github.com/hahwul/dalfox
<span class="hljs-built_in">cd</span> dalfox
cargo build --release
<span class="hljs-comment"># 바이너리 경로: ./target/release/dalfox</span>
</code></pre>
<p>Rust 1.93 이상(2024 edition)이 필요합니다. 없다면 <a href="https://rustup.rs/">rustup</a>으로 설치하세요.</p>
<h2 id="설치-확인">설치 확인</h2>
<pre><code class="language-bash hljs">dalfox --version
</code></pre>
<p><code>dalfox 3.2.3</code> 같은 한 줄이 출력되면 됩니다.</p>
<h2 id="셸-자동완성">셸 자동완성</h2>
<p><code>hahwul/dalfox</code> Homebrew tap, AUR 패키지, <code>.deb</code> / <code>.rpm</code> 패키지, Nix flake로 설치하면 bash·zsh·fish 자동완성(과 <code>dalfox(1)</code> man 페이지)이 함께 설치되므로 따로 할 일이 없습니다.</p>
<p>코어 <code>brew install dalfox</code> formula, Snap, Docker, Cargo, 릴리스 아카이브, 소스 빌드처럼 다른 방법으로 설치했다면 직접 생성하면 됩니다.</p>
<pre><code class="language-bash hljs">dalfox completion bash &gt; /etc/bash_completion.d/dalfox
dalfox completion zsh &gt; <span class="hljs-string">&quot;</span><span class="hljs-subst">${</span><span class="hljs-variable">fpath</span><span class="hljs-punctuation">[1]</span><span class="hljs-subst">}</span><span class="hljs-string">/_dalfox&quot;</span>
dalfox completion fish &gt; ~/.config/fish/completions/dalfox.fish
</code></pre>
<p><code>powershell</code>과 <code>elvish</code>도 지원합니다. 자세한 내용은 <a href="https://dalfox.hahwul.com/ko/reference/cli/">CLI 레퍼런스</a>를 참조하세요.</p>
<h2 id="도움말-보기">도움말 보기</h2>
<p>Dalfox는 <a href="https://github.com/clap-rs/clap">clap</a>을 쓰기 때문에 도움말을 언제든 볼 수 있습니다.</p>
<pre><code class="language-bash hljs">dalfox --help
dalfox scan --help
</code></pre>
<h2 id="다음-단계">다음 단계</h2>
<p><a href="https://dalfox.hahwul.com/ko/getting-started/quick-start/">빠른 시작</a>에서 첫 스캔을 실행해 보세요. 스캔 전에 기본값을 조정하고 싶다면 <a href="https://dalfox.hahwul.com/ko/getting-started/configuration/">설정</a>으로 넘어가세요.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v2에서 마이그레이션</title>
      <link>https://dalfox.hahwul.com/ko/getting-started/migration/</link>
      <guid>https://dalfox.hahwul.com/ko/getting-started/migration/</guid>
      <description>Dalfox v2(Go)에서 v3(Rust)로 옮기면서 바뀐 scan 서브커맨드, 플래그, 제거된 기능과 대안을 확인합니다.</description>
      <content:encoded><![CDATA[<p>Dalfox v3은 기존 Go 구조를 버리고 Rust로 완전히 새로 작성한 버전입니다. Go 소스는 <a href="https://github.com/hahwul/dalfox/tree/v2"><code>v2</code> 브랜치</a>에 남아 있고 치명적인 보안 수정만 백포트됩니다. 새로운 작업은 모두 v3에서 이루어집니다.</p>
<p>이 페이지는 v2 작업 흐름을 v3에 옮기는 방법을 정리합니다. 어떤 서브커맨드가 하나로 합쳐졌고, 어떤 플래그 이름이 바뀌었고, 무엇이 왜 사라졌으며 그 대가로 무엇을 얻었는지 다룹니다.</p>
<h2 id="1-서브커맨드-통합">1. 서브커맨드 통합</h2>
<p>v3는 스캔 관련 서브커맨드를 하나의 진입점으로 모았습니다.</p>
<table>
<thead>
<tr>
<th>v2 사용법</th>
<th>v3 대응</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>dalfox url [url]</code></td>
<td><code>dalfox scan [url]</code></td>
<td><code>dalfox [url]</code>만 써도 됩니다 — <code>scan</code>이 기본 서브커맨드입니다. 다만 이 형태는 <code>--config</code>, <code>--debug</code>, <code>--no-color</code>, <code>-S</code> 외의 스캔 플래그를 받지 않습니다</td>
</tr>
<tr>
<td><code>dalfox file [file]</code></td>
<td><code>dalfox scan [file]</code></td>
<td>입력 종류는 자동으로 판별합니다</td>
</tr>
<tr>
<td><code>dalfox pipe</code></td>
<td><code>cat targets | dalfox scan</code> (또는 <code>dalfox scan --input-type pipe</code>)</td>
<td>파이프 입력은 <code>stdin</code>에서 그대로 읽습니다</td>
</tr>
<tr>
<td><code>dalfox sxss [url]</code></td>
<td><code>dalfox scan [url] --sxss</code></td>
<td>저장형 XSS는 이제 스캔 옵션입니다 — <a href="https://dalfox.hahwul.com/ko/guide/stored-xss/">저장형 XSS</a> 참고</td>
</tr>
<tr>
<td><code>dalfox server --type mcp</code></td>
<td><code>dalfox mcp</code></td>
<td>MCP는 별도의 stdio 서브커맨드가 되었습니다 — <a href="https://dalfox.hahwul.com/ko/integrations/mcp/">MCP 서버</a> 참고</td>
</tr>
<tr>
<td><code>dalfox server</code></td>
<td><code>dalfox server</code></td>
<td>기본 바인딩 주소가 v2의 <code>0.0.0.0</code>에서 <code>127.0.0.1</code>로 바뀌었습니다. 모든 인터페이스에서 받으려면 <code>--host 0.0.0.0</code>을 주세요. 포트는 그대로 <code>6664</code>이고 <code>--type</code>은 없어졌습니다 — <a href="https://dalfox.hahwul.com/ko/integrations/server/">REST API 서버</a> 참고</td>
</tr>
<tr>
<td><code>dalfox payload --entity-event-handler</code>, <code>--entity-useful-tags</code>, <code>--entity-special-chars</code>, <code>--remote-portswigger</code>, <code>--remote-payloadbox</code></td>
<td><code>dalfox payload event-handlers</code>, <code>useful-tags</code>, <code>special-chars</code>, <code>portswigger</code>, <code>payloadbox</code></td>
<td>스위치 대신 위치 인자 하나로 고릅니다. <code>--enum-*</code>, <code>--entity-gf</code>, <code>--make-bulk</code>, <code>--encoder-url</code>에 대응하는 셀렉터는 없습니다. 있는 셀렉터는 <code>dalfox payload --help</code>로 확인하세요</td>
</tr>
</tbody>
</table>
<div class="alert alert-info">
  <span class="alert-icon" aria-hidden="true">
    
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
    
  </span>
  <div class="alert-content"><p>legacy url, file, pipe 서브커맨드는 숨겨진 별칭으로 남아 있습니다. 세 가지 모두 v2 형태 그대로 동작합니다. dalfox url URL도, dalfox url -u URL도 됩니다. 새 스크립트에서는 dalfox scan URL을 쓰세요. sxss는 남지 않았습니다. 저장형 XSS 스캔은 scan 서브커맨드의 --sxss 플래그로 옮겨졌습니다.</p></div>
</div>
<p>v2의 <code>--rawdata</code>, <code>--har</code>, <code>--http</code> 입력 스위치도 없어졌습니다. 프록시로 잡아둔 raw HTTP 요청과 HAR 익스포트는 자동으로 판별되며(<code>dalfox scan request.txt</code>, <code>dalfox scan capture.har</code>), <code>--input-type raw-http</code> / <code>--input-type har</code>로 강제할 수도 있습니다. 요청 줄에 경로만 있으면 스킴은 <code>:scheme</code> 의사 헤더가 있을 때 그 값을 따르고, 없으면 HTTP/2 신호가 있거나 Host가 <code>:443</code>일 때 <code>https</code>, 그 밖에는 <code>http</code>입니다. 스킴을 고정하려면 요청 줄에 전체 URL을 쓰세요. <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#raw-http-%EB%AA%A8%EB%93%9C">Raw HTTP 모드</a>와 <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#har-%EB%AA%A8%EB%93%9C">HAR 모드</a>를 참고하세요.</p>
<h2 id="2-이름이-바뀐-플래그">2. 이름이 바뀐 플래그</h2>
<table>
<thead>
<tr>
<th>v2 플래그</th>
<th>v3 플래그</th>
<th>이유와 동작</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-w, --worker &lt;int&gt;</code></td>
<td><code>--workers &lt;int&gt;</code></td>
<td>이름이 바뀌었고 <code>-w</code> 단축형은 없습니다. 동시에 도는 스캔 워커 수를 정하며, 기본값이 100에서 50으로 줄었습니다.</td>
</tr>
<tr>
<td><code>-H, --header &lt;string&gt;</code></td>
<td><code>-H, --headers &lt;string&gt;</code></td>
<td>긴 이름만 복수형이 되었고 <code>-H</code>는 그대로입니다. 여러 번 넘길 수 있습니다.</td>
</tr>
<tr>
<td><code>-C, --cookie &lt;string&gt;</code></td>
<td><code>--cookies &lt;string&gt;</code></td>
<td>일관성을 위해 복수형으로 바꿨고 <code>-C</code> 단축형은 없습니다. 여러 번 넘길 수 있습니다.</td>
</tr>
<tr>
<td><code>-p, --param &lt;string&gt;</code></td>
<td><code>-p, --param &lt;string&gt;</code></td>
<td>같은 플래그지만 위치 접미사를 붙일 수 있습니다: <code>-p id:query</code>, <code>-p sort:body</code>, <code>-p token:header</code>.</td>
</tr>
<tr>
<td><code>--skip-mining-all</code></td>
<td><code>--skip-mining</code></td>
<td>이름만 바뀌었습니다.</td>
</tr>
<tr>
<td><code>--mining-dict=false</code>, <code>--mining-dom=false</code></td>
<td><code>--skip-mining-dict</code>, <code>--skip-mining-dom</code></td>
<td><code>--skip-*</code> 형태만 남았습니다(v2에도 있던 플래그입니다).</td>
</tr>
<tr>
<td><code>--output-request</code>, <code>--output-response</code></td>
<td><code>--include-request</code>, <code>--include-response</code></td>
<td>이름이 바뀌었고, <code>--include-all</code>은 둘 다 켭니다. 여전히 옵트인입니다.</td>
</tr>
<tr>
<td><code>--limit-result &lt;int&gt;</code></td>
<td><code>--limit &lt;int&gt;</code></td>
<td>이름이 바뀌었고, 상한에 도달하면 스캔을 멈춥니다. <code>--limit-result-type</code>은 이름 그대로입니다.</td>
</tr>
<tr>
<td><code>--trigger &lt;url&gt;</code> (<code>sxss</code>)</td>
<td><code>--sxss-url &lt;url&gt;</code></td>
<td><code>--sxss</code>만 주고 <code>--sxss-url</code>을 생략하면 폼 탐색 결과로 확인 URL을 자동 판별합니다.</td>
</tr>
<tr>
<td><code>--silence-force</code> (<code>file</code> / <code>pipe</code>)</td>
<td><code>-S, --silence</code></td>
<td>이제 <code>-S</code>가 PoC 출력만 남깁니다.</td>
</tr>
<tr>
<td><code>--mass</code>, <code>--multicast</code>, <code>--mass-worker</code> (<code>file</code> / <code>pipe</code>)</td>
<td><code>--max-concurrent-targets</code>, <code>--max-targets-per-host</code></td>
<td>대상은 항상 동시에 스캔하며, 이 두 플래그로 그 폭을 제한합니다.</td>
</tr>
</tbody>
</table>
<p>플래그 전체 목록은 <a href="https://dalfox.hahwul.com/ko/reference/cli/">CLI 레퍼런스</a>에 있습니다.</p>
<h2 id="3-사라진-기능과-대안">3. 사라진 기능과 대안</h2>
<p>v3를 빠르고 안전하게, XSS에만 집중하도록 유지하기 위해 몇몇 레거시 플래그와 그 뒤의 무거운 엔진을 걷어냈습니다.</p>
<table>
<thead>
<tr>
<th>사라진 v2 플래그</th>
<th>대안</th>
<th>이유</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--use-bav</code>, <code>--skip-bav</code></td>
<td>없음.</td>
<td><strong>범위</strong>. BAV(Basic Another Vulnerability) 점검을 제거했습니다. v3는 오직 XSS 스캐너이며, 다른 취약점 유형은 전용 스캐너를 쓰는 편이 낫습니다.</td>
</tr>
<tr>
<td><code>--found-action &lt;cmd&gt;</code>, <code>--found-action-shell</code></td>
<td><a href="https://dalfox.hahwul.com/ko/integrations/server/">REST API 웹훅</a>, 또는 stdout 파이프(<code>dalfox scan ... | post-script.sh</code>).</td>
<td><strong>보안</strong>. 결과마다 임의 셸 명령을 실행하는 구조는 RCE 위험을 불러오고 동시성도 발목 잡았습니다.</td>
</tr>
<tr>
<td><code>--skip-headless</code>, <code>--force-headless-verification</code></td>
<td>설정할 것이 없습니다 — 정적 분석이 기본으로 켜져 있습니다(<code>--skip-ast-analysis</code>로 끌 수 있습니다).</td>
<td><strong>엔진 교체</strong>. Headless Chrome(<code>chromedp</code>)을 완전히 없앴습니다. v3는 컴파일러급 JavaScript 파서(<code>oxc</code>)로 데이터 흐름과 DOM 싱크를 브라우저 없이 추적합니다. <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a> 참고.</td>
</tr>
<tr>
<td><code>--grep &lt;file&gt;</code>, <code>--skip-grepping</code></td>
<td>없음.</td>
<td><strong>엔진 교체</strong>. 정규식 응답 매칭 대신 컨텍스트를 아는 AST 분석을 씁니다. <code>--only-poc g</code>(grep 결과)도 함께 사라졌습니다.</td>
</tr>
<tr>
<td><code>--report</code>, <code>--report-format</code></td>
<td><code>-f markdown -o &lt;file&gt;</code>, <code>-f sarif -o &lt;file&gt;</code>.</td>
<td><strong>표준화</strong>. 리포트 전용 플래그를 출력 형식 플래그로 합쳤습니다 — <a href="https://dalfox.hahwul.com/ko/guide/output/">출력과 리포트</a> 참고.</td>
</tr>
<tr>
<td><code>--max-cpu</code></td>
<td>자동.</td>
<td><strong>구조 변화</strong>. 비동기 스케줄러(<code>tokio</code>)가 코어에 작업을 알아서 분배하므로 수동 CPU 고정은 의미가 없습니다.</td>
</tr>
<tr>
<td><code>--no-spinner</code></td>
<td>자동.</td>
<td><strong>UI</strong>. 스피너와 진행 표시줄은 stdout이 터미널이고 <code>-S</code>가 꺼져 있을 때만 그려집니다. 배너는 <code>-S</code>와 <code>plain</code>을 제외한 모든 출력 형식에서 빠집니다.</td>
</tr>
<tr>
<td><code>--context-aware</code>, <code>--magic-char-test</code></td>
<td>설정할 것이 없습니다.</td>
<td><strong>기본 내장</strong>. 반사되는 모든 파라미터에 문자별 프로브(<code>valid_specials</code> / <code>invalid_specials</code>)를 보내고, 그 결과로 페이로드를 고릅니다.</td>
</tr>
<tr>
<td><code>--deep-domxss</code>, <code>--detailed-analysis</code>, <code>--fast-scan</code>, <code>--har-file-path</code>, <code>--output-all</code></td>
<td>없음.</td>
<td><strong>제거됨</strong>. 대신할 v3 플래그가 없습니다. v3는 HAR 파일을 입력으로 읽지만 기록하지는 않습니다.</td>
</tr>
</tbody>
</table>
<p>헤드리스 검증이 없어졌기 때문에 결과의 증거 등급이 v2보다 중요해졌습니다. <code>[V]</code>는 파싱한 응답에서 DOM 수준으로 확인된 것이고, <code>[A]</code>는 정적 분석이 찾아낸 소스→싱크 흐름으로 브라우저에서 한 번 확인해볼 값입니다. 등급 판정은 <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a>에서 설명합니다.</p>
<h2 id="4-v3에서-새로-생긴-것">4. v3에서 새로 생긴 것</h2>
<ul>
<li><strong>MCP 서버(<code>dalfox mcp</code>)</strong> — stdio 위의 JSON-RPC로 AI 코딩 어시스턴트에 Dalfox를 노출하며, v2의 <code>server --type mcp</code>를 대신합니다. <a href="https://dalfox.hahwul.com/ko/integrations/mcp/">MCP 서버</a> 참고.</li>
<li><strong>시간 예산(<code>--scan-timeout &lt;secs&gt;</code>)</strong> — 대상별 페이로드 주입 단계에 상한을 두어, 반쯤 멈춘 서버가 실행을 붙잡지 못하게 합니다. 사전 점검, 탐색, 마이닝은 그 단계보다 먼저 실행되어 이 상한에 포함되지 않으며, 거기에는 요청 단위 <code>--timeout</code>만 적용됩니다. (<code>dalfox server</code>와 MCP의 <code>scan_timeout</code>은 작업 전체에 적용됩니다.)</li>
<li><strong>페이로드 상한(<code>--max-payloads-per-param &lt;int&gt;</code>)</strong> — 조합 폭발(우회 × 인코더)이 요청 폭주로 번지지 않게 막습니다.</li>
<li><strong>사전 점검(<code>--dry-run</code>)</strong> — 페이로드를 한 개도 보내지 않고 탐색된 파라미터와 예상 요청 수를 보여줍니다.</li>
<li><strong>적응형 WAF 우회(<code>--waf-evasion</code>)</strong> — v2에도 있던 플래그지만 그때는 <code>worker=1, delay=3s</code>로 고정된 프리셋이었습니다. v3에서는 (WAF 탐지 여부와 무관하게) 요청 간격을 무작위화하고, 차단 응답이 몰릴 때 쿨다운을 점증시킵니다. <a href="https://dalfox.hahwul.com/ko/guide/waf-bypass/">WAF 우회</a> 참고.</li>
<li><strong>HTTP 파라미터 오염(<code>--hpp</code>)</strong> — 쿼리 파라미터를 중복시켜 문자열 매칭에 의존하는 WAF를 지나갑니다.</li>
<li><strong>관리형 OAST(<code>--blind-oob</code>)</strong> — interactsh 세션을 등록하고 콜백을 그것을 일으킨 페이로드와 연결합니다. 기존의 <code>-b</code> 콜백 URL과 함께 쓸 수 있습니다.</li>
<li><strong>속도 조절과 재시도(<code>--rate-limit</code>, <code>--retries</code>)</strong> — 모든 워커가 공유하는 전역 초당 요청 상한, 그리고 5xx와 일시적 오류에 대한 백오프 재시도.</li>
<li><strong>이어서 하기와 증분 실행(<code>--state-file</code>, <code>--baseline</code>)</strong> — 이전 실행에서 끝낸 대상은 건너뛰거나, 이전 JSON 리포트 이후 새로 생긴 결과만 보고합니다.</li>
<li><strong>세션 감시</strong> — 스캔에 자격증명이 있으면 자동으로 켜지며(<code>--session-check</code>로 확인할 마커를 직접 지정할 수 있습니다), 인증된 스캔 도중 세션이 끊기면 깨끗한 결과가 아니라 미완료로 보고합니다. <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#%EC%84%B8%EC%85%98-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81">세션 모니터링</a> 참고.</li>
<li><strong>셸 자동완성(<code>dalfox completion &lt;shell&gt;</code>)</strong> — bash, zsh, fish, PowerShell, Elvish.</li>
</ul>
<h2 id="다음-단계">다음 단계</h2>
<ul>
<li><a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/">스캔 모드</a>를 다시 읽어보세요. 손이 기억하는 플래그가 옮겨졌을 수 있습니다.</li>
<li>자주 쓰는 v2 명령줄은 <a href="https://dalfox.hahwul.com/ko/getting-started/configuration/">설정 파일</a>로 옮기세요.</li>
<li><a href="https://dalfox.hahwul.com/ko/reference/cli/">CLI 레퍼런스</a>에서 v2에 아예 없던 플래그들을 훑어보세요.</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>빠른 시작</title>
      <link>https://dalfox.hahwul.com/ko/getting-started/quick-start/</link>
      <guid>https://dalfox.hahwul.com/ko/getting-started/quick-start/</guid>
      <description>5분 안에 첫 Dalfox XSS 스캔을 실행하고 파일, 파이프라인, JSON 출력, 인증, 블라인드 XSS를 시도합니다.</description>
      <content:encoded><![CDATA[<p>이 페이지는 설치부터 검증된 탐지 결과까지 단계별로 안내합니다. 실제 출력을 확인할 수 있도록 의도적으로 취약하게 만든 데모 대상을 사용합니다.</p>
<div class="alert alert-warning">
  <span class="alert-icon" aria-hidden="true">
    
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
    
  </span>
  <div class="alert-content"><p>테스트 권한이 있는 대상만 스캔하세요. Dalfox는 실제 XSS 페이로드를 전송합니다.</p></div>
</div>
<h2 id="1-단일-url-스캔">1. 단일 URL 스캔</h2>
<pre><code class="language-bash hljs">dalfox <span class="hljs-string">&#39;https://xss-game.appspot.com/level1/frame?query=test&#39;</span>
</code></pre>
<p>첫 번째 인자가 대상입니다. Dalfox는 이것이 URL임을 알아보고 <code>scan</code> 서브커맨드를 자동으로 붙여 실행합니다. <code>?</code>나 <code>&amp;</code>가 들어간 URL은 따옴표로 감싸세요. 셸이 특수 문자로 해석하며, zsh는 따옴표 없는 <code>?</code>를 만나면 <code>no matches found</code>를 내고 실행을 멈춥니다. 실행하면 이런 것들이 보입니다.</p>
<ul>
<li>버전이 적힌 배너.</li>
<li>파라미터를 찾고 컨텍스트를 살피는 동안 찍히는 <code>INF</code> 라인.</li>
<li><code>WRN XSS found N XSS</code> 요약과 그 뒤에 결과마다 찍히는 <code>[POC][V]…</code>(취약) 또는 <code>[POC][R]…</code>(반사됨) 라인. 그 아래에 이슈 내용, 실제로 통한 페이로드, 페이로드가 들어간 응답 줄이 붙습니다.</li>
<li>마지막의 <code>INF scan completed in … seconds</code>.</li>
</ul>
<p>서브커맨드 없는 이 형태는 전역 플래그(<code>--config</code>, <code>--debug</code>, <code>--no-color</code>, <code>-S</code>)만 받습니다. 그 밖의 스캔 플래그를 쓰려면 <code>dalfox scan &lt;target&gt; …</code> 형태가 필요하며, 이 페이지의 나머지 예제도 이 형태를 씁니다.</p>
<h2 id="2-파일에서-스캔">2. 파일에서 스캔</h2>
<p>크롤러가 뽑아둔 URL 목록을 그대로 넘기세요.</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># urls.txt, 한 줄에 대상 하나</span>
dalfox scan urls.txt
</code></pre>
<p>URL마다 같은 파이프라인을 거칩니다. 탐지 결과는 스캔이 끝날 때 <code>WRN XSS found N XSS</code> 요약 뒤에 출력됩니다. 검증되는 즉시 하나씩 보고 싶다면 <code>--stream-findings</code>를 더하세요.</p>
<h2 id="3-파이프라인에서-스캔">3. 파이프라인에서 스캔</h2>
<p>파이프로 입력하면 Dalfox는 <code>stdin</code>에서 읽습니다.</p>
<pre><code class="language-bash hljs">cat urls.txt <span class="hljs-punctuation">|</span> dalfox
<span class="hljs-comment"># 또는 정찰 도구와 연결해서:</span>
waybackurls example.com <span class="hljs-punctuation">|</span> gf xss <span class="hljs-punctuation">|</span> dalfox
</code></pre>
<h2 id="4-json-출력-얻기">4. JSON 출력 얻기</h2>
<p><code>jq</code>나 대시보드, CI에 그대로 물려 쓰세요.</p>
<pre><code class="language-bash hljs">dalfox scan <span class="hljs-string">&#39;https://target.app/search?q=test&#39;</span> -f json -o report.json
</code></pre>
<p><code>plain</code>을 제외한 모든 형식(<code>json</code>, <code>jsonl</code>, <code>markdown</code>, <code>sarif</code>, <code>toml</code>)은 배너를 끄기 때문에 파일이 깔끔하게 남습니다.</p>
<p>종료 코드도 CI에 맞춰져 있습니다. <code>0</code>은 탐지 결과 없음, <code>1</code>은 탐지 결과 있음, <code>2</code>는 오류이거나 믿을 수 없는 결과(모든 대상 접속 실패, 유실된 요청이 너무 많음, 로그인 세션 만료)입니다. <a href="https://dalfox.hahwul.com/ko/guide/output/#%EC%A2%85%EB%A3%8C-%EC%BD%94%EB%93%9C">종료 코드</a>를 참고하세요.</p>
<h2 id="5-인증이-필요한-스캔">5. 인증이 필요한 스캔</h2>
<p>쿠키나 헤더, 커스텀 메서드를 함께 넘기면 됩니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://api.target.app/v1/users <span class="hljs-string">\
</span>  -X POST <span class="hljs-string">\
</span>  -H <span class="hljs-string">&quot;Authorization: Bearer eyJ...&quot;</span> <span class="hljs-string">\
</span>  -H <span class="hljs-string">&quot;Content-Type: application/json&quot;</span> <span class="hljs-string">\
</span>  -d <span class="hljs-string">&#39;{&quot;name&quot;:&quot;test&quot;}&#39;</span> <span class="hljs-string">\
</span>  --cookies <span class="hljs-string">&quot;session=abc123&quot;</span>
</code></pre>
<p>프록시로 잡아둔 <strong>raw HTTP 요청</strong> 파일을 그대로 물려도 됩니다.</p>
<pre><code class="language-bash hljs">dalfox scan --input-type raw-http request.txt
</code></pre>
<p>브라우저 DevTools나 프록시에서 뽑은 <strong>HAR</strong> 전체를 다시 흘려보낼 수도 있습니다. Dalfox는 그 안의 모든 요청을 스캔하며, 각 요청의 메서드, 헤더, 쿠키, 본문을 그대로 살립니다.</p>
<pre><code class="language-bash hljs">dalfox scan capture.har            <span class="hljs-comment"># 자동 판별</span>
dalfox scan --input-type har capture.har
</code></pre>
<h2 id="6-blind-xss-탐지">6. Blind XSS 탐지</h2>
<p>대역외 콜백(Interactsh, Burp Collaborator, XSS Hunter 등)을 씁니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app <span class="hljs-string">\
</span>  -b https://your-callback.interact.sh
</code></pre>
<p>블라인드 페이로드는 요청에 이미 들어 있는 파라미터(쿼리, 폼 인코딩된 <code>-d</code> 본문, <code>-H</code> 헤더, 쿠키)와 페이지에 있는 같은 출처 POST 폼에 들어갑니다. 탐색이나 마이닝으로 나중에 찾은 파라미터에는 가지 않습니다. 나중에 관리자 패널에서 페이로드가 터지면 콜백 서버가 그것을 기록합니다.</p>
<p><a href="https://github.com/projectdiscovery/interactsh">interactsh</a>(OAST) 서버 관리를 Dalfox에 맡길 수도 있습니다. 세션을 등록하고, 콜백을 원본 페이로드와 연결 짓고, 알아서 폴링합니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --blind-oob             <span class="hljs-comment"># 공개 interactsh 메시</span>
dalfox scan https://target.app --blind-oob<span class="hljs-operator">=</span>oast.fun    <span class="hljs-comment"># 서버 지정</span>
</code></pre>
<p>도착한 콜백은 <code>detection_method: oob</code>인 <code>V</code> 탐지 결과가 됩니다. 자체 호스팅 서버라면 <code>--blind-oob-secret</code>을 쓰고, 스캔이 끝난 뒤 폴링을 얼마나 더 이어갈지는 <code>--blind-oob-wait</code>으로 정합니다. 자세한 내용은 <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#blind-xss">Blind XSS</a>를 참고하세요.</p>
<p>OAST 채널에서 <code>--insecure</code>(기본으로 켜져 있음)는 <code>--blind-oob=</code>로 직접 지정한 서버, 예컨대 자체 서명 인증서를 쓰는 자체 호스팅 서버에만 적용됩니다. 공개 interactsh 서버는 항상 TLS 검증을 거칩니다.</p>
<h2 id="7-먼저-dry-run-실행">7. 먼저 Dry-run 실행</h2>
<p><code>--dry-run</code>으로 Dalfox가 무엇을 스캔할지 미리 봅니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --dry-run
</code></pre>
<p>페이로드는 하나도 쏘지 않은 채 파라미터를 찾고 요청량만 가늠합니다.</p>
<h2 id="출력-읽기">출력 읽기</h2>
<p>각 탐지 결과에는 태그가 붙습니다.</p>
<table>
<thead>
<tr>
<th>태그</th>
<th>의미</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>[V]</code></td>
<td><strong>취약(Vulnerable)</strong>: Dalfox가 입력이 악용 가능하다고 판단함 — 파싱된 응답에서 페이로드가 실행 가능한 위치에 도달했거나(예: Dalfox 마커가 붙은 DOM 요소), 대역외 콜백이 발생함</td>
</tr>
<tr>
<td><code>[A]</code></td>
<td><strong>AST 탐지(AST-detected)</strong>: 정적 JS 분석에서 소스→싱크 흐름을 발견함</td>
</tr>
<tr>
<td><code>[R]</code></td>
<td><strong>반사됨(Reflected)</strong>: 페이로드가 응답에 나타났으나 DOM 증거는 없음</td>
</tr>
<tr>
<td><code>[I]</code></td>
<td><strong>정보(Informational)</strong>: XSS 판정이 아님. 예: 옵트인 <code>--detect-outdated-libs</code>로 찾은 알려진 취약 JS 라이브러리</td>
</tr>
</tbody>
</table>
<p><code>V</code>와 <code>A</code>는 바로 조치할 수 있는 결과입니다. <code>R</code>은 한 번 볼 만하지만 이후 단계에서 더 걸러질 수 있습니다.</p>
<p><code>[V]</code>는 브라우저 실행이 아닙니다. Dalfox는 설계상 브라우저를 구동하지 않습니다. 순수 클라이언트 사이드 DOM-XSS는 지금은 <code>[A]</code>로 보고되니 브라우저에서 직접 확인해 보세요. 각 결과에는 <code>detection_method</code>(어떻게 찾았는지)와 <code>confidence</code>(증거가 그 주장을 얼마나 강하게 뒷받침하는지)도 함께 실립니다 — <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a> 문서를 참고하세요.</p>
<h2 id="다음-단계">다음 단계</h2>
<ul>
<li>다양한 <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/">스캐닝 모드</a>를 알아보세요.</li>
<li><a href="https://dalfox.hahwul.com/ko/guide/parameters/">파라미터가 어떻게 탐색되는지</a> 이해하세요.</li>
<li>까다로운 대상을 위해 <a href="https://dalfox.hahwul.com/ko/guide/payloads/">페이로드와 인코더</a>를 조정하세요.</li>
<li>즐겨 쓰는 플래그를 <a href="https://dalfox.hahwul.com/ko/getting-started/configuration/">설정 파일</a>에 저장하세요.</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>탐지 모델</title>
      <link>https://dalfox.hahwul.com/ko/guide/detection-model/</link>
      <guid>https://dalfox.hahwul.com/ko/guide/detection-model/</guid>
      <description>Dalfox 결과의 신뢰도, 탐지 방식, 심각도, 증거 등급, DOM 검증, AST 기반 XSS 결과를 이해합니다.</description>
      <content:encoded><![CDATA[<p>Dalfox의 모든 탐지 결과는 서로 다른 세 질문에 답합니다. 이 셋을 하나의 척도로 읽는 것이 출력에 대한 오해의 가장 큰 원인이라, 별도 필드로 분리되어 있습니다.</p>
<table>
<thead>
<tr>
<th>축</th>
<th>필드</th>
<th>답하는 질문</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>신뢰도</strong></td>
<td><code>type</code> — <code>V</code> / <code>R</code></td>
<td>이것을 취약점이라고 말할 수 있나?</td>
</tr>
<tr>
<td><strong>방식</strong></td>
<td><code>detection_method</code></td>
<td>어떻게 찾았나?</td>
</tr>
<tr>
<td><strong>영향도</strong></td>
<td><code>severity</code></td>
<td>악용되면 얼마나 나쁜가?</td>
</tr>
</tbody>
</table>
<p>셋 중 둘은 서로 독립적으로 움직입니다. <strong><code>severity</code>는 아직 아닙니다.</strong> XSS
탐지 결과에서 severity는 현재 티어를 다시 말한 값이라(<code>V</code> → <code>High</code>, <code>A</code> →
<code>Medium</code>, <code>R</code> → <code>Info</code>), 여기에 필터나 정렬을 걸어도 <code>type</code>이 주지 않는 정보는
없습니다. 실제 정보를 담는 건 라이브러리 권고에서 값을 가져오는 <code>I</code>뿐입니다.
severity가 스스로 영향도를 판정하기 전까지는 표시용 편의로 보세요.</p>
<p><code>[A]</code>는 이 분리보다 먼저 만들어졌습니다. <code>type</code> 필드에 자리하면서 <em>방식</em> 질문에 답하기 때문에, 코드를 포함해 누구도 이것이 신뢰도 척도에서 어디에 속하는지 말할 수 없었습니다. 아래 <a href="#%EC%A0%84%ED%99%98">전환</a> 절에 따라 흡수될 예정입니다.</p>
<p>이 페이지는 그 구분이 출력만 봐서는 드러나지 않기 때문에 존재합니다. <a href="https://github.com/hahwul/dalfox/issues/1238">이슈 #1238</a>에서 <a href="https://github.com/OSTARA711">@OSTARA711</a>과 정리한 내용을 토대로 작성했습니다.</p>
<h2 id="신뢰도-type의-의미">신뢰도: <code>type</code>의 의미</h2>
<table>
<thead>
<tr>
<th>태그</th>
<th>이름</th>
<th>의미</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>V</code></td>
<td><strong>Vulnerable(취약)</strong></td>
<td>Dalfox가 이 입력이 악용 가능하다고 판단합니다. 조치하세요.</td>
</tr>
<tr>
<td><code>R</code></td>
<td><strong>Reflected(반사됨)</strong></td>
<td>페이로드가 응답에 돌아왔지만 실행 가능한 위치인지는 확인되지 않았습니다. 주장이 아니라 신호이므로 직접 확인하세요.</td>
</tr>
<tr>
<td><code>A</code></td>
<td>AST 탐지</td>
<td>과도기 — 방식 라벨입니다. <a href="#전환">전환</a> 참고.</td>
</tr>
<tr>
<td><code>I</code></td>
<td>정보성</td>
<td>XSS 주장이 아닙니다 (예: 알려진 취약 JS 라이브러리, CWE-1104).</td>
</tr>
</tbody>
</table>
<p><code>--only-poc</code>로 필터링합니다 (예: <code>--only-poc v</code>, <code>--only-poc v,a</code>, <code>--only-poc i</code>).</p>
<h3 id="v가-뜻하지-않는-것"><code>V</code>가 뜻하지 않는 것</h3>
<p><code>V</code>는 브라우저 실행이 <strong>아닙니다.</strong> Dalfox는 브라우저를 구동하지 않고 CDP도 쓰지 않습니다. 페이지를 렌더링하거나 <code>alert()</code>가 뜨는 것을 관찰하지 않습니다. 요청 기반 방식에서 <code>V</code>는 페이로드가 <em>실제 HTTP 응답으로 파싱한 DOM 트리</em>에서 발견됐다는 뜻입니다. 정적 분석이지만, <code>R</code>의 단순 문자열 매칭보다 강한 증거에 근거합니다.</p>
<p>Dalfox가 실제 실행을 관측하는 방식은 <strong>대역외 콜백</strong>(<code>--blind-oob</code>를 쓴 blind XSS) 하나뿐입니다. 주입된 <code>&lt;script src=…&gt;</code>가 콜백을 보내오면, 무언가가 그 페이로드를 불러와 가져간 것입니다. 대부분은 피해자의 브라우저지만, 연관된 DNS나 HTTP 상호작용이면 무엇이든 인정되므로 URL을 따라가는 서버 측 fetcher도 콜백을 일으킵니다. 이건 경험적 증거이며 Dalfox가 만들어내는 가장 강한 증거입니다. 다만 Dalfox가 제어하지 않는 클라이언트에서 옵니다. 일반 <code>-b</code> 콜백 URL은 호출이 여러분의 리스너로 가므로 Dalfox는 탐지 결과를 기록하지 않습니다.</p>
<h2 id="방식-detection-method의-의미">방식: <code>detection_method</code>의 의미</h2>
<table>
<thead>
<tr>
<th>값</th>
<th>무엇을 읽는가</th>
<th>페이로드 전송?</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>reflection</code></td>
<td>응답 본문에서 페이로드 바이트</td>
<td>예</td>
</tr>
<tr>
<td><code>dom-verification</code></td>
<td>HTML로 파싱한 응답에서 실행 가능한 위치</td>
<td>예</td>
</tr>
<tr>
<td><code>ast</code></td>
<td>응답에 담긴 JavaScript에서 소스→싱크 흐름</td>
<td>흐름을 찾는 데는 보내지 않음. 프로브 응답에서 찾은 흐름은 재확인 요청 1건을 보냄</td>
</tr>
<tr>
<td><code>oob</code></td>
<td>페이로드를 불러온 쪽이 보낸 대역외 콜백(DNS 또는 HTTP)</td>
<td>예</td>
</tr>
<tr>
<td><code>library</code></td>
<td><code>&lt;script&gt;</code> 태그에서 알려진 취약 버전</td>
<td>아니오</td>
</tr>
</tbody>
</table>
<p><strong>AST 탐지 결과를 선택할 때는 <code>type == &quot;A&quot;</code>가 아니라 <code>detection_method == &quot;ast&quot;</code>를 쓰세요.</strong> 방식 필드는 안정적이고, 티어는 그렇지 않습니다.</p>
<h3 id="실제로-나오는-조합">실제로 나오는 조합</h3>
<p>티어와 방식은 따로 정해지므로 <code>V</code>가 곧 <code>dom-verification</code>을 뜻하지 않습니다.
Dalfox가 내보내는 탐지 결과는 다음 중 하나입니다.</p>
<table>
<thead>
<tr>
<th><code>type</code></th>
<th><code>detection_method</code></th>
<th><code>confidence</code></th>
<th><code>severity</code></th>
<th>산출 경로</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>V</code></td>
<td><code>dom-verification</code></td>
<td><code>high</code></td>
<td>High</td>
<td>전용 DOM 검증 요청</td>
</tr>
<tr>
<td><code>V</code></td>
<td><code>reflection</code></td>
<td><code>high</code></td>
<td>High</td>
<td>반사 단계에서, 반사 응답 자체가 <em>이미</em> 실행 가능한 위치로 파싱되는 경우</td>
</tr>
<tr>
<td><code>V</code></td>
<td><code>oob</code></td>
<td><code>high</code></td>
<td>High</td>
<td>대역외 콜백</td>
</tr>
<tr>
<td><code>V</code></td>
<td><code>ast</code></td>
<td><code>high</code> <strong>또는</strong> <code>low</code></td>
<td>High</td>
<td>레거시 AST 승격 두 곳 — <a href="#전환">전환</a> 절이 다루는 바로 그 불일치</td>
</tr>
<tr>
<td><code>A</code></td>
<td><code>ast</code></td>
<td><code>high</code> <strong>또는</strong> <code>low</code></td>
<td>Medium</td>
<td>그 외 모든 소스→싱크 흐름</td>
</tr>
<tr>
<td><code>R</code></td>
<td><code>reflection</code></td>
<td><code>low</code></td>
<td>Info</td>
<td>실행 가능 위치가 확인되지 않은 반사. <code>--hpp</code>의 중복 파라미터 반사(<code>inject_type: inHTML-HPP</code>) 포함</td>
</tr>
<tr>
<td><code>I</code></td>
<td><code>library</code></td>
<td><em>(없음)</em></td>
<td>Medium / High</td>
<td><code>--detect-outdated-libs</code></td>
</tr>
</tbody>
</table>
<p>사람들이 의아해하는 건 <code>V</code> + <code>reflection</code> 행입니다. <code>reflection</code>은 증거가 얼마나
약한지가 아니라 <em>어느 요청에서 증거가 나왔는지</em>를 가리킵니다. 이 행도
<code>dom-verification</code> 행과 똑같은 DOM을 파싱했고, 다만 요청을 하나 더 쓰는 대신 이미
받아둔 응답에서 했을 뿐입니다.</p>
<h3 id="dom-verification의-증거"><code>dom-verification</code>의 증거</h3>
<p>페이로드가 실행 가능한 위치에 도달했음을 증명하는 다섯 경로: CSS 셀렉터로 매칭된 Dalfox 마커, 위험한 속성에 들어간 실행 가능 스킴(<code>javascript:</code>, <code>data:text/html</code>), 싱크를 호출하는 핸들러를 가진 주입된 엘리먼트, AST 범위가 페이로드를 포함하는 <code>&lt;script&gt;</code> 내부 싱크 호출, 그리고 페이로드가 자신을 둘러싼 JS 문자열을 종료시킨 인라인 핸들러 브레이크아웃. 어느 것이 발동했는지는 <code>evidence</code> 필드가 알려줍니다.</p>
<h3 id="응답-content-type이-허용하는-것">응답 content type이 허용하는 것</h3>
<p>증거는 브라우저가 그 응답에 사용할 파서에서 성립해야 하므로, 어떤 검사가 성공할 수 있는지는 응답의 <code>Content-Type</code>이 결정합니다.</p>
<table>
<thead>
<tr>
<th>응답 타입</th>
<th>읽는 방식</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>text/html</code>, 또는 쓸 만한 <code>Content-Type</code>이 없고 본문이 HTML처럼 시작하는 경우</td>
<td>HTML로 파싱</td>
</tr>
<tr>
<td><code>application/xhtml+xml</code>, <code>image/svg+xml</code>, 그 밖의 XML(<code>text/xml</code>, <code>application/xml</code>, <code>*+xml</code>)</td>
<td>XML로 파싱. 활성 네임스페이스(XHTML, SVG)에 있는 마크업만 인정</td>
</tr>
<tr>
<td>JavaScript(<code>application/javascript</code>, <code>text/javascript</code> 등)</td>
<td>스크립트로 읽음. JSONP 콜백 같은 JS 컨텍스트 페이로드만 <code>V</code>가 되며, 본문 속 HTML 마크업은 절대 <code>V</code>가 되지 않음</td>
</tr>
<tr>
<td><code>application/json</code>, <code>*+json</code>, <code>text/csv</code>, 명시된 <code>text/plain</code>, 바이너리 미디어</td>
<td>마크업 문서가 아님. 반사를 버리며 <code>R</code>도 남기지 않음</td>
</tr>
</tbody>
</table>
<p><code>text/plain</code>은 <code>X-Content-Type-Options: nosniff</code> 유무와 상관없이 무해로 취급합니다. 브라우저는 명시된 <code>text/plain</code>을 HTML로 스니핑하지 않기 때문입니다. 대상 페이지에 대한 AST 패스도 같은 규칙을 따라, 그 페이지가 활성 마크업 문서일 때만 실행됩니다. 표의 마지막 행에는 예외가 하나 있습니다. 리다이렉트의 <code>Location</code> 헤더에 페이로드가 반사되면 본문 타입과 상관없이 <code>R</code>을 기록합니다.</p>
<h3 id="ast와-dom-xss의-천장"><code>ast</code>와 DOM-XSS의 천장</h3>
<p>AST 패스는 응답에 담긴 JavaScript를 파싱해, 위험한 소스(<code>location.hash</code>, <code>location.search</code>, <code>document.referrer</code>, <code>postMessage</code> 등)에서 온 데이터가 새니타이저를 거치지 않고 위험한 싱크(<code>innerHTML</code>, <code>document.write</code>, <code>eval</code> 등)에 도달하는지 추적합니다. 각 <code>&lt;script&gt;</code> 블록을 한 번 읽고 발견한 모든 흐름을 보고하므로, 명령줄에 넘기지 않은 입력이 결과에 등장할 수 있습니다 — 서버로 전송조차 되지 않는 URL 프래그먼트도 포함됩니다. 랜딩 페이지에 대한 패스는 <code>-p</code>로 좁혀지지 않습니다. <code>-p</code>는 어떤 파라미터를 <em>요청</em>할지를 정하는 옵션이고, 그 패스는 이미 받아 둔 응답을 읽기 때문입니다.</p>
<p>이것으로 결함처럼 보이지만 아닌 결과도 설명됩니다. <strong>순수 클라이언트 사이드 DOM-XSS</strong>에서는 페이로드를 런타임에 JavaScript가 페이지에 써 넣으므로 서버 응답에는 절대 나타나지 않고, 응답을 파싱하는 방식들이 찾을 대상 자체가 없습니다. <code>location.hash → innerHTML</code>만 취약한 정적 페이지라면 <code>--only-poc v</code>가 아무것도 반환하지 않는 것이 정상입니다. 브라우저에서 devtools를 켜고 POC URL을 열어 확인하세요 — Dalfox는 모든 AST 탐지 결과에 완성된 POC URL을 출력하며, 랜딩 페이지에서 나온 탐지 결과 중 URL에 담을 수 없는 소스(<code>window.name</code>, <code>document.referrer</code>, 쿠키, <code>postMessage</code> 등)에는 <code>[manual POC: …]</code> 재현 힌트를 붙입니다.</p>
<table>
<thead>
<tr>
<th>플래그</th>
<th>효과</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--skip-ast-analysis</code></td>
<td>소스→싱크 분석을 끕니다</td>
</tr>
<tr>
<td><code>--analyze-external-js</code></td>
<td>동일 출처 <code>&lt;script src&gt;</code> 번들도 가져와 분석합니다</td>
</tr>
</tbody>
</table>
<p><code>--skip-mining-dom</code>은 이 패스에 영향을 주지 <strong>않습니다</strong> — 그쪽은 <code>&lt;input&gt;</code> 요소의 <code>id</code>/<code>name</code> 속성에서 파라미터 <em>이름</em>을 수집하는 작업입니다. <a href="https://dalfox.hahwul.com/ko/guide/parameters/">파라미터와 탐색</a>을 참고하세요.</p>
<h2 id="confidence-주장의-근거-등급"><code>confidence</code>: 주장의 근거 등급</h2>
<p>모든 XSS 탐지 결과는 <code>high</code> 또는 <code>low</code>의 <code>confidence</code>와, 판단 신호를 나열한 <code>confidence_reason</code>을 함께 가집니다. 요청 기반 방식은 증거를 그대로 따르고, AST 탐지 결과는 흐름의 형태로 판정합니다.</p>
<p><code>high</code>는 다음을 <strong>모두</strong> 만족해야 합니다.</p>
<ul>
<li><strong>링크만으로 공격자가 도달할 수 있는 소스</strong> — <code>location.*</code>, <code>document.URL</code>, <code>URLSearchParams</code>는 페이로드를 URL에 직접 싣습니다. 원래라면 공격자가 제어하는 구동 페이지가 필요한 소스(<code>window.name</code>, <code>document.referrer</code>, <code>postMessage</code>, 스토리지, <code>history.state</code>)는 <code>low</code>입니다. 다만 <strong>페이지가 그 소스를 쿼리 파라미터에서 스스로 채워 넣는 경우는 예외</strong>로, 이때는 링크만으로도 구동되므로 <code>high</code>가 되고 근거에 <code>non-URL source seeded from a query parameter by the page</code>가 붙습니다.</li>
<li><strong>페이지 CSP가 실행을 허용하는 페이로드</strong> — 인라인 스크립트가 허용되거나, 싱크가 스크립트를 직접 실행해서(<code>eval</code>, <code>Function</code>, <code>document.write</code>, <code>&lt;script&gt;</code> 텍스트) 인라인 핸들러 권한에 의존하지 않는 경우입니다. report-only CSP는 아무것도 강제하지 않으므로 등급을 낮추지 않습니다.</li>
<li><strong>Trusted Types에 가로막히지 않음</strong> — <code>require-trusted-types-for 'script'</code>와 TrustedHTML 계열 싱크의 조합은 <code>low</code>입니다.</li>
</ul>
<p>새니타이저는 판정 신호가 아닙니다. 이미 <em>필터</em>로 작동하기 때문입니다. 분석기가 새니타이저를 taint를 지우는 요소로 처리하므로, 탐지 결과가 존재한다는 것 자체가 경로에 알려진 새니타이저가 없었다는 뜻입니다.</p>
<p><code>confidence_reason</code>은 두 방향을 섞지 않습니다. <code>high</code>면 판단을 뒷받침한 신호를, <code>low</code>면 <strong>가로막은 것만</strong> 나열하므로 성립했던 신호는 표시되지 않습니다. 정보성으로만 붙는 근거가 하나 있습니다. <code>flow sits inside a conditional branch</code>는 흐름이 조건 분기 안에 있다는 기록으로, <code>high</code> 등급에만 나타나며 그 자체로 등급을 바꾸지 않습니다.</p>
<h3 id="등급이-보이는-곳과-보이지-않는-곳">등급이 보이는 곳과 보이지 않는 곳</h3>
<p><code>confidence</code>는 <code>json</code>, <code>jsonl</code>, <code>toml</code>, <code>markdown</code>, <code>sarif</code>가 실어 나릅니다.
기본 <code>plain</code> 출력에는 <strong>표시되지 않으므로</strong>, 아래 트리아지 안내는 기계 판독
포맷을 전제로 합니다. 아직 이 값에 반응하는 동작도 없습니다. <code>--only-poc</code>,
<code>--limit-result-type</code>, 중복 제거 순위, 종료 코드는 전부 <code>type</code>을 읽습니다.
등급은 전환의 미리보기이지 아직 제어 수단이 아닙니다.</p>
<h2 id="전환">전환</h2>
<p>등급이 아직 아무것도 결정하지 않는다는 것이 핵심입니다. 무엇이 움직이기 전에 각 탐지 결과가 어디로 갈지 미리 볼 수 있습니다.</p>
<ol>
<li><strong>현재</strong> — <code>type</code> 그대로. <code>detection_method</code>와 <code>confidence</code>가 추가됩니다. 선택자로서의 <code>type == &quot;A&quot;</code>는 더 이상 권장하지 않으니 <code>detection_method == &quot;ast&quot;</code>를 쓰세요.</li>
<li><strong>다음</strong> — <code>--tier-model confidence</code>를 옵트인으로 제공.</li>
<li><strong>그 다음</strong> — 그것이 기본값이 되고, <code>--tier-model legacy</code>가 탈출구로 남습니다. <code>A</code>는 사라집니다. <code>high</code>로 판정된 AST 탐지 결과는 <code>V</code>, 나머지는 <code>R</code>로 갑니다. 원래 <code>R</code>이 그 자리였습니다. <code>R</code>의 이름도 이때 함께 바뀝니다. 그 시점부터 반사형 외의 탐지 결과도 담게 되므로, 정확히 그 순간 단어가 맞지 않게 됩니다.</li>
</ol>
<p><code>--only-poc a</code>는 계속 동작합니다. 티어가 사라진 뒤에는 <code>detection_method == &quot;ast&quot;</code>를 선택합니다. 플래그 값은 절대 제거되지 않습니다.</p>
<p>과도기에는 <code>type</code>과 <code>confidence</code>가 어긋날 수 있습니다. <code>type=V, confidence=low</code>인 탐지 결과가 나오는 식입니다. 레거시 경로 두 곳이 근거가 부족한 상태로 AST 탐지 결과를 <code>V</code>로 승격시키기 때문입니다. 등급은 Dalfox가 실제로 주장할 수 있는 것을, 티어는 지금까지 출력해 온 것을 보고합니다. 이 불일치는 버그가 아니라 미리보기 신호입니다.</p>
<p>티어가 등급에서 파생되기 시작하면 티어를 읽는 모든 것이 함께 움직입니다. 같은 <code>--only-poc v</code>가 다른 집합을 고르고, 종료 코드도 다른 집합에서 뒤집히며, 중복 제거의 우선순위도 달라집니다. 이는 우회해야 할 부작용이 아니라 의도된 효과입니다.</p>
<h2 id="여러-방식이-섞인-출력-읽기">여러 방식이 섞인 출력 읽기</h2>
<pre><code>INF found reflected 0 params
WRN XSS found 0 XSS (+3 A)
[POC][A][GET][DOM-XSS] https://target.app/?q=%3Cimg+src%3Dx+onerror%3D…
  ├── Issue: DOM-based XSS via URLSearchParams.get(q) to innerHTML (needs runtime confirmation)
  └── Payload: q=&lt;img src=x onerror=alert(1) class=dlx1944740c&gt;
</code></pre>
<ul>
<li><code>found reflected 0 params</code> — <strong>reflection</strong> 방식이 서버 측 반사를 찾지 못했다는 뜻입니다. 정적 사이트에서는 당연한 결과입니다.</li>
<li><code>XSS found 0 XSS</code> — 대표 숫자는 <code>V</code>만 셉니다. <code>(+3 A)</code>가 아래에 출력될 나머지 등급을 알려줍니다.</li>
<li><code>[A]</code> 블록은 위 두 줄과 무관하게 <strong>ast</strong> 방식에서 나옵니다.</li>
</ul>
<p>DOM-XSS 대상에서 요약 줄만 읽고 멈추면 보고서의 모든 탐지 결과를 놓치게 됩니다.</p>
<h3 id="티어-합계가-발견-수와-다른-이유">티어 합계가 발견 수와 다른 이유</h3>
<p>출력 전에 후처리가 두 번 돌기 때문에, 티어별 개수는 스캔 중 기록된 탐지 결과 수와
같지 않습니다.</p>
<ul>
<li><strong>중복 <code>R</code> 정리</strong> — 같은 대상에서 같은 <code>(param, location, inject_type)</code>에 <code>V</code>가 있으면
<code>R</code>은 제거됩니다. 둘 다 남기면 같은 입력이 서로 다른 강도로 두 번 나열되기
때문입니다. <code>V</code>와 <code>A</code>는 제거되지 않습니다.</li>
<li><strong>AST 중복 제거</strong> — 같은 소스→싱크 흐름을 프리플라이트, 프로브, 반사 루프가
각각 찾을 수 있습니다. 지문당 하나만 남으며, 기준은 <code>type</code> 다음 <code>severity</code>
입니다. <code>confidence</code>는 여기에 관여하지 않으므로 <code>low</code> 등급인 <code>V</code>가 여전히
<code>high</code>인 <code>A</code>보다 앞섭니다.</li>
</ul>
<p><code>--stream-findings</code>는 스캔 루프의 탐지 결과를 기록되는 즉시 내보내는데, 이는 <code>R</code> 정리와
AST 중복을 가장 강한 것 하나로 줄이는 과정 <em>이전</em>입니다. 따라서 스트림에 보였던 <code>R</code>이 최종 보고서에는 없을 수 있고, 랜딩 페이지
AST 패스, 오래된 라이브러리, OOB 콜백에서 나온 결과는 최종 보고서에만 나타납니다.
둘이 다르면 최종 보고서가 정답입니다.</p>
<h3 id="티어-선택-only-poc와-limit-result-type">티어 선택: <code>--only-poc</code>와 <code>--limit-result-type</code></h3>
<p>둘 다 티어로 고르지만 하는 일이 다릅니다. <code>--only-poc</code>는 쉼표 목록(<code>v,r,a,i</code>)을, <code>--limit-result-type</code>은 <code>all</code>(기본값), <code>v</code>, <code>r</code>, <code>a</code>, <code>i</code> 중 하나를 받습니다.</p>
<table>
<thead>
<tr>
<th>플래그</th>
<th>효과</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--only-poc</code></td>
<td><strong>출력을 필터링합니다.</strong> 다른 티어는 버려집니다</td>
</tr>
<tr>
<td><code>--limit-result-type</code></td>
<td><strong><code>--limit</code> 카운트 대상만 정합니다.</strong> 다른 티어도 그대로 보고되며, 한도만 소모하지 않습니다</td>
</tr>
</tbody>
</table>
<p><code>--limit 2 --limit-result-type v</code>는 &quot;<code>V</code> 두 개가 쌓일 때까지 스캔하고, 그 과정에서
찾은 것은 전부 보여줘&quot;라는 뜻입니다. 나머지를 실제로 숨기려면 <code>--only-poc v</code>를
함께 주세요.</p>
<h3 id="종료-코드">종료 코드</h3>
<p><code>0</code>은 탐지 결과 없음, <code>1</code>은 <strong>티어와 무관하게</strong> 탐지 결과가 하나라도 있음(<code>--only-poc</code>, <code>--baseline</code>
필터, 위 정리를 적용한 뒤 기준), <code>2</code>는 하드 에러(잘못된 입력, 모든 대상 도달 불가,
<code>--output</code> 기록 실패)이며, 탐지 결과 없이 정상적으로 끝나지 못한 실행도 포함합니다.
기본값 <code>--on-session-loss abort</code>에서 세션이 끊긴 경우, 스캔 워커가 크래시한 경우,
심각한 전송 손실이 여기에 해당합니다. <code>R</code> 하나나 <code>--detect-outdated-libs</code>가 만든 <code>I</code> 하나도
<code>V</code>와 똑같이 <code>1</code>을 냅니다. Dalfox가 악용 가능하다고 판단한 것에만 CI를 실패시키려면
<code>--only-poc v</code>를 쓰세요.</p>
<h2 id="목적별-플래그-선택">목적별 플래그 선택</h2>
<table>
<thead>
<tr>
<th>목적</th>
<th>플래그</th>
</tr>
</thead>
<tbody>
<tr>
<td>Dalfox가 악용 가능하다고 판단한 것만 보기</td>
<td><code>--only-poc v</code></td>
</tr>
<tr>
<td>운영 중인 대상에서 정적 분석 노이즈 억제</td>
<td><code>--skip-ast-analysis</code></td>
</tr>
<tr>
<td>이름 수집만 끄고 DOM-XSS 탐지는 유지</td>
<td><code>--skip-mining-dom</code></td>
</tr>
<tr>
<td>파라미터 하나만 테스트하되 모든 DOM 싱크는 확인</td>
<td><code>-p q</code> (<code>[A]</code>는 <code>-p</code> 범위를 따르지 않습니다)</td>
</tr>
<tr>
<td>대량의 AST 탐지 결과 트리아지</td>
<td><code>-f json</code>으로 받아 <code>confidence</code>로 정렬한 뒤 <code>confidence_reason</code> 읽기</td>
</tr>
<tr>
<td>악용 가능 판단에만 CI 실패시키기</td>
<td><code>--only-poc v</code> (그렇지 않으면 어떤 티어든 <code>1</code>)</td>
</tr>
</tbody>
</table>
]]></content:encoded>
    </item>
    <item>
      <title>출력과 리포트</title>
      <link>https://dalfox.hahwul.com/ko/guide/output/</link>
      <guid>https://dalfox.hahwul.com/ko/guide/output/</guid>
      <description>Dalfox XSS 탐지 결과를 plain text, JSON, JSONL, Markdown, SARIF, TOML로 내보내 CI와 보안 파이프라인에 연결합니다.</description>
      <content:encoded><![CDATA[<p>모든 스캔은 동일한 내부 결과 구조를 만듭니다. Dalfox는 이를 선택한 형식으로 렌더링합니다. <code>plain</code>을 제외한 모든 형식은 배너를 빼므로, 파일로 리디렉션한 리포트가 깔끔하게 유지됩니다.</p>
<h2 id="형식-선택">형식 선택</h2>
<pre><code class="language-bash hljs">dalfox scan https://target.app -f json -o report.json
</code></pre>
<table>
<thead>
<tr>
<th>형식</th>
<th>플래그</th>
<th>기계 판독 가능</th>
<th>적합한 용도</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>plain</code></td>
<td><code>-f plain</code> (기본값)</td>
<td>아니오</td>
<td>사람이 읽는 터미널 출력</td>
</tr>
<tr>
<td><code>json</code></td>
<td><code>-f json</code></td>
<td>예</td>
<td>단일 JSON 문서, 대시보드, <code>jq</code></td>
</tr>
<tr>
<td><code>jsonl</code></td>
<td><code>-f jsonl</code></td>
<td>예</td>
<td>스트리밍, 로그 파이프라인</td>
</tr>
<tr>
<td><code>markdown</code></td>
<td><code>-f markdown</code></td>
<td>아니오</td>
<td>리포트, 풀 리퀘스트 코멘트</td>
</tr>
<tr>
<td><code>sarif</code></td>
<td><code>-f sarif</code></td>
<td>예</td>
<td>GitHub 코드 스캐닝, SARIF 소비자</td>
</tr>
<tr>
<td><code>toml</code></td>
<td><code>-f toml</code></td>
<td>예</td>
<td>사람 + 파이프라인</td>
</tr>
</tbody>
</table>
<h2 id="파일로-저장하기">파일로 저장하기</h2>
<pre><code class="language-bash hljs">dalfox scan https://target.app -f jsonl -o findings.jsonl
</code></pre>
<p><code>-o</code>가 없으면 출력은 <code>stdout</code>으로 나갑니다.</p>
<h2 id="결과-필드">결과 필드</h2>
<p>모든 탐지 결과에는 다음이 포함됩니다.</p>
<table>
<thead>
<tr>
<th>필드</th>
<th>예시</th>
<th>의미</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>type</code></td>
<td><code>V</code>, <code>A</code>, <code>R</code>, <code>I</code></td>
<td>탐지 티어: Vulnerable / AST 탐지 / Reflected / Informational</td>
</tr>
<tr>
<td><code>type_description</code></td>
<td><code>&quot;Vulnerable - dalfox asserts this input is exploitable; act on it&quot;</code></td>
<td>사람이 읽는 라벨(한 단어가 아니라 문장 전체)</td>
</tr>
<tr>
<td><code>detection_method</code></td>
<td><code>&quot;ast&quot;</code></td>
<td>어떻게 찾았는지: <code>reflection</code>, <code>dom-verification</code>, <code>ast</code>, <code>oob</code>, <code>library</code></td>
</tr>
<tr>
<td><code>confidence</code></td>
<td><code>&quot;high&quot;</code></td>
<td>증거가 그 주장을 얼마나 강하게 뒷받침하는지 (<code>high</code> / <code>low</code>). <code>I</code>에는 없음</td>
</tr>
<tr>
<td><code>confidence_reason</code></td>
<td><code>&quot;URL-carried source; inline script permitted&quot;</code></td>
<td>판단 근거 신호</td>
</tr>
<tr>
<td><code>inject_type</code></td>
<td><code>&quot;inHTML&quot;</code></td>
<td>탐지 라벨: 주입한 페이로드는 <code>inHTML</code>(<code>--sxss</code>에서는 <code>sxss-inHTML</code>, 해당하면 <code>-CSTI</code> 접미어나 <code>-VHtml</code> 같은 프레임워크 싱크 접미어가 붙음), <code>inHTML-HPP</code>, <code>DOM-XSS</code>(AST), <code>blind-oob-&lt;location&gt;-&lt;protocol&gt;</code>, <code>OutdatedComponent</code>(<code>I</code>)</td>
</tr>
<tr>
<td><code>method</code></td>
<td><code>&quot;GET&quot;</code></td>
<td>HTTP 메서드</td>
</tr>
<tr>
<td><code>data</code></td>
<td><code>&quot;https://target.app/?q=%3Csvg%20onload%3Dalert%281%29%20class%3Ddlx1ec4110f%3E&quot;</code></td>
<td>PoC URL</td>
</tr>
<tr>
<td><code>param</code></td>
<td><code>&quot;q&quot;</code></td>
<td>공격에 사용된 파라미터</td>
</tr>
<tr>
<td><code>location</code></td>
<td><code>&quot;Query&quot;</code></td>
<td>파라미터가 실리는 위치: <code>Query</code>, <code>Body</code>, <code>JsonBody</code>, <code>MultipartBody</code>, <code>GraphqlBody</code>, <code>XmlBody</code>, <code>Header</code>(쿠키 포함), <code>Path</code>, <code>Fragment</code>. 알 수 없으면 생략</td>
</tr>
<tr>
<td><code>payload</code></td>
<td><code>&lt;svg onload=alert(1)&gt;</code></td>
<td>정확한 페이로드</td>
</tr>
<tr>
<td><code>evidence</code></td>
<td><code>&quot;DOM verification successful for param q (DOM marker)&quot;</code></td>
<td>Dalfox가 그렇게 판단한 근거</td>
</tr>
<tr>
<td><code>cwe</code></td>
<td><code>&quot;CWE-79&quot;</code></td>
<td>표준 CWE</td>
</tr>
<tr>
<td><code>severity</code></td>
<td><code>&quot;High&quot;</code></td>
<td>High / Medium / Low / Info</td>
</tr>
<tr>
<td><code>message_id</code></td>
<td><code>606</code></td>
<td>카탈로그 메시지 ID</td>
</tr>
<tr>
<td><code>message_str</code></td>
<td><code>&quot;Triggered XSS Payload (DOM marker): q=&lt;svg onload=alert(1) class=dlx1ec4110f&gt;&quot;</code></td>
<td>짧은 메시지</td>
</tr>
</tbody>
</table>
<p>다음 세 필드는 요청했을 때만 나타납니다: <code>new</code>(<code>--baseline-mode annotate</code>), <code>request</code>(<code>--include-request</code>), <code>response</code>(<code>--include-response</code>).</p>
<p>각 티어가 실제로 어떤 증거인지, 그리고 순수 클라이언트 사이드 DOM-XSS가 왜 <code>V</code>에
도달하지 못하는지는 <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a> 문서에서 다룹니다.</p>
<p><code>V</code> / <code>A</code> / <code>R</code>은 XSS 탐지 결과입니다. <code>I</code>(<strong>Informational</strong>)는 공격에 사용할 수 없는
관찰 항목으로, 현재는 <strong>오래되었거나 알려진 취약점이 있는 JS 라이브러리</strong>
(<code>inject_type: &quot;OutdatedComponent&quot;</code>, <code>CWE-1104</code>)만 해당하며, 페이로드나 파라미터가 없는 간결한
<code>[INF]</code> 라인으로 렌더링됩니다. 이 항목은 <strong>옵트인</strong>입니다. Dalfox는 기본적으로
검증된 XSS에 집중하므로, <code>--detect-outdated-libs</code>를 전달하지 않는 한 라이브러리 리포팅은 꺼져 있습니다
(추가 요청은 <strong>0건</strong>이며, 프리플라이트 응답의 <code>&lt;script&gt;</code> 태그를 검사합니다). <code>--only-poc v,a,r</code>로 걸러낼 수 있습니다.</p>
<p>선택적으로 전체 요청/응답을 포함할 수 있습니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app -f json --include-all -o report.json
<span class="hljs-comment"># 또는 세부적으로:</span>
dalfox scan ... --include-request
dalfox scan ... --include-response
</code></pre>
<p>기록되는 요청은 Dalfox가 실제로 보낸 것 그대로입니다. <code>-H</code>로 준 헤더와 쿠키가
전부 원문으로 들어갑니다. <code>--include-request</code> / <code>--include-all</code>로 만든 리포트는
공유하기 전에 내용을 확인하세요. 유닉스에서는 <code>-o</code> 파일을 <code>0600</code>으로 생성해
같은 호스트의 다른 계정이 읽지 못하게 하지만, 그 파일이 이후에 어디로 가는지는
별개의 문제입니다.</p>
<h2 id="json과-jsonl-형태">JSON과 JSONL 형태</h2>
<p><code>-f json</code>은 엔벨로프를 <code>meta</code> 아래에, 탐지 결과를 <code>findings</code> 아래에 담은 문서 하나를 씁니다.</p>
<pre><code class="language-json hljs"><span class="hljs-punctuation">{</span>
  <span class="hljs-name">&quot;findings&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-name">&quot;confidence&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;high&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;confidence_reason&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;payload reached an executable position in the parsed response&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;cwe&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;CWE-79&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;data&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;https://target.app/?q=%3Csvg%20onload%3Dalert%281%29%20class%3Ddlx1ec4110f%3E&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;detection_method&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;reflection&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;evidence&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;DOM verification successful for param q (DOM marker)&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;inject_type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;inHTML&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;location&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Query&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;message_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">606</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;message_str&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Triggered XSS Payload (DOM marker): q=&lt;svg onload=alert(1) class=dlx1ec4110f&gt;&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;method&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;GET&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;param&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;q&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;payload&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&lt;svg onload=alert(1) class=dlx1ec4110f&gt;&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;severity&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;High&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;V&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-name">&quot;type_description&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Vulnerable - dalfox asserts this input is exploitable; act on it&quot;</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">],</span>
  <span class="hljs-name">&quot;meta&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-name">&quot;dalfox_version&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;3.2.3&quot;</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;dedup_mode&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;exact&quot;</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;failed_requests&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;findings_count&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;incomplete&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">false</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;scan_duration_ms&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1234</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;target_summary&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
      <span class="hljs-punctuation">{</span> <span class="hljs-name">&quot;findings_count&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-name">&quot;status&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;findings&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-name">&quot;target&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;https://target.app/?q=a&quot;</span> <span class="hljs-punctuation">}</span>
    <span class="hljs-punctuation">],</span>
    <span class="hljs-name">&quot;targets&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;https://target.app/?q=a&quot;</span><span class="hljs-punctuation">],</span>
    <span class="hljs-name">&quot;targets_deduplicated&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>
    <span class="hljs-name">&quot;total_requests&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">87</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p><code>-f jsonl</code>은 같은 데이터를 한 줄에 객체 하나씩 씁니다. 첫 줄은 <code>{&quot;meta&quot;: {…}}</code>이고, 그 뒤의 각 줄이 탐지 결과 하나입니다. 탐지 결과만 필요하다면 첫 줄을 건너뛰거나, <code>jq 'select(.severity==&quot;High&quot;)'</code>처럼 탐지 결과 필드로 거르세요.</p>
<h2 id="스캔-메타데이터-엔벨로프">스캔 메타데이터 엔벨로프</h2>
<p>JSON, JSONL, SARIF, TOML, Markdown 출력은 모두 동일한 스캔 수준 메타데이터 엔벨로프를 담습니다.</p>
<ul>
<li><code>dalfox_version</code></li>
<li><code>targets</code> (입력 대상)</li>
<li><code>scan_duration_ms</code></li>
<li><code>total_requests</code></li>
<li><code>failed_requests</code> — 재시도를 다 쓰고도 응답을 받지 못한 요청 수(리셋, 거부, 타임아웃). 대상에 닿지 못한 페이로드는 테스트되지 않은 것입니다</li>
<li><code>findings_count</code></li>
<li><code>target_summary[]</code> — 대상마다 항목 하나: <code>target</code>, <code>status</code>(<code>findings</code>, <code>clean</code>, <code>skipped</code>, <code>incomplete</code>), <code>findings_count</code>, 건너뛰었거나 세션이 끊긴 경우 <code>error_code</code>(Ctrl-C / <code>--limit</code> / <code>--scan-timeout</code>으로 도중에 끊긴 대상은 <code>error_code</code> 없이 <code>incomplete</code>. 세션이 끊긴 경우에는 감지된 신호를 담은 <code>error_message</code>도), 그리고 WAF가 탐지된 경우 <code>waf</code> 객체(<code>type</code> / <code>confidence</code> / <code>evidence</code>를 담은 <code>detected[]</code>와, 추가 인코더·변형 수·우회 중 보낸/차단된 요청 수를 담은 <code>bypass</code> 블록)</li>
<li><code>dedup_mode</code> / <code>targets_deduplicated</code> — 적용된 <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#%EA%B1%B0%EC%9D%98-%EA%B0%99%EC%9D%80-url-%EB%AC%B6%EA%B8%B0"><code>--dedup-urls</code></a> 모드와 그것이 병합한 대상 수. 축소된 입력 목록이 리포트에 드러나도록 합니다(Markdown은 실제로 병합이 있었을 때만 행을 표시합니다)</li>
<li><code>targets_unparsable</code> — 대상 목록의 줄을 파싱하지 못해 건너뛴 경우에만 포함됩니다. <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#file-%EB%AA%A8%EB%93%9C">파일 모드</a> 참고</li>
<li><code>baseline</code> — <code>--baseline</code>을 쓴 경우에만 포함됩니다. <a href="#%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%9D%BC%EC%9D%B8-%EC%83%88%EB%A1%9C-%EC%83%9D%EA%B8%B4-%EA%B2%83%EB%A7%8C-%EB%B3%B4%EA%B3%A0%ED%95%98%EA%B8%B0">베이스라인</a> 참고</li>
<li><code>resumed</code> — <code>--state-file</code>을 쓴 경우에만 포함됩니다. <code>state_file</code>(경로)과 <code>targets_skipped_completed</code>(이전 실행에서 끝나 건너뛴 대상 수)</li>
<li><code>incomplete</code> — 실행이 <strong>완전히 테스트되지 않았을 때</strong> <code>true</code>입니다. 스캔 도중 대상의 인증 세션이 끊어졌거나(<a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#%EC%84%B8%EC%85%98-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81">세션 모니터링</a> 참고), 전체 요청의 10% 이상(최소 3건)이 응답을 받지 못했거나, Ctrl-C / <code>--limit</code> / <code>--scan-timeout</code>으로 모든 대상이 끝나기 전에 실행이 멈춘 경우입니다. <code>target_summary</code> 항목을 전부 훑는 대신 이 필드 하나만 보세요. <code>&quot;findings_count&quot;: 0</code>과 <code>&quot;incomplete&quot;: true</code>가 함께 있다면 안전하다는 뜻이 <em>아닙니다</em></li>
</ul>
<p>세션이 끊어진 대상은 <code>&quot;status&quot;: &quot;incomplete&quot;</code>(아예 실행되지 않았다면 <code>&quot;skipped&quot;</code>)에 <code>&quot;error_code&quot;: &quot;SESSION_LOST&quot;</code>, 그리고 감지된 신호가 <code>&quot;error_message&quot;</code>에 담겨 보고됩니다. 절대 <code>&quot;clean&quot;</code>으로는 표시되지 않습니다. Ctrl-C, <code>--limit</code>, <code>--scan-timeout</code>으로 도중에 끊긴(또는 실행이 그 전에 멈춰 도달하지 못한) 대상도 탐지 결과가 없으면 <code>error_code</code> 없이 <code>&quot;incomplete&quot;</code>로 표시됩니다.</p>
<p><strong>SARIF</strong>에서는 엔벨로프가 <code>runs[0].properties</code>와 <code>runs[0].tool.driver.properties</code> 아래에 중복으로 실려, GitHub 코드 스캐닝을 비롯한 소비 도구가 컨텍스트를 잃지 않습니다. 각 결과의 <code>ruleId</code>는 <code>dalfox/cwe-&lt;n&gt;</code>(XSS는 <code>dalfox/cwe-79</code>, 오래된 라이브러리는 <code>dalfox/cwe-1104</code>)이고, <code>level</code>은 <code>severity</code>를 따르며(High → <code>error</code>, Medium → <code>warning</code>, Low / Info → <code>note</code>), PoC URL은 location의 <code>uri</code>에 들어갑니다. <code>partialFingerprints[&quot;vulnIdentity/v1&quot;]</code>은 코드 스캐닝이 실행 간에 같은 건을 맞춰 볼 수 있게 하는 안정적인 해시입니다. 탐지 결과 필드(<code>type</code>, <code>inject_type</code>, <code>param</code>, <code>payload</code>, <code>severity</code>, <code>detection_method</code>, <code>confidence</code> 등)는 결과의 <code>properties</code> 아래에 있고, <code>message.text</code>에는 <code>message_str</code>과 근거가 함께 담깁니다.</p>
<p><strong>TOML</strong>에서는 최상위 <code>[meta]</code> 테이블로 나타납니다(탐지 결과는 <code>[[results]]</code> 아래).</p>
<p><strong>Markdown</strong>에서는 탐지 결과 요약 위에 사람이 읽을 수 있는 테이블(<code>## Scan Metadata</code> + <code>### Target Summary</code>)로 렌더링됩니다. 실패한 요청, incomplete, 중복 제거된 대상, 베이스라인, 재개처럼 무슨 일이 있었을 때만 의미 있는 행은 그때만 나타납니다.</p>
<p>Plain 텍스트 출력은 탐지 결과만 담습니다.</p>
<h2 id="사일런스-모드">사일런스 모드</h2>
<p>로그 없이 <code>stdout</code>에 <strong>탐지 결과만</strong> 내보냅니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --silence
<span class="hljs-comment"># 탐지 결과를 다른 도구로 파이프:</span>
cat urls.txt <span class="hljs-punctuation">|</span> dalfox scan --silence -f jsonl <span class="hljs-punctuation">|</span> jq <span class="hljs-string">&#39;select(.severity==&quot;High&quot;)&#39;</span>
</code></pre>
<p>셸 파이프라인과 cron 작업에 유용합니다.</p>
<h2 id="긴-스캔-중-탐지-결과-스트리밍">긴 스캔 중 탐지 결과 스트리밍</h2>
<p>기본적으로 plain 렌더러는 각 탐지 결과 블록(POC + Issue /
Payload / Line)을 스캔 종료 시점의 <code>WRN XSS found N XSS</code> 요약 <strong>이후에</strong> 출력하므로,
로그는 자연스러운 순서(시작 → 진행 → 요약 → 세부 정보)로 읽힙니다.</p>
<p>대상이 크고 스캔이 길어질 때는 <code>--stream-findings</code>로 스캔 도중 출력으로 전환할 수 있습니다.
각 탐지 결과는 검증되는 즉시 진행 표시줄 위에 출력됩니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --stream-findings
</code></pre>
<p><code>--stream-findings</code>는 <code>plain</code> 형식에만 영향을 미칩니다. 스캔 종료 시점에 스트리머가 그대로
반영할 수 없는 필터(<code>--output</code>, <code>--limit</code>, <code>--only-poc</code>, <code>--baseline</code>)를 적용해야 하면 자동으로
비활성화됩니다. 명령줄에서 <code>--stream-findings</code>를 준 경우에는 이를 끈 플래그를 짚은 <code>Warning:</code>이 stderr에 출력됩니다.</p>
<h2 id="poc-스타일">POC 스타일</h2>
<p>개념 증명(proof-of-concept)을 다양한 클라이언트 형태로 다시 렌더링합니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --poc-type curl      <span class="hljs-comment"># curl 명령</span>
dalfox scan https://target.app --poc-type httpie    <span class="hljs-comment"># HTTPie</span>
dalfox scan https://target.app --poc-type http-request  <span class="hljs-comment"># 원시 HTTP</span>
</code></pre>
<p>기본값은 <code>plain</code>입니다. 티켓 등록에 적합합니다. <code>--poc-type</code>은 <code>plain</code> 리포트의 POC 줄만 바꿉니다. 구조화 형식은 항상 <code>data</code>에 PoC URL을 담습니다. <code>http-request</code>는 Dalfox가 해당 건에 기록한 원시 요청을 출력하며, 기록된 요청이 없으면 URL로 대체합니다.</p>
<h2 id="필터링">필터링</h2>
<p>특정 결과 유형만 표시합니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --only-poc v     <span class="hljs-comment"># V(Vulnerable)만</span>
dalfox scan https://target.app --only-poc v,a   <span class="hljs-comment"># V + AST</span>
</code></pre>
<p>결과 수를 제한합니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --limit <span class="hljs-number">50</span>
dalfox scan https://target.app --limit <span class="hljs-number">10</span> --limit-result-type v
</code></pre>
<h2 id="베이스라인-새로-생긴-것만-보고하기">베이스라인: 새로 생긴 것만 보고하기</h2>
<p><code>--only-poc</code>와 <code>--limit</code>은 <em>형태</em>로 거릅니다. 이미 트리아지를 끝낸 건과 오늘 아침에 새로 나타난 건을 구분하지 못하므로, 기존 이슈가 100건인 저장소는 PR마다 똑같은 100건을 다시 보게 되고 결국 게이트는 항상 빨간불이거나 꺼두게 됩니다.</p>
<p><code>--baseline</code>이 이 문제를 해결합니다. 이전 리포트를 지정하면 거기에 이미 있는 건은 억제됩니다.</p>
<pre><code class="language-bash hljs">dalfox scan scope.txt -f json -o baseline.json      <span class="hljs-comment"># 최초 1회, 기존 백로그 기록</span>
dalfox scan scope.txt --baseline baseline.json      <span class="hljs-comment"># 이후 매 실행</span>
</code></pre>
<p><strong>별도의 베이스라인 작성 명령은 없습니다.</strong> 평범한 <code>-f json -o</code>(또는 <code>-f jsonl -o</code>) 리포트가 그대로 베이스라인입니다.</p>
<h3 id="모드">모드</h3>
<table>
<thead>
<tr>
<th>모드</th>
<th>플래그</th>
<th>동작</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>filter</code> (기본)</td>
<td><code>--baseline-mode filter</code></td>
<td>이미 알려진 건을 제거합니다. 카운트, <code>--limit</code>, <strong>종료 코드</strong>가 모두 신규 건만 기준으로 결정됩니다 — CI 게이트용 모드입니다.</td>
</tr>
<tr>
<td><code>annotate</code></td>
<td><code>--baseline-mode annotate</code></td>
<td>모든 건을 그대로 두고 각각에 <code>new: true</code> / <code>new: false</code>를 붙입니다. 전체 집합을 보되 신규 여부를 표시하고 싶은 대시보드용입니다.</td>
</tr>
</tbody>
</table>
<h3 id="무엇을-같은-건으로-볼까">무엇을 &quot;같은 건&quot;으로 볼까</h3>
<p>지문(fingerprint)은 그 건을 드러낸 <strong>실행</strong>이 아니라 취약점 자체의 정체성을 기준으로 만듭니다.</p>
<p><strong>포함:</strong> 호스트 + 경로 · 파라미터 이름 · 파라미터 위치(query / header / cookie / body / path) · 인젝션 컨텍스트 · CWE · 탐지 티어 · 증거 계열(DOM 건의 <code>Source → Sink</code> 쌍).</p>
<p><strong>제외:</strong> 페이로드와 그것이 들어간 쿼리 스트링, 페이로드 순서, AST의 줄/열 번호, 타임스탬프, 요청/응답 캡처.</p>
<p>따라서 실행마다 페이로드가 달라져도 같은 스캔은 깔끔하게 매칭되고, 번들러가 <code>app.js</code>의 줄 번호를 밀어도 이미 처리한 DOM 건이 되살아나지 않습니다. <strong>티어</strong>가 지문에 들어가므로, 지난주 <code>R</code>이던 건이 오늘 <code>V</code>가 되면 신규로 보고됩니다. 이런 승격이야말로 게이트가 잡아야 할 변화입니다.</p>
<h3 id="metabaseline-블록"><code>meta.baseline</code> 블록</h3>
<p>모든 구조화 형식이 diff 결과를 함께 보고합니다.</p>
<pre><code class="language-json hljs"><span class="hljs-string">&quot;baseline&quot;</span>: <span class="hljs-punctuation">{</span>
  <span class="hljs-name">&quot;path&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;baseline.json&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-name">&quot;mode&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;filter&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-name">&quot;enabled&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">true</span><span class="hljs-punctuation">,</span>
  <span class="hljs-name">&quot;baseline_findings&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">100</span><span class="hljs-punctuation">,</span>
  <span class="hljs-name">&quot;new&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span>
  <span class="hljs-name">&quot;known&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">98</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>베이스라인 파일이 없거나, 형식이 깨졌거나, 다른 메이저 버전이 쓴 것이면 <strong>stderr에 경고를 내고 diff를 비활성화</strong>할 뿐 스캔을 실패시키지는 않습니다. 파이프라인에 남은 낡은 경로 하나 때문에 멀쩡히 돌던 스캔이 아무것도 보고하지 못하는 빨간 빌드가 되어서는 안 되기 때문입니다. 이 상황은 엔벨로프에 <code>&quot;enabled&quot;: false</code>와 <code>warning</code>으로 드러나므로, &quot;신규 없음&quot;과 &quot;diff가 아예 돌지 않음&quot;을 구분할 수 있습니다.</p>
<h3 id="베이스라인-갱신">베이스라인 갱신</h3>
<p>전용 명령은 없습니다. <code>--baseline</code> <strong>없이</strong> 다시 실행해(그래야 리포트에 신규 건만이 아니라 전체 집합이 담깁니다) 파일을 교체하면 됩니다.</p>
<pre><code class="language-bash hljs">dalfox scan scope.txt -f json -o baseline.json
git commit -am <span class="hljs-string">&quot;chore: refresh dalfox baseline&quot;</span>
</code></pre>
<p><code>--baseline</code>을 켠 채 <code>-o</code>를 같은 파일로 지정하면 <code>filter</code> 모드에서 베이스라인이 파괴됩니다. 다시 쓰인 리포트에는 신규 건만 들어 있어서 다음 실행이 백로그 전체를 다시 보고하게 됩니다. 두 경로가 같으면 Dalfox가 경고합니다.</p>
<h3 id="주의사항">주의사항</h3>
<ul>
<li><strong><code>--limit</code>은 diff 이전에 셉니다.</strong> 스캔 중 중단 조건은 수집되는 모든 건을 세므로(베이스라인에 이미 있는 건 포함), <code>--limit 10 --baseline b.json</code>으로 처음 10건이 전부 기존 건인 대상을 돌리면 나머지를 테스트하지 않은 채 조기 종료하고 &quot;신규 0&quot;을 보고합니다. 신규 기준으로 게이트할 때는 <code>--limit</code>을 빼세요. 둘을 함께 쓰면 Dalfox가 경고합니다.</li>
<li><strong><code>--stream-findings</code>는 <code>--baseline</code>이 있으면 비활성화됩니다.</strong> <code>--only-poc</code>과 같은 이유입니다. 스트리머는 어떤 건이 이미 베이스라인에 있는지 알 수 없어서, 요약은 신규만 보고하는데 화면에는 트리아지가 끝난 백로그 전체가 흘러가게 됩니다.</li>
<li><strong>CLI 전용입니다.</strong> <code>dalfox server</code>와 MCP 서버는 <code>--baseline</code>을 적용하지 않습니다. 공유 config의 <code>scan.baseline</code>도 그쪽에서는 조용히 무시됩니다.</li>
</ul>
<h2 id="색상-및-tty-동작">색상 및 TTY 동작</h2>
<pre><code class="language-bash hljs">dalfox scan https://target.app --no-color
<span class="hljs-comment"># 또는</span>
<span class="hljs-variable">NO_COLOR</span><span class="hljs-operator">=</span><span class="hljs-number">1</span> dalfox scan https://target.app
</code></pre>
<p>Dalfox는 출력이 파일이나 비 TTY로 리다이렉트될 때도 색상을 자동으로 비활성화합니다.</p>
<h2 id="toml">TOML</h2>
<p>JSON과 동일한 데이터 형태이며(다른 형식과의 일관성을 위한 최상위 <code>[meta]</code> 엔벨로프 포함), TOML로 작성됩니다. 탐지 결과는 <code>[[results]]</code> 테이블 배열로 렌더링됩니다.</p>
<pre><code class="language-toml hljs"><span class="hljs-punctuation">[</span>meta<span class="hljs-punctuation">]</span>
dalfox_version <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;3.2.3&quot;</span>
dedup_mode <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;exact&quot;</span>
failed_requests <span class="hljs-punctuation">=</span> <span class="hljs-number">0</span>
findings_count <span class="hljs-punctuation">=</span> <span class="hljs-number">1</span>
incomplete <span class="hljs-punctuation">=</span> <span class="hljs-literal">false</span>
scan_duration_ms <span class="hljs-punctuation">=</span> <span class="hljs-number">1234</span>
targets <span class="hljs-punctuation">=</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;https://target.app/?q=a&quot;</span><span class="hljs-punctuation">]</span>
targets_deduplicated <span class="hljs-punctuation">=</span> <span class="hljs-number">0</span>
total_requests <span class="hljs-punctuation">=</span> <span class="hljs-number">87</span>

<span class="hljs-punctuation">[[</span>meta<span class="hljs-punctuation">.</span>target_summary<span class="hljs-punctuation">]]</span>
findings_count <span class="hljs-punctuation">=</span> <span class="hljs-number">1</span>
status <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;findings&quot;</span>
target <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;https://target.app/?q=a&quot;</span>

<span class="hljs-punctuation">[[</span>results<span class="hljs-punctuation">]]</span>
type <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;V&quot;</span>
type_description <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;Vulnerable - dalfox asserts this input is exploitable; act on it&quot;</span>
inject_type <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;inHTML&quot;</span>
method <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;GET&quot;</span>
data <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;https://target.app/?q=%3Csvg%20onload%3Dalert%281%29%20class%3Ddlx1ec4110f%3E&quot;</span>
param <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;q&quot;</span>
payload <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;&lt;svg onload=alert(1) class=dlx1ec4110f&gt;&quot;</span>
evidence <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;DOM verification successful for param q (DOM marker)&quot;</span>
cwe <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;CWE-79&quot;</span>
severity <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;High&quot;</span>
message_id <span class="hljs-punctuation">=</span> <span class="hljs-number">606</span>
message_str <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;Triggered XSS Payload (DOM marker): q=&lt;svg onload=alert(1) class=dlx1ec4110f&gt;&quot;</span>
location <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;Query&quot;</span>
detection_method <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;reflection&quot;</span>
confidence <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;high&quot;</span>
confidence_reason <span class="hljs-punctuation">=</span> <span class="hljs-string">&quot;payload reached an executable position in the parsed response&quot;</span>
</code></pre>
<pre><code class="language-bash hljs">dalfox scan https://target.app -f toml -o report.toml
</code></pre>
<h2 id="sarif-github-코드-스캐닝">SARIF → GitHub 코드 스캐닝</h2>
<pre><code class="language-bash hljs">dalfox scan urls.txt -f sarif -o dalfox.sarif
</code></pre>
<p>GitHub의 <code>upload-sarif</code> 액션으로 <code>dalfox.sarif</code>를 업로드하면, 탐지 결과가 리포지토리의 <strong>Security → Code scanning</strong> 탭에 나타납니다.</p>
<h2 id="ci-예시">CI 예시</h2>
<pre><code class="language-yaml hljs"><span class="hljs-comment"># .github/workflows/xss-scan.yml</span>
- <span class="hljs-name">name</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">Dalfox scan</span>
  <span class="hljs-name">run</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">dalfox scan scope.txt -f sarif -o dalfox.sarif --silence --waf-evasion</span>

- <span class="hljs-name">uses</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">github/codeql-action/upload-sarif@v3</span>
  <span class="hljs-name">with</span><span class="hljs-punctuation">:</span>
    <span class="hljs-name">sarif_file</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">dalfox.sarif</span>
</code></pre>
<h3 id="신규-탐지-결과만으로-게이트하기">신규 탐지 결과만으로 게이트하기</h3>
<p><code>baseline.json</code>을 스코프 파일과 함께 커밋해 두고 종료 코드로 빌드를 실패시키세요. 베이스라인에 없는 건이 나타났을 때만 빨간불이 됩니다.</p>
<pre><code class="language-yaml hljs"><span class="hljs-comment"># .github/workflows/xss-scan.yml</span>
- <span class="hljs-name">name</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">Dalfox scan (new findings gate)</span>
  <span class="hljs-name">run</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">|</span><span class="hljs-doctag">
    dalfox scan scope.txt \
      --baseline .dalfox/baseline.json \
      --only-poc v \
      -f json -o dalfox.json --silence</span>    

- <span class="hljs-name">name</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">Upload report</span>
  <span class="hljs-name">if</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">always()</span>
  <span class="hljs-name">uses</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">actions/upload-artifact@v4</span>
  <span class="hljs-name">with</span><span class="hljs-punctuation">:</span>
    <span class="hljs-name">name</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">dalfox-report</span>
    <span class="hljs-name">path</span><span class="hljs-punctuation">:</span> <span class="hljs-literal">dalfox.json</span>
</code></pre>
<p>트리아지 후 베이스라인을 갱신하려면 <code>--baseline</code> <strong>없이</strong> 실행하고 <code>-o</code>를 베이스라인 파일로 지정하세요.</p>
<pre><code class="language-bash hljs">dalfox scan scope.txt --only-poc v -f json -o .dalfox/baseline.json
</code></pre>
<p>위 게이트 명령을 그대로 두고 <code>-o .dalfox/baseline.json</code>만 붙이면 <em>신규</em> 건만 담긴 리포트가 파일을 덮어써서 기록해둔 백로그가 날아갑니다. <code>--output</code>과 <code>--baseline</code>이 같은 경로로 해석되면 Dalfox가 stderr에 경고합니다.</p>
<h2 id="종료-코드">종료 코드</h2>
<p>Dalfox는 다음을 반환합니다.</p>
<table>
<thead>
<tr>
<th>코드</th>
<th>의미</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code></td>
<td>성공적으로 완료, 탐지 결과 없음</td>
</tr>
<tr>
<td><code>1</code></td>
<td>성공적으로 완료, <strong>티어와 무관하게</strong> 탐지 결과 하나 이상</td>
</tr>
<tr>
<td><code>2</code></td>
<td>입력/설정/런타임 오류, 또는 <code>-o</code> 파일을 쓰지 못한 경우. 탐지 결과가 없을 때는 다음도 해당: 모든 대상을 건너뜀(접속 불가, 맞지 않는 콘텐츠 타입 등), 대상의 스캔 워커가 중단됨(<code>INTERNAL_ERROR</code>), 요청의 10% 이상(최소 3건)이 응답을 받지 못함, 기본값 <code>--on-session-loss abort</code>에서 스캔 도중 세션이 끊어짐 (탐지 결과가 있었다면 여전히 <code>1</code>)</td>
</tr>
</tbody>
</table>
<p><code>1</code>은 모든 티어를 포함합니다. <code>R</code> 하나나 <code>--detect-outdated-libs</code>가 만든 <code>I</code> 하나도 <code>V</code>와 똑같이 빌드를 실패시킵니다. Dalfox가 악용 가능하다고 판단한 것만 게이트로 삼으려면 <code>--only-poc v</code>를 주고 종료 코드를 그대로 쓰세요. 코드가 정해지기 전에 필터가 적용됩니다. (JSON에 <code>jq</code>로 <code>severity == &quot;High&quot;</code>를 거는 방식도 오늘은 거의 같은 집합을 얻습니다. severity가 현재 티어를 따라가기 때문입니다. <code>V</code>는 <code>High</code>, <code>A</code>는 <code>Medium</code>, <code>R</code>은 <code>Info</code>입니다. 예외는 <code>I</code> 라이브러리 결과로, 권고(advisory)의 severity를 그대로 가지므로 <code>High</code>일 수 있습니다. <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a> 참고.)</p>
<p><code>--baseline</code>은 같은 종료 코드를 <strong>신규 여부</strong>로 좁힙니다. 기본 <code>filter</code> 모드에서는 억제된 건이 종료 코드 판정에 도달하지 않으므로, 백로그가 전부 베이스라인에 들어 있는 실행은 <code>0</code>으로 끝납니다. <a href="#%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%9D%BC%EC%9D%B8-%EC%83%88%EB%A1%9C-%EC%83%9D%EA%B8%B4-%EA%B2%83%EB%A7%8C-%EB%B3%B4%EA%B3%A0%ED%95%98%EA%B8%B0">베이스라인</a> 참고.</p>
<h2 id="다음-단계">다음 단계</h2>
<ul>
<li><a href="https://dalfox.hahwul.com/ko/integrations/server/">REST API 서버</a>로 스캔을 자동화하세요.</li>
<li><a href="https://dalfox.hahwul.com/ko/integrations/mcp/">MCP 서버</a>로 AI 에이전트에 맡기세요.</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>파라미터와 탐색</title>
      <link>https://dalfox.hahwul.com/ko/guide/parameters/</link>
      <guid>https://dalfox.hahwul.com/ko/guide/parameters/</guid>
      <description>Dalfox가 query, body, header, cookie, path, fragment, GraphQL, XML 파라미터를 찾고 필터링하고 테스트하는 방식을 설명합니다.</description>
      <content:encoded><![CDATA[<p>XSS를 찾는 일은 올바른 파라미터를 찾는 데서 시작합니다. Dalfox의 탐색 엔진은 여러 단계로 이루어진 파이프라인입니다. 전체를 이해할 필요는 거의 없지만, 각 구성 요소를 알아두면 스캔을 튜닝하고 싶을 때 도움이 됩니다.</p>
<h2 id="파이프라인-개요">파이프라인 개요</h2>
<ol>
<li><strong>Discovery(탐색):</strong> 요청에 이미 들어 있는 입력을 프로빙합니다. 쿼리 값(과 쿼리 파라미터 <em>이름</em>), 헤더, 쿠키, 경로 세그먼트, 페이지에서 찾은 폼의 필드가 대상입니다. URL 프래그먼트 키도 목록에 올라가고 <code>-p</code>로 지정할 수도 있지만, 프래그먼트는 서버에 전달되지 않으므로 스캔은 이를 퍼징하지 않습니다.</li>
<li><strong>Mining(마이닝):</strong> <code>-d</code> 본문의 파라미터(form, JSON, GraphQL 변수, XML, multipart)를 프로빙한 뒤, 요청에 없는 이름을 찾습니다. 사전 워드리스트와 응답 속 <code>&lt;input&gt;</code> 요소의 <code>id</code>/<code>name</code>이 그 출처입니다.</li>
<li><strong>Active probing(능동 프로빙):</strong> 각 파라미터를 프로빙해 어떤 특수 문자가 살아남는지 파악하고, URL 디코딩을 여러 번 하는 서버를 찾아냅니다.</li>
<li><strong>Payload generation(페이로드 생성):</strong> 컨텍스트를 인식하는 페이로드 세트(HTML, JS, 속성, CSS)를 만듭니다. 파라미터의 페이로드를 보내기 전에 <strong>빠른 프로브</strong>(fast probe)가 샌드위치 마커 요청을 한 번 보냅니다(영문자를 제거하는 필터를 잡기 위한 숫자 전용 대체 프로브 포함). 아무것도 반사되지 않으면 <code>--deep-scan</code>이 아닌 한 해당 파라미터의 무거운 페이로드 루프를 건너뜁니다. 능동 프로빙에서 이미 마커가 돌아온 것을 봤다면, 빠른 프로브는 요청을 따로 보내지 않고 그 결과를 재사용합니다.</li>
<li><strong>Reflection check(반사 확인):</strong> 페이로드를 보낸 뒤 그것이 돌아오는지 확인합니다.</li>
<li><strong>DOM verification(DOM 검증):</strong> 응답을 파싱하여 페이로드가 실행 가능한 위치에 도달했는지 확인합니다. AST 기반 DOM-XSS 분석은 먼저 랜딩 페이지 자체에 대해 한 번 실행되고, 이어서 파라미터당 한 번 빠른 프로브의 응답(빠른 프로브를 건너뛴 경우에는 첫 반사 응답)으로 실행됩니다.</li>
</ol>
<h2 id="특정-파라미터-지정하기">특정 파라미터 지정하기</h2>
<p>테스트할 파라미터를 Dalfox에 정확히 지정합니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app/api <span class="hljs-string">\
</span>  -p q <span class="hljs-string">\
</span>  -p id:query <span class="hljs-string">\
</span>  -p auth:header <span class="hljs-string">\
</span>  -p token:cookie
</code></pre>
<p>위치(Location): <code>query</code>, <code>body</code>, <code>json</code>, <code>multipart</code>, <code>cookie</code>, <code>header</code>, <code>graphql</code>, <code>xml</code>. 주입 지점이 쿼리 문자열이 아니라면 <code>name:location</code> 형식을 쓰세요. <code>graphql</code>과 <code>xml</code>은 요청 본문에서 자동으로 탐지됩니다(<a href="#graphql-%EB%B0%8F-xml-%EB%B3%B8%EB%AC%B8-%EC%A3%BC%EC%9E%85">GraphQL 및 XML 본문 주입</a> 참조). 힌트는 이미 발견된 파라미터를 필터링할 수는 있지만, 이름만으로 새 본문을 합성하지는 못합니다. <code>path</code>와 <code>fragment</code>도 같은 방식으로 필터로만 동작합니다. 경로 세그먼트는 위치로 이름이 붙습니다(<code>-p path_segment_0:path</code>).</p>
<p>위치 힌트가 없는 경우(<code>-p q</code>만 지정):</p>
<ol>
<li>탐색/마이닝이 이미 해당 이름의 파라미터를 찾았다면, 그대로 유지합니다(필터).</li>
<li>그렇지 않으면 Dalfox가 이를 <strong>합성</strong>(synthesize)합니다. 위치는 요청에서 추론하며(URL 쿼리 → 본문 → 쿠키 → 헤더), 기본값은 <code>query</code>입니다.</li>
</ol>
<p>즉, <code>-p q --skip-discovery --skip-mining</code> 같은 조합도 아무것도 스캔하지 않고 조용히 지나가는 대신 <code>q</code>를 그대로 테스트합니다.</p>
<h2 id="워드리스트로-마이닝하기">워드리스트로 마이닝하기</h2>
<p>URL에 파라미터가 없더라도 Dalfox는 흔히 쓰이는 이름을 시도해 볼 수 있습니다:</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># 로컬 워드리스트</span>
dalfox scan https://target.app -W ./params.txt

<span class="hljs-comment"># 원격 워드리스트(첫 조회 이후 캐시됨)</span>
dalfox scan https://target.app --remote-wordlists burp,assetnote
</code></pre>
<p>스캔당 목록은 하나만 사용합니다. <code>--remote-wordlists</code>를 불러오는 데 성공하면 그것이 우선하고 <code>-W</code>는 무시됩니다. <code>-W</code>는 원격 조회가 실패했을 때의 대체 수단입니다. 마이닝한 이름은 쿼리 파라미터로 테스트합니다.</p>
<p>사전과 DOM 후보는 이름마다 요청을 하나씩 보내지 않고, 요청 하나에 최대 64개 이름을 담은 버킷으로 검사합니다(요청 라인은 약 8 KiB 이하로 유지). 카나리(canary)가 반사된 이름은 그 응답 하나로 식별됩니다. 아무것도 반사되지 않았는데 응답이 달라진 버킷은 같은 크기의 대조(control) 요청과 비교한 뒤, 버킷을 나눠 어떤 이름이 변화를 일으켰는지 찾습니다. 추가 요청은 이런 모호한 버킷에만 쓰므로 큰 워드리스트도 저렴하게 확인할 수 있습니다.</p>
<p>같은 요청에도 본문이 매번 달라지는 페이지(순환 위젯, 타임스탬프 등)에서는 상태 코드 변화만 응답 변화로 인정합니다. 이름이 여러 개 든 버킷의 요청이 실패했거나 서버가 크기 때문에 거부했다면(예: 쿼리 길이 제한) 버리지 않고 나눠서 다시 보냅니다.</p>
<p>사용자 지정 또는 원격 워드리스트를 선택하지 않으면 기존 XSS 중심 이름을
유지하면서 API, 인증, 페이지네이션, feature flag, 미디어, 운영 관련 이름을
넓힌 Param Miner 시드가 기본 목록에 추가됩니다.</p>
<h3 id="자동-축소auto-collapse">자동 축소(Auto-collapse)</h3>
<p>반사가 매우 심한 사이트(예: 모든 것을 그대로 되돌려주는 검색 페이지)는 워드리스트 마이닝을 폭발적으로 늘릴 수 있습니다. Dalfox는 두 가지 방법으로 이를 방어합니다:</p>
<ul>
<li><strong>Sentinel 사전 프로브(pre-probe):</strong> 워드리스트를 순회하기 전에, 실제 필드와 절대 충돌하지 않을 무작위처럼 보이는 파라미터 이름 세 개를 테스트합니다. 세 개가 모두 반사되면 그 페이지는 거울(mirror)이므로 마이닝을 건너뛰고 단일 합성 <code>any</code> Query 파라미터로 대체합니다. 비용 상한: 워드리스트 크기와 무관하게 3개 요청. 사전 프로브가 이득이 될 만큼 워드리스트가 충분히 클 때(&gt;15개 항목)만 실행됩니다.</li>
<li><strong>EWMA 축소:</strong> 버킷 처리가 끝난 뒤 Dalfox는 이동 반사 비율(rolling reflection ratio)을 관찰합니다. 후보 이름 15개 이상, 반사 5건 이상을 거친 뒤에도 그 값이 ≥85%이면 작은 목록에 대해 확인 프로브를 수행합니다. sentinel도 반사되면 마이닝한 Query 파라미터를 <code>any</code> 플레이스홀더 하나로 접고, 반사되지 않으면 확인된 후보를 모두 유지합니다. 따라서 sentinel이 부정되더라도 큰 워드리스트의 나머지 커버리지는 잘리지 않습니다.</li>
</ul>
<p>sentinel이 확인된 경로는 합성 Query 주입 지점 하나를 만듭니다. sentinel이 부정되면 개별 반사 이름을 유지하면서도 버킷 요청의 효율은 그대로 얻습니다.</p>
<h2 id="노이즈-정리하기">노이즈 정리하기</h2>
<p>특정 파라미터 무시:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --ignore-param csrf --ignore-param __RequestVerificationToken
</code></pre>
<p>URL 패턴으로 범위 지정:</p>
<pre><code class="language-bash hljs">dalfox scan urls.txt <span class="hljs-string">\
</span>  --include-url <span class="hljs-string">&#39;^https://api\.target\.app/&#39;</span> <span class="hljs-string">\
</span>  --exclude-url <span class="hljs-string">&#39;/static/|/health&#39;</span>
</code></pre>
<p>범위 밖(out-of-scope) 도메인 목록:</p>
<pre><code class="language-bash hljs">dalfox scan urls.txt --out-of-scope-file scope-block.txt
<span class="hljs-comment"># 또는 와일드카드를 사용해 인라인으로(플래그를 반복하고, 플래그 하나에 패턴 하나)</span>
dalfox scan urls.txt --out-of-scope <span class="hljs-string">&#39;*.google.com&#39;</span> --out-of-scope <span class="hljs-string">&#39;*.cdn.cloudflare.net&#39;</span>
</code></pre>
<p><code>--out-of-scope</code>는 쉼표로 나누지 않습니다. <code>'*.google.com,*.cdn.cloudflare.net'</code>은 패턴 하나로 읽히고 아무것과도 일치하지 않습니다.</p>
<h2 id="탐색만-하고-공격하지-않기">탐색만 하고 공격하지 않기</h2>
<p>두 모드 모두 같은 탐색, 마이닝, 능동 프로빙 요청을 실행하고 스캔 단계 전에 멈추므로, 스캔의 XSS 페이로드는 보내지 않습니다. 차이는 무엇을 출력하느냐입니다.</p>
<p>Dry-run은 공격 계획을 출력합니다. 대상 수, 대상별로 찾은 파라미터, 실제 스캔이 보낼 요청 수의 하한 추정치입니다. WAF 자극 프로브(<code>&lt;script&gt;</code> 페이로드를 담은 요청 1건)도 건너뜁니다. <code>--only-discovery</code>는 <code>--skip-waf-probe</code>를 주지 않는 한 이 프로브를 보내므로, 공격 형태의 요청을 하나도 보내면 안 될 때는 dry-run을 쓰세요.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --dry-run
</code></pre>
<p>Discovery-only는 탐색된 파라미터마다 한 줄(URL, 이름, 위치)을 출력합니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --only-discovery
</code></pre>
<p>두 모드 모두 범위 지정과 CI 사전 점검에 도움이 됩니다.</p>
<h2 id="단계-건너뛰기">단계 건너뛰기</h2>
<p>더 빠르게 진행하거나 불안정한 대상을 우회하려면 파이프라인의 일부를 건너뛰세요:</p>
<table>
<thead>
<tr>
<th>플래그</th>
<th>건너뛰는 대상</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--skip-discovery</code></td>
<td>탐색 단계 전체(쿼리, 헤더, 쿠키, 경로, 폼, 프래그먼트)</td>
</tr>
<tr>
<td><code>--skip-mining</code></td>
<td>모든 워드리스트/DOM 마이닝</td>
</tr>
<tr>
<td><code>--skip-mining-dict</code></td>
<td>사전 마이닝만</td>
</tr>
<tr>
<td><code>--skip-mining-dom</code></td>
<td><code>&lt;input&gt;</code>의 <code>id</code>/<code>name</code> 속성에서 파라미터 이름을 수집하는 마이닝만</td>
</tr>
<tr>
<td><code>--skip-reflection-header</code></td>
<td>내장된 공통 헤더 스윕. <code>-H</code>로 넘긴 헤더는 여전히 프로빙됩니다</td>
</tr>
<tr>
<td><code>--skip-reflection-cookie</code></td>
<td>요청에 담긴 쿠키 프로빙</td>
</tr>
<tr>
<td><code>--skip-reflection-path</code></td>
<td>경로 세그먼트 반사 확인</td>
</tr>
</tbody>
</table>
<p>명시적으로 지정한 것은 이 플래그들의 영향을 받지 않습니다. <code>-d</code> 본문 파라미터는 <code>--skip-mining</code>에서도 프로빙되고, <code>-p name:header</code>나 <code>-p name:cookie</code>는 해당 <code>--skip-reflection-*</code> 플래그가 있어도 프로빙됩니다. 페이로드 쪽 건너뛰기 플래그(<code>--skip-xss-scanning</code>, <code>--skip-ast-analysis</code>, <code>--skip-waf-probe</code>)는 <a href="https://dalfox.hahwul.com/ko/reference/cli/">CLI 레퍼런스</a>에 있습니다.</p>
<blockquote>
<p><code>--skip-mining-dom</code>은 응답 HTML에서 파라미터 <em>이름</em>을 수집하는 동작만 멈춥니다. DOM-XSS 탐지 자체를 끄지는 <strong>않습니다</strong>: 인라인 <code>&lt;script&gt;</code> 블록을 정적 분석해 <code>location.hash</code> → <code>innerHTML</code> 같은 source→sink 흐름을 찾아 <code>[A]</code>(AST 탐지) 결과를 내는 패스는 <a href="https://dalfox.hahwul.com/ko/guide/payloads/#%ED%8E%98%EC%9D%B4%EB%A1%9C%EB%93%9C-%EB%8B%A8%EA%B3%84-%EA%B1%B4%EB%84%88%EB%9B%B0%EA%B8%B0"><code>--skip-ast-analysis</code></a>가 제어하는 별개의 단계입니다. 결과에서 해당 항목만 걸러내려면 <code>--only-poc v,r</code>을 사용하세요. 두 서브시스템의 차이와 각 증거 등급의 의미는 <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a> 문서를 참고하세요.</p>
</blockquote>
<h2 id="주입-마커injection-markers">주입 마커(Injection markers)</h2>
<p>주입 지점을 이미 알고 있다면 <code>--inject-marker</code>로 표시하세요:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app/api <span class="hljs-string">\
</span>  --inject-marker FUZZ <span class="hljs-string">\
</span>  -d <span class="hljs-string">&#39;{&quot;filter&quot;:&quot;FUZZ&quot;}&#39;</span>
</code></pre>
<p>마커를 지정하면 탐색, 마이닝, 능동 프로빙을 건너뜁니다. <code>FUZZ</code>를 포함한 쿼리 값, form 본문 값, 최상위 JSON 문자열 값, 헤더 값, 쿠키 값이 각각 파라미터가 되고, 각 페이로드는 그 값 전체를 대체합니다. 그 밖의 위치(경로 세그먼트, 중첩된 JSON 필드)에 둔 마커는 인식하지 않습니다.</p>
<p>쿼리 파라미터나 헤더를 직접 지정할 수도 있습니다:</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># 쿼리 파라미터</span>
dalfox scan <span class="hljs-string">&#39;https://example.com/?q=FUZZ&amp;page=1&#39;</span> --inject-marker FUZZ

<span class="hljs-comment"># 헤더</span>
dalfox scan https://example.com -H <span class="hljs-string">&#39;X-Search: FUZZ&#39;</span> --inject-marker FUZZ
</code></pre>
<h2 id="자동-사전-인코딩auto-pre-encoding">자동 사전 인코딩(Auto pre-encoding)</h2>
<p>일부 엔드포인트는 페이로드를 원시 텍스트로 받아들이지 않습니다. 이들은 어떤 구조적 인코딩(base64, JSON, JWT 등)으로 감싸진 형태를 기대합니다. 쿼리 탐색 중 어떤 파라미터에서 일반 마커가 돌아오지 않으면, Dalfox는 아래의 래핑 형태를 시도하고 마커가 반사된 래핑을 유지합니다. 이후 그 파라미터의 페이로드는 같은 래핑을 거쳐 전송됩니다. 설정할 것은 없습니다. 쿼리 파라미터에 적용됩니다.</p>
<p>단일 단계 인코딩은 마커를 미리 인코딩해 보내는 방식으로 찾습니다:</p>
<table>
<thead>
<tr>
<th>탐지된 형태</th>
<th>페이로드 인코딩 방식</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>base64</code></td>
<td><code>BASE64(payload)</code></td>
</tr>
<tr>
<td><code>2base64</code></td>
<td><code>BASE64(BASE64(payload))</code></td>
</tr>
<tr>
<td><code>2url</code> / <code>3url</code></td>
<td>2회 또는 3회 URL 인코딩</td>
</tr>
</tbody>
</table>
<p>능동 프로빙도 <code>&lt;</code>가 필터링되는 쿼리·경로 파라미터에 <code>2url</code> / <code>3url</code>을 시도해, URL 디코딩을 여러 번 하는 서버를 잡아냅니다.</p>
<p>조합 가능한 파이프라인은 파라미터의 기존 값에서 추론합니다. 값이 구조화된 래퍼로 디코딩되면, Dalfox는 모든 리프(leaf) 문자열 필드를 각각 별도의 가상 하위 파라미터로 프로빙하고, 마커가 반사된 리프를 유지합니다:</p>
<table>
<thead>
<tr>
<th>래퍼 형태</th>
<th>파이프라인</th>
</tr>
</thead>
<tbody>
<tr>
<td>Base64로 감싼 JSON <code>?qs=eyJ…</code></td>
<td><code>JsonField(/leaf) → Base64</code></td>
</tr>
<tr>
<td>Base64URL로 감싼 JSON</td>
<td><code>JsonField(/leaf) → Base64Url</code></td>
</tr>
<tr>
<td>순수 URL 인코딩된 JSON <code>?blob=%7B…%7D</code></td>
<td><code>JsonField(/leaf)</code></td>
</tr>
<tr>
<td>JWT/JWS <code>?token=h.p.s</code></td>
<td><code>JsonField(/leaf) → Base64Url → JwtAssemble</code></td>
</tr>
</tbody>
</table>
<p>각 리프는 대괄호 스타일 표시 이름으로 각각 별도의 Param에 등록됩니다. <code>qs</code>의 <code>move_url</code> 필드에 있는 페이로드는 <code>qs[move_url]</code>로 표시되고, 배열 요소는 <code>qs[items][0]</code>으로 나타납니다. 와이어 수준의 치환은 여전히 원래 부모 파라미터(<code>qs</code>)를 대상으로 하므로, 요청은 서버에 정상적으로 보입니다.</p>
<p>JWT의 경우 원래 헤더와 서명 세그먼트는 그대로(verbatim) 보존됩니다. 서명은 수정된 페이로드와 일치하지 않으므로, 이는 토큰을 검증하지 않는 엔드포인트에서만 발동합니다. 올바르게 서명된 JWT는 탐지 결과를 반환하지 않습니다. 이는 놓친 것이 아니라 의도된 동작입니다.</p>
<p>대상이 Dalfox가 자동 탐지하지 못하는 래핑을 쓴다면, <code>--inject-marker</code>(위 참조)로 주입 지점을 고정할 수는 있지만 페이로드는 래핑 없이 그대로 전송됩니다.</p>
<h2 id="graphql-및-xml-본문-주입">GraphQL 및 XML 본문 주입</h2>
<p>요청 본문(<code>-d</code>, 또는 캡처된 <code>raw-http</code> / <code>har</code> 요청)이 GraphQL이나 XML 문서라면, Dalfox는 본문 전체를 하나의 불투명한 덩어리로 테스트하는 대신 그 안의 값들을 주입 지점으로 다룹니다.</p>
<p><strong>GraphQL</strong>(JSON 본문. content type이 아니라 본문의 형태로 판별합니다): GraphQL 오퍼레이션(값이 <code>query</code>/<code>mutation</code>/<code>subscription</code> 또는 익명 <code>{ … }</code> 축약형으로 시작하는 <code>query</code>/<code>mutation</code> 필드)과 <code>variables</code> 오브젝트를 <strong>둘 다</strong> 가진 본문은, <code>variables</code> 안의 모든 문자열 리프를 프로빙하고 반사되는 것마다 각각 <code>graphql</code> 파라미터(이름은 <code>variables.&lt;경로&gt;</code>)로 등록합니다. 각 페이로드는 요청 전체를 재구성하며(오퍼레이션과 다른 변수들은 그대로 함께 전송) 서버는 항상 유효하고 파싱 가능한 GraphQL 요청을 받습니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app/graphql <span class="hljs-string">\
</span>  -X POST <span class="hljs-string">\
</span>  -H <span class="hljs-string">&#39;Content-Type: application/json&#39;</span> <span class="hljs-string">\
</span>  -d <span class="hljs-string">&#39;{&quot;query&quot;:&quot;query($q:String!){ search(term:$q){ id } }&quot;,&quot;variables&quot;:{&quot;q&quot;:&quot;seed&quot;}}&#39;</span>
<span class="hljs-comment"># → variables.q 가 `graphql` 파라미터로 주입됩니다</span>
</code></pre>
<p>단지 <code>query</code>라는 이름의 필드만 있는 평범한 REST 엔드포인트(검색창, <code>{&quot;query&quot;:&quot;laptop&quot;}</code>)는 GraphQL로 <strong>취급되지 않습니다</strong> — <code>variables</code> 오브젝트와 오퍼레이션 형태의 값이 모두 필요하므로, 일반 JSON 본문은 그대로 <code>json</code> 경로에 남습니다.</p>
<p><strong>XML / SOAP</strong>(<code>text/xml</code>, <code>application/xml</code>, <code>application/soap+xml</code>, <code>application/xhtml+xml</code>, 또는 <code>&lt;?xml …?&gt;</code> 프롤로그가 있는 본문): 각 엘리먼트 텍스트 노드와 속성 값을 프로빙하고, 반사되는 것마다 <code>xml</code> 파라미터가 됩니다. 바이트 범위 스플라이스(byte-range splice)로 페이로드를 제자리에 주입하며, 문서의 다른 모든 바이트 — 네임스페이스, 형제 엘리먼트, SOAP 엔벨로프 — 는 그대로 유지되고 요청의 XML content-type도 보존됩니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app/soap <span class="hljs-string">\
</span>  -X POST <span class="hljs-string">\
</span>  -H <span class="hljs-string">&#39;Content-Type: application/soap+xml&#39;</span> <span class="hljs-string">\
</span>  -d <span class="hljs-string">&#39;&lt;soap:Envelope xmlns:soap=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot;&gt;&lt;soap:Body&gt;&lt;search&gt;&lt;term&gt;seed&lt;/term&gt;&lt;/search&gt;&lt;/soap:Body&gt;&lt;/soap:Envelope&gt;&#39;</span>
<span class="hljs-comment"># → &lt;term&gt; 텍스트 노드가 `xml` 파라미터로 주입됩니다</span>
</code></pre>
<p>모든 반사 탐지 결과와 마찬가지로, 값은 응답이 브라우저가 마크업으로 렌더링할 문서일 때만 <code>[V]</code>로 등급이 매겨집니다(<a href="https://dalfox.hahwul.com/ko/guide/detection-model/#%EC%9D%91%EB%8B%B5-content-type%EC%9D%B4-%ED%97%88%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%83">탐지 모델</a> 참고) — <code>application/json</code>으로 응답하는 GraphQL API나 이스케이프된 값을 되돌려주는 XML 서비스는 올바르게 무해(inert)로 보고됩니다. 실제로 위험한 곳은 반사된 값을 마크업으로 렌더링하는 관리자 화면, 리포트, 에러 페이지입니다.</p>
<h2 id="반사-프로브-형태">반사 프로브 형태</h2>
<p>모든 탐색 및 마이닝 프로브는 단일 토큰 대신 샌드위치 마커(<code>OPEN + INNER + CLOSE</code>)를 보냅니다. 그런 다음 응답은 네 가지 경우 중 하나로 분류됩니다:</p>
<table>
<thead>
<tr>
<th>반사</th>
<th>의미</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Full</strong></td>
<td>완전한 <code>OPEN+INNER+CLOSE</code>가 살아남음. 표준 반사.</td>
</tr>
<tr>
<td><strong>PrefixOnly</strong></td>
<td><code>OPEN+INNER</code>는 존재하고 <code>CLOSE</code>가 제거됨. 접미사 제거 필터를 시사.</td>
</tr>
<tr>
<td><strong>SuffixOnly</strong></td>
<td><code>INNER+CLOSE</code>는 존재하고 <code>OPEN</code>이 제거됨. 접두사 제거 필터를 시사.</td>
</tr>
<tr>
<td><strong>InnerOnly</strong></td>
<td><code>INNER</code>만 살아남음. 정규식 추출이거나 양쪽 래핑이 모두 제거됐음을 시사.</td>
</tr>
</tbody>
</table>
<p>네 가지 모두 &quot;반사됨&quot;으로 취급됩니다. 탐색은 해당 파라미터를 기록하고 스캔이 진행됩니다. 단순한 단일 토큰 확인이었다면 <em>Full</em>을 제외한 모든 경우를 놓쳐, 접두사/접미사를 제거하는 엔드포인트를 탐지하지 못했을 것입니다. 마커 토큰은 스캔마다 고유합니다(<code>dlx</code>/<code>dlxmid</code>/<code>xld</code> 접두사에 스캔당 8자리 16진수가 붙음). 따라서 HTML에서 우연히 충돌할 가능성은 무시할 만합니다.</p>
<h2 id="탐지-결과가-검증됨이-되는-기준">탐지 결과가 &quot;검증됨&quot;이 되는 기준</h2>
<table>
<thead>
<tr>
<th>결과</th>
<th>확인 방식</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>V</strong> (Vulnerable, 취약)</td>
<td>Dalfox가 응답 DOM을 파싱해 페이로드가 실행될 수 있는 위치에 있음을 확인합니다. 브라우저 실행이 아니라 실제 응답을 정적으로 파싱한 결과입니다. <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a> 참고. <code>evidence</code> 필드는 이를 입증한 경로를 태깅합니다: DOM 마커(CSS 셀렉터 적중), 실행 가능한 URL(위험한 속성 안의 <code>javascript:</code>/<code>data:</code>), HTML 구조적 증거(값이 싱크 호출인 <code>on*</code> 핸들러를 가진 주입된 요소), JS 컨텍스트 AST(파싱된 AST가 페이로드의 바이트 범위에 포함됨을 보여주는 <code>&lt;script&gt;</code> 내부의 싱크 호출), 인라인 핸들러 브레이크아웃(기존 <code>on*</code> 속성 안의 JS 문자열을 페이로드가 닫음).</td>
</tr>
<tr>
<td><strong>A</strong> (AST-detected, AST 탐지)</td>
<td>정적 JavaScript 분석이 사용자 제어 소스를 위험한 싱크로 추적함(예: <code>innerHTML = location.hash</code>).</td>
</tr>
<tr>
<td><strong>R</strong> (Reflected, 반사됨)</td>
<td>페이로드 텍스트가 응답 본문에 나타났지만 아직 DOM 증거는 없음. 여전히 수동으로 조사할 가치가 있음.</td>
</tr>
</tbody>
</table>
<p><code>V</code>와 <code>A</code>는 신호입니다. <code>R</code>은 힌트입니다.</p>
<h2 id="안전한-컨텍스트">안전한 컨텍스트</h2>
<p>반사된 위치가 모두 <code>&lt;textarea&gt;</code>, <code>&lt;title&gt;</code>, <code>&lt;noscript&gt;</code>, <code>&lt;xmp&gt;</code>, <code>&lt;plaintext&gt;</code> 내부라면 보고하지 않습니다. 그곳의 콘텐츠는 텍스트로 렌더링되므로 오탐(false positive)만 발생시킬 뿐입니다. 파라미터 자체는 계속 스캔하므로, 먼저 해당 요소를 닫는 페이로드(<code>&lt;/textarea&gt;&lt;svg onload=…&gt;</code>)는 여전히 찾을 수 있습니다.</p>
<p>같은 게이트가 무해한 형태 몇 가지를 더 걸러냅니다. <code>&lt;script&gt;</code> 안에만 반사되었고 파싱한 JavaScript상 싱크 호출이 생기지 않는 경우, URL 값 속성 밖에서 서버가 이스케이프(퍼센트 또는 엔티티 인코딩)해서 되돌려준 경우, 그리고 <code>javascript:</code> / <code>data:</code> 페이로드가 URL 값 속성의 맨 앞에 한 번도 오지 않는 경우입니다. 브라우저가 마크업으로 렌더링하지 않는 응답(JSON, <code>text/plain</code> 등)은 <a href="https://dalfox.hahwul.com/ko/guide/detection-model/#%EC%9D%91%EB%8B%B5-content-type%EC%9D%B4-%ED%97%88%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%83">content type</a> 기준으로 따로 걸러냅니다.</p>
<h2 id="다음-단계">다음 단계</h2>
<ul>
<li>페이로드가 어떻게 구성되는지는 <a href="https://dalfox.hahwul.com/ko/guide/payloads/">페이로드와 인코딩</a>에서 확인하세요.</li>
<li>WAF를 상대하고 있나요? <a href="https://dalfox.hahwul.com/ko/guide/waf-bypass/">WAF 우회</a>로 넘어가세요.</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>페이로드와 인코딩</title>
      <link>https://dalfox.hahwul.com/ko/guide/payloads/</link>
      <guid>https://dalfox.hahwul.com/ko/guide/payloads/</guid>
      <description>Dalfox XSS 페이로드 계열, 컨텍스트 인식 인코더, 커스텀 페이로드, 원격 워드리스트, 블라인드 XSS, 딥 스캔을 설정합니다.</description>
      <content:encoded><![CDATA[<p>Dalfox는 컨텍스트를 인식하는 엄선된 페이로드 라이브러리를 내장하고 있습니다. 대부분은 여기에 신경 쓸 일이 없습니다. 엔진이 각 주입 컨텍스트에 맞는 페이로드를 알아서 고릅니다. 이 페이지에서는 내장된 내용과 이를 확장하는 방법을 다룹니다.</p>
<h2 id="페이로드-계열">페이로드 계열</h2>
<p>Dalfox는 여러 계열로부터 페이로드를 구성합니다:</p>
<table>
<thead>
<tr>
<th>계열</th>
<th>예시</th>
<th>사용 시점</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>HTML 태그</strong></td>
<td><code>&lt;svg onload=alert(1)&gt;</code></td>
<td>HTML 컨텍스트</td>
</tr>
<tr>
<td><strong>속성 브레이크아웃</strong></td>
<td><code>&#39;&gt;&lt;img src=x onerror=alert(1)&gt;</code></td>
<td>속성 내부</td>
</tr>
<tr>
<td><strong>JavaScript</strong></td>
<td><code>&quot;;alert(1);//</code></td>
<td><code>&lt;script&gt;</code> 블록 내부</td>
</tr>
<tr>
<td><strong>이벤트 핸들러</strong></td>
<td><code>onmouseover=alert(1)</code></td>
<td>기존 속성 값</td>
</tr>
<tr>
<td><strong>DOM 클로버링</strong></td>
<td><code>&lt;img id=x&gt;</code></td>
<td>레거시 DOM 조회</td>
</tr>
<tr>
<td><strong>URL 프로토콜</strong></td>
<td><code>javascript:alert(1)</code></td>
<td><code>href</code>/<code>src</code> 계열 속성</td>
</tr>
<tr>
<td><strong>CSP 우회</strong></td>
<td><code>strict-dynamic</code> 스크립트 가젯, nonce 재사용, 허용된 호스트의 JSONP</td>
<td>응답에 우회 가능한 CSP가 있을 때</td>
</tr>
<tr>
<td><strong>mXSS</strong></td>
<td><code>&lt;foreignobject&gt;</code>/DOMPurify 우회</td>
<td>새니타이저가 변형한 DOM</td>
</tr>
<tr>
<td><strong>블라인드</strong></td>
<td><code>&quot;&#39;&gt;&lt;script src=CALLBACK&gt;&lt;/script&gt;</code></td>
<td><code>-b</code>/<code>--blind</code> 또는 <code>--blind-oob</code>가 설정된 경우</td>
</tr>
</tbody>
</table>
<p>대부분의 페이로드 템플릿은 마커(<code>class={CLASS}</code> 또는 <code>id={ID}</code>)를 지니고 있어, 검증 단계에서 DOM 내에서 자신의 요소를 확실하게 식별할 수 있습니다. 길이 제한이 있는 반사에 들어가도록 마커를 뺀 짧은 페이로드도 몇 개 있는데, 이런 페이로드는 파싱된 응답에서 페이로드 자신의 이벤트 핸들러나 <code>&lt;script&gt;</code> 본문을 찾아 검증합니다.</p>
<h2 id="컨텍스트-인식-선택">컨텍스트 인식 선택</h2>
<p>탐색 중 Dalfox는 각 파라미터를 <strong>주입 컨텍스트</strong>, 즉 반사된 값이 도달하는 위치에 따라 분류합니다:</p>
<ul>
<li>HTML 본문 → HTML 태그, mXSS, DOM 클로버링 페이로드 (값이 HTML 주석 안에 들어가면 <code>--&gt;…&lt;!--</code>로 감쌈)</li>
<li>따옴표로 감싼 속성 내부 → 속성 브레이크아웃과 스스로 실행되는 이벤트 핸들러 페이로드. URL 프로토콜 페이로드를 가장 먼저 보냄</li>
<li><code>&lt;script&gt;</code> 내부 → 문자열 구분자 브레이크아웃(<code>'-alert(1)-'</code>, <code>${alert(1)}</code> 등)과 <code>&lt;/script&gt;</code> 태그 브레이크아웃</li>
<li><code>&lt;style&gt;</code> 내부 → <code>&lt;/style&gt;</code> 브레이크아웃 뒤에 HTML 태그</li>
<li>알 수 없음 → HTML, 속성, mXSS, DOM 클로버링, URL 프로토콜 페이로드를 번갈아 섞은 조합</li>
</ul>
<p>덕분에 적중률은 높이면서 요청 수는 적정선으로 유지합니다.</p>
<h2 id="csp-인식-우회-페이로드">CSP 인식 우회 페이로드</h2>
<p>프리플라이트(preflight) 단계가 <code>Content-Security-Policy</code>(또는 <code>…-Report-Only</code>) 헤더나 같은 정책을 담은 <code>&lt;meta http-equiv&gt;</code>를 발견하면, Dalfox는 이를 파싱하여 스크립트 실행 페이로드를 해당 정책의 실제 약점에 맞게 조정합니다. 페이로드는 실제로 악용 가능한 지시어(directive)에 대해서만 생성되므로, CSP가 없는(또는 견고하게 설정된) 대상은 추가 요청을 보지 않습니다.</p>
<table>
<thead>
<tr>
<th>CSP 형태</th>
<th>Dalfox가 내보내는 페이로드</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>unsafe-inline</code> / <code>unsafe-eval</code></td>
<td>직접 인라인 / <code>eval</code> 계열 페이로드</td>
</tr>
<tr>
<td><code>base-uri</code> / <code>object-src</code> 누락</td>
<td><code>&lt;base&gt;</code> 하이재킹 / <code>&lt;object&gt;</code>/<code>&lt;embed&gt;</code> 주입</td>
</tr>
<tr>
<td><code>script-src</code> 내의 <code>data:</code> / <code>blob:</code></td>
<td><code>&lt;script src=data:…&gt;</code> / <code>Blob</code> URL 로더</td>
</tr>
<tr>
<td>화이트리스트에 등록된 CDN 호스트</td>
<td>해당 호스트에 맞는 JSONP / 프레임워크 <strong>스크립트 가젯</strong></td>
</tr>
<tr>
<td><code>strict-dynamic</code></td>
<td>DOM 스크립트 가젯(RequireJS <code>data-main</code>, <code>document.write</code> 자가 전파, AngularJS 부트스트랩)과, nonce가 캡처된 경우 <strong>nonce 재사용</strong></td>
</tr>
</tbody>
</table>
<p>특히 눈여겨볼 형태가 둘 있습니다:</p>
<ul>
<li><strong><code>strict-dynamic</code>.</strong> <code>strict-dynamic</code> 하에서는 브라우저가 호스트 허용 목록을 무시하므로, 평범한 <code>&lt;script src=allowed-host&gt;</code>는 더 이상 로드되지 않습니다. Dalfox는 DOM 스크립트 가젯(이미 신뢰된 스크립트가 공격자 스크립트를 생성하게 만드는 페이로드)으로 전환하고, 정책이 nonce를 고정(pin)하면 <code>&lt;script nonce=…&gt;</code> 재사용 페이로드를 내보냅니다(nonce가 정적이거나 예측 가능하거나 반사될 때 효과적).</li>
<li><strong>Nonce / 해시 고정(pinning).</strong> <code>'nonce-…'</code> 및 <code>'sha256-…'</code> 토큰이 파싱되어 정책 분류에 사용됩니다. <code>strict-dynamic</code>도 없고 가젯 호스트도 없는 순수 무작위 nonce/해시 정책은 *견고함(hardened)*으로 취급됩니다. Dalfox는 그런 정책에 요청을 낭비하지 않습니다.</li>
</ul>
<p>가젯은 공개된 CSP 우회 연구(JSONBee, cure53 H5SC, Google CSP Evaluator)에서 가져왔습니다.</p>
<h2 id="trusted-types-인식">Trusted Types 인식</h2>
<p><a href="https://web.dev/articles/trusted-types">Trusted Types</a>는 견고하게 설계된 앱에서 DOM-XSS를 완화하는 주된 수단입니다. Dalfox의 AST DOM-XSS 분석기는 이를 이해합니다:</p>
<ul>
<li><strong>엄격한(strict)</strong> 정책 콜백(<code>createPolicy('p', {createHTML: s =&gt; DOMPurify.sanitize(s)})</code>)은 다른 새니타이저와 마찬가지로 오염(taint)을 제거하므로, <code>p.createHTML(x)</code>를 거쳐 전달된 값은 더 이상 보고되지 않습니다.</li>
<li><strong>관대한(permissive)</strong> 기본 정책(우회 가능한 것으로 잘 알려진 no-op <code>createPolicy('default', {createHTML: x =&gt; x})</code>)은 보호 수단으로 <em>오인되지 않습니다</em>. 탐지 결과는 유지되고 플래그가 지정됩니다.</li>
<li>응답 CSP가 <code>require-trusted-types-for 'script'</code>를 강제하고, <strong>동시에</strong> 페이지가 엄격한 <code>'default'</code> 정책을 정의하면, 브라우저가 모든 TrustedHTML 싱크를 자동으로 새니타이즈합니다. Dalfox는 이 경우 오탐이 될 탐지 결과를 억제합니다.</li>
</ul>
<p>이 분류기는 의도적으로 보수적입니다. 안전함을 입증할 수 없는 것은 무엇이든 관대한(permissive) 상태로 남으므로, 탐지 결과가 유지됩니다. 억제는 강제(enforcement) 없이는 결코 발동하지 않으므로, 기본 정책을 정의했지만 <code>require-trusted-types-for</code>를 빠뜨린 페이지는 여전히 보고됩니다. 즉, 미탐(false negative)이 도입되지 않습니다.</p>
<h2 id="인코더">인코더</h2>
<p>인코더는 <em>동일한 페이로드</em>를 여러 형태로 변환하여, WAF와 서버 측 필터가 모두 같은 바이트를 보지 않도록 합니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app -e url,html,base64
</code></pre>
<p>사용 가능한 인코더:</p>
<table>
<thead>
<tr>
<th>인코더</th>
<th><code>&lt;</code>를 변환하는 형태</th>
<th>비고</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>none</code></td>
<td><code>&lt;</code> (원시)</td>
<td>인코딩을 끕니다(아래 참고)</td>
</tr>
<tr>
<td><code>url</code></td>
<td><code>%3C</code></td>
<td>1회 URL 인코딩</td>
</tr>
<tr>
<td><code>2url</code></td>
<td><code>%253C</code></td>
<td>2중 URL 인코딩</td>
</tr>
<tr>
<td><code>3url</code></td>
<td><code>%25253C</code></td>
<td>3중</td>
</tr>
<tr>
<td><code>4url</code></td>
<td><code>%2525253C</code></td>
<td>4중</td>
</tr>
<tr>
<td><code>html</code></td>
<td><code>&amp;#x003c;</code></td>
<td>모든 문자를 16진 엔티티로 바꿉니다</td>
</tr>
<tr>
<td><code>htmlpad</code></td>
<td><code>&amp;#x000003c;</code></td>
<td>7자리로 0을 채운 16진 엔티티. 영문자, 숫자, 공백은 그대로 둡니다</td>
</tr>
<tr>
<td><code>base64</code></td>
<td><code>PA==</code></td>
<td>페이로드 전체를 base64로 인코딩</td>
</tr>
<tr>
<td><code>unicode</code></td>
<td><code>＜</code></td>
<td>출력 가능한 ASCII 문자를 전각(fullwidth) 문자(U+FF01–U+FF5E)로 매핑</td>
</tr>
<tr>
<td><code>zwsp</code></td>
<td><code>&lt;</code> + U+200B</td>
<td><code>&lt;</code> <code>&gt;</code> <code>&quot;</code> <code>&#39;</code> <code>(</code> <code>)</code> <code>/</code> <code>;</code> 뒤에 폭 없는 공백(zero-width space) 삽입</td>
</tr>
</tbody>
</table>
<p>기본값: <code>url,html</code>. 원시 페이로드는 항상 함께 전송되므로, 활성화된 인코더마다 기본 페이로드당 변형이 하나씩 늘어납니다(기본값이면 페이로드마다 세 가지 형태로 보냅니다). 목록에 <code>none</code>을 추가하면, Dalfox는 원시 페이로드만 보냅니다.</p>
<h2 id="커스텀-페이로드">커스텀 페이로드</h2>
<p>한 줄에 하나씩, 직접 만든 목록을 제공합니다. 빈 줄과 <code>#</code>으로 시작하는 줄은 건너뜁니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --custom-payload mypayloads.txt
</code></pre>
<p>로컬 내장 라이브러리 대신 커스텀 파일을 사용합니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --custom-payload mypayloads.txt --only-custom-payload
</code></pre>
<p><code>--custom-payload</code> 없이 <code>--only-custom-payload</code>만 주면 거부됩니다. 파일이 없거나 쓸 수 있는 줄이 하나도 없을 때도 마찬가지이며, <code>--only-custom-payload</code> 없이 쓸 때는 경고만 하고 내장 페이로드로 스캔합니다. 커스텀 파일이 로컬 반사 및 DOM 검사의 기본 페이로드가 됩니다. 적응형 합성과 CSP/기술 공유 페이로드는 추가하지 않습니다. 인코더와 WAF 변형은 커스텀 항목에서 파생되며, 명시적으로 요청한 <code>--remote-payloads</code>는 계속 사용됩니다.</p>
<h2 id="원격-페이로드-소스">원격 페이로드 소스</h2>
<p>커뮤니티 워드리스트를 필요할 때 가져옵니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --remote-payloads portswigger,payloadbox
</code></pre>
<p>지원되는 소스: <code>portswigger</code>, <code>payloadbox</code>. 실행마다 한 번 가져오며, <code>--proxy</code>와 <code>--timeout</code>을 준수합니다.</p>
<h2 id="페이로드-확인하기">페이로드 확인하기</h2>
<p>스캔을 실행하지 않고 페이로드 계열을 출력합니다. 각 셀렉터의 설명은 <a href="https://dalfox.hahwul.com/ko/reference/cli/">CLI 레퍼런스</a>에 있습니다. <code>portswigger</code>와 <code>payloadbox</code>는 원격 목록을 가져오고, 나머지는 내장 목록입니다:</p>
<pre><code class="language-bash hljs">dalfox payload javascript      <span class="hljs-comment"># alert(1), alert`1`, prompt(1), ...</span>
dalfox payload event-handlers  <span class="hljs-comment"># onerror, onmouseover, ...</span>
dalfox payload useful-tags     <span class="hljs-comment"># svg, img, script, ...</span>
dalfox payload uri-scheme      <span class="hljs-comment"># javascript:, data:</span>
dalfox payload special-chars   <span class="hljs-comment"># &lt; &gt; &quot; &#39; ` ( ) ... 및 인코딩된 변형</span>
dalfox payload functions       <span class="hljs-comment"># 확인 가능한 싱크: alert(1), window[&#39;alert&#39;](1), ...</span>
dalfox payload awesome-alert   <span class="hljs-comment"># PoC alert: alert(document.domain), alert(document.cookie)</span>
dalfox payload dom-clobbering  <span class="hljs-comment"># DOM 클로버링 벡터</span>
dalfox payload mxss            <span class="hljs-comment"># mutation-XSS / 새니타이저 우회 페이로드</span>
dalfox payload blind           <span class="hljs-comment"># blind-XSS 스켈레톤 ({} = 콜백 URL)</span>
dalfox payload portswigger     <span class="hljs-comment"># 원격 목록을 가져와 출력</span>
dalfox payload payloadbox      <span class="hljs-comment"># 원격 목록을 가져와 출력</span>
dalfox payload all             <span class="hljs-comment"># 모든 로컬 셀렉터를 &quot;# name&quot; 헤더로 묶어 출력</span>
</code></pre>
<p>모든 셀렉터는 한 줄에 하나씩 출력하므로 일반적인 셸 도구와 조합할 수 있습니다:</p>
<pre><code class="language-bash hljs">dalfox payload functions <span class="hljs-punctuation">|</span> grep -i prompt
dalfox payload special-chars <span class="hljs-punctuation">|</span> wc -l
</code></pre>
<p><code>--json</code>을 붙이면 JSON 배열로 출력합니다(<code>dalfox payload all --json</code>은 모든 로컬 그룹을 하나의 배열로 합치고, 셀렉터 없이 <code>dalfox payload --json</code>을 실행하면 셀렉터별 개수를 출력합니다).</p>
<p><code>special-chars</code> 그룹은 수동 반사 테스트에 유용합니다. 각 바이트를 하나씩 주입해 어떤 문자가
그대로 반사되는지, 어떤 문자가 HTML/URL 인코딩되어 돌아오는지, 어떤 문자가 제거되는지 확인할 수
있습니다. <code>functions</code>와 <code>awesome-alert</code>는 <em>눈으로</em> 실행을 확인하고 호스트/오리진을 표시하도록
선별되어 있어, 스크린샷 한 장으로 영향을 증명할 수 있습니다.</p>
<h2 id="alert-커스터마이징">&quot;alert&quot; 커스터마이징</h2>
<p>전형적인 <code>alert(1)</code>은 요란할 수 있습니다. 이를 교체하면 곳곳에서 대화 상자를 띄우지 않고도 영향(impact)을 입증할 수 있습니다:</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># alert(document.domain): 값이 JavaScript 표현식으로 남음</span>
dalfox scan https://target.app --custom-alert-value document.domain

<span class="hljs-comment"># alert(&#39;dalfox&#39;): 값이 문자열 리터럴이 됨</span>
dalfox scan https://target.app --custom-alert-value dalfox --custom-alert-type str
</code></pre>
<ul>
<li><code>--custom-alert-value</code>: <code>alert(1)</code> / <code>prompt(1)</code> / <code>confirm(1)</code> 호출(백틱 형태 포함)의 <code>1</code>을 이 값으로 바꿉니다. 기본값 <code>1</code>. 주입 컨텍스트가 파악된 파라미터의 주요 반사 페이로드에만 적용되고, DOM 검증용 페이로드와 생성된 페이로드는 <code>alert(1)</code>을 그대로 쓰므로 보고된 PoC에 <code>alert(1)</code>이 나올 수 있습니다.</li>
<li><code>--custom-alert-type</code>: <code>none</code>(기본값)은 값을 그대로 넣으므로 <code>document.domain</code>이 표현식으로 남고, <code>str</code>은 값을 작은따옴표로 감싸 문자열 리터럴로 만듭니다.</li>
</ul>
<h2 id="blind-xss">Blind XSS</h2>
<p>Blind XSS는 나중에, 직접 볼 수 없는 컨텍스트(관리자 패널, 지원 담당자의 대시보드)에서 발동합니다. 대역 외(out-of-band) 리스너가 필요합니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app -b https://your-callback.interact.sh
</code></pre>
<p>커스텀 블라인드 템플릿:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app <span class="hljs-string">\
</span>  -b https://your-callback.example <span class="hljs-string">\
</span>  --custom-blind-xss-payload blind-templates.txt
<span class="hljs-comment"># 각 줄에는 {callback}이 있어야 함(콜백 URL로 치환됨)</span>
</code></pre>
<p><code>{callback}</code>이 들어 있는 줄만 사용되며, 나머지 줄은 경고와 함께 건너뜁니다. <code>#</code> 주석과 빈 줄은 무시합니다. 리터럴 <code>{}</code>는 그대로 두므로 템플릿에 <code>()=&gt;{}</code> 같은 JavaScript를 넣을 수 있습니다. 그래서 <code>dalfox payload blind</code>가 출력하는 <code>{}</code> 스켈레톤을 여기에 쓰려면 <code>{}</code>를 <code>{callback}</code>으로 바꿔야 합니다. 쓸 수 있는 줄이 하나도 없으면 내장 템플릿으로 대체합니다.</p>
<p>직접 운영하는 콜백 서버가 없다면 <code>--blind-oob</code>가 interactsh에 등록하고 콜백을 직접 폴링하며, 도착한 콜백은 <code>V</code> 탐지 결과가 됩니다. 스캔 모드의 <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#blind-xss">Blind XSS</a>를 참고하세요.</p>
<h2 id="http-파라미터-오염hpp">HTTP 파라미터 오염(HPP)</h2>
<p>일부 필터는 같은 이름의 파라미터 중 하나만 검사합니다. <code>--hpp</code>를 주면 Dalfox는 각 <strong>쿼리</strong> 파라미터의 처음 다섯 개 페이로드를, 파라미터를 중복시킨 채 다시 보냅니다. 페이로드는 마지막 자리, 첫 자리, 양쪽 모두에 넣어 봅니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --hpp
</code></pre>
<p>적중하면 <code>inject_type</code>이 <code>inHTML-HPP</code>인 <code>R</code>로 보고됩니다. 중복 파라미터 처리를 통과했다는 뜻일 뿐 실행 가능한 위치에 도달했다는 증거는 아니므로, 직접 확인하세요.</p>
<h2 id="딥-스캔">딥 스캔</h2>
<p>기본적으로 Dalfox는 검증된 페이로드를 찾으면 해당 파라미터에 대한 테스트를 중단합니다. <code>--deep-scan</code>은 계속 진행하며, 파라미터당 기본 페이로드 3000개라는 내장 상한도 해제합니다(<a href="https://dalfox.hahwul.com/ko/reference/cli/">CLI 레퍼런스</a>의 <code>--max-payloads-per-param</code> 참고):</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app --deep-scan
</code></pre>
<p>연구에는 유용하지만, 프로덕션 파이프라인에서는 더 느립니다.</p>
<h2 id="페이로드-단계-건너뛰기">페이로드 단계 건너뛰기</h2>
<table>
<thead>
<tr>
<th>플래그</th>
<th>효과</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--skip-xss-scanning</code></td>
<td>탐색과 프로빙만 수행; 페이로드 주입 없음</td>
</tr>
<tr>
<td><code>--skip-ast-analysis</code></td>
<td>인라인 스크립트의 AST 기반 DOM-XSS 탐지 건너뛰기 (<code>[A]</code> 결과)</td>
</tr>
</tbody>
</table>
<p><code>--skip-ast-analysis</code>는 <code>source → sink</code> 흐름(예: <code>location.hash</code> → <code>innerHTML</code>)을 <code>[A]</code>(AST 탐지) 결과로 보고하는 정적 DOM-XSS 패스를 제어하며, 파라미터 마이닝과는 독립적입니다. <code>--skip-mining-dom</code>은 이 패스에 영향을 주지 <strong>않습니다</strong>. 패스는 그대로 두고 결과에서만 숨기려면 <code>--only-poc v,r</code>을 사용하세요. <code>[A]</code>가 실제로 무엇을 증명하는지, 그리고 순수 클라이언트 사이드 DOM-XSS가 왜 <code>[V]</code>에 도달하지 못하는지는 <a href="https://dalfox.hahwul.com/ko/guide/detection-model/">탐지 모델</a> 문서에서 다룹니다.</p>
<h2 id="다음-단계">다음 단계</h2>
<ul>
<li><a href="https://dalfox.hahwul.com/ko/guide/waf-bypass/">WAF 우회</a>와 함께 쓰면 필터를 비껴가도록 페이로드를 다듬을 수 있습니다.</li>
<li>탐지 결과를 내보내려면 <a href="https://dalfox.hahwul.com/ko/guide/output/">출력과 리포트</a>를 참고하세요.</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>스캔 모드</title>
      <link>https://dalfox.hahwul.com/ko/guide/scanning-modes/</link>
      <guid>https://dalfox.hahwul.com/ko/guide/scanning-modes/</guid>
      <description>URL, 파일, 파이프라인, raw HTTP, HAR, 저장형·블라인드 XSS, REST, MCP 중 Dalfox 스캔 모드를 선택합니다.</description>
      <content:encoded><![CDATA[<p>Dalfox는 여러 형태의 대상을 받아들입니다. 모든 모드는 동일한 탐색, 페이로드, 검증 엔진을 공유하며, URL을 입력하는 방식과 결과가 어디로 가는지만 다릅니다.</p>
<p>내부적으로는 실제 작업을 하는 네 개의 서브커맨드가 있습니다: <code>scan</code>(스캐너), <code>server</code>(장시간 유지되는 REST API), <code>payload</code>(페이로드 유틸리티), <code>mcp</code>(Model Context Protocol stdio 서버). 여기에 셸 자동완성 스크립트를 출력하는 <code>completion</code>이 더해집니다. 아래에서 &quot;URL / File / Pipe / Raw HTTP / HAR / SXSS&quot;로 표시된 것은 모두 <code>scan</code> 서브커맨드가 <code>--input-type</code>으로 처리하는 <em>입력 형태</em>이지, 독립적인 서브커맨드가 아닙니다.</p>
<blockquote>
<p>팬아웃 입력 형태(<code>file</code>, <code>pipe</code>, <code>raw-http</code>, <code>har</code>)는 <code>scan</code> 전용입니다. 각각 하나의 입력을 여러 대상으로 확장합니다. <code>server</code>와 <code>mcp</code> 인터페이스는 호출당 단일 대상을 다룹니다. 하나의 URL에 명시적인 메서드/헤더/쿠키/본문(HAR 항목 하나가 담는 것과 동일한 충실도)을 더해서 받으므로, 캡처한 세션을 재생하려면 요청마다 한 번씩 호출하면 됩니다.</p>
</blockquote>
<h2 id="auto-기본값">Auto (기본값)</h2>
<p>Dalfox에 URL만 넘겨주세요. 나머지는 알아서 판단합니다.</p>
<pre><code class="language-bash hljs">dalfox <span class="hljs-string">&#39;https://target.app/search?q=test&#39;</span>
</code></pre>
<p>내부적으로는 <code>scan</code> 서브커맨드를 <code>--input-type auto</code>로 실행합니다. 인자가 URL인지, URL 목록 파일인지, raw HTTP 요청 파일인지, HAR 파일인지, <code>stdin</code>으로 들어오는 스트림인지는 알아서 판별합니다.</p>
<p>서브커맨드를 생략한 형태는 대상과 전역 플래그(<code>--config</code>, <code>--debug</code>, <code>--no-color</code>, <code>-S</code>)만 받습니다. 스캔 플래그를 쓰려면 서브커맨드를 명시해야 합니다. <code>dalfox https://target.app -p q</code>가 아니라 <code>dalfox scan https://target.app -p q</code>입니다.</p>
<h2 id="url-모드">URL 모드</h2>
<p>URL 파싱을 강제합니다(거의 필요하지 않지만 스크립트에서 유용합니다):</p>
<pre><code class="language-bash hljs">dalfox scan --input-type url https://target.app
</code></pre>
<h2 id="file-모드">File 모드</h2>
<p>URL 목록을 한 줄에 하나씩 스캔합니다:</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># urls.txt</span>
<span class="hljs-comment"># https://target.app/search?q=1</span>
<span class="hljs-comment"># https://target.app/profile?id=2</span>
dalfox scan urls.txt
<span class="hljs-comment"># 또는 명시적으로:</span>
dalfox scan --input-type file urls.txt
</code></pre>
<p>주석(<code>#</code>)과 빈 줄은 무시됩니다. 각 URL은 전체 파이프라인을 거칩니다.</p>
<p>스캔할 수 없는 줄 — <code>mailto:</code>, <code>javascript:</code>, <code>tel:</code>, <code>ftp://</code> URL, 잘린 조각처럼 <code>gau</code> / <code>katana</code> / <code>waybackurls</code> 덤프에 흔히 섞이는 것들 — 은 치명적 오류가 아니라 <strong>경고와 함께 건너뜁니다</strong>. 5만 줄짜리 목록에 이상한 줄 하나가 있다고 해서 스캔 전체를 잃지 않습니다. 건너뛴 개수는 <code>meta.targets_unparsable</code>에 담기므로, 일부를 버린 목록이 전체 커버리지처럼 읽히는 일은 없습니다. 목록의 <em>모든</em> 줄이 파싱되지 않으면 그때는 오류입니다.</p>
<p>파일로 지정했는데 실제로는 없는 경로(<code>dalfox scan ./urls.txt</code>, <code>-i har capture.har</code>)는 &quot;파일 없음&quot;으로 보고됩니다. 조용히 호스트명으로 재해석하지 않습니다 — 예전에는 DNS 실패 하나만 남기고 겉보기에는 깨끗한 종료 코드 0을 돌려줬습니다.</p>
<h2 id="pipe-모드">Pipe 모드</h2>
<p><code>stdin</code>에서 읽습니다. 정찰 도구를 체이닝할 때 흔히 쓰이는 방식입니다:</p>
<pre><code class="language-bash hljs">cat urls.txt <span class="hljs-punctuation">|</span> dalfox scan
waybackurls example.com <span class="hljs-punctuation">|</span> gf xss <span class="hljs-punctuation">|</span> dalfox scan
hakrawler -url https://target.app <span class="hljs-punctuation">|</span> dalfox scan
</code></pre>
<p>Dalfox는 입력을 버퍼링하고 중복을 제거한 뒤 모든 줄을 대상으로 스캔합니다.</p>
<h3 id="명령줄-대상과-함께-파이프하기">명령줄 대상과 함께 파이프하기</h3>
<p>대상을 인자로 주면서 동시에 파이프로도 넘기면 두 목록이 병합됩니다:</p>
<pre><code class="language-bash hljs">cat urls.txt <span class="hljs-punctuation">|</span> dalfox scan https://target.app/one
<span class="hljs-comment"># [info] Merged 12 target(s) from stdin and 1 target(s) from arguments</span>
</code></pre>
<p>이 경우 명령줄 대상만으로도 이미 스캔이 가능하므로, Dalfox는 <code>stdin</code>의 첫 바이트를 약 500ms만 기다립니다. 래퍼나 CI 잡, 잡 러너가 열어둔 채 아무것도 쓰지 않는 파이프는 실행을 막지 않고 경고와 함께 건너뜁니다. 일단 스트림이 데이터를 보내기 시작하면 끝까지 읽으므로, 길거나 천천히 쓰이는 목록이 잘리는 일은 없습니다.</p>
<p>이 동작을 조정하려면 <code>DALFOX_STDIN_WAIT_MS</code>로 대기 시간을 늘리거나 <code>0</code>으로 병합을 끌 수 있습니다(<a href="https://dalfox.hahwul.com/ko/reference/environment/">환경 변수</a> 참고). <code>stdin</code>이 곧 입력이며 얼마가 걸리든 기다려야 한다면 <code>--input-type pipe</code>를 사용하세요.</p>
<h3 id="거의-같은-url-묶기">거의 같은 URL 묶기</h3>
<p>기본값(<code>--dedup-urls exact</code>)에서 Dalfox는 완전히 같은 대상만 버립니다. 쿼리 값까지 포함한 전체 URL과 메서드가 모두 일치해야 합니다. 그런데 <code>gau</code> / <code>katana</code> / <code>waybackurls</code> 결과는 그런 모양이 아닙니다. 보통은 엔드포인트 몇 개에 값만 수천 개가 붙어 있고, 결국 <code>?id=1</code> … <code>?id=9999</code>가 주입 지점 하나를 9999번 풀스캔하게 됩니다.</p>
<p><code>--dedup-urls signature</code>는 이것을 하나로 묶습니다. 키는 메서드, 스킴, 호스트, 포트, 경로, 그리고 <em>정렬된 파라미터 이름 집합</em>입니다. 쿼리는 물론 본문(form, JSON, multipart) 파라미터도 같은 자격으로 포함됩니다. 값은 키에서 제외되므로 값만 다른 URL 무리는 대상 하나로 묶입니다. 무엇을 버렸는지는 로그로 남고, 그 개수는 스캔 메타데이터(<code>dedup_mode</code>, <code>targets_deduplicated</code>)에도 실리므로 축소된 실행이 목록 전체를 커버한 것처럼 보이지 않습니다.</p>
<p>대표로 남는 것은 목록에서 가장 먼저 나온 URL입니다. 단, 값이 비어 있는 앞쪽 URL보다 모든 파라미터에 값이 채워진 뒤쪽 URL이 우선합니다. 정찰 결과에는 <code>?id=42</code>보다 <code>?id=</code>가 먼저 오는 경우가 많은데, 값이 빈 URL은 404가 되기 쉬워서 그것이 대표가 되면 무리 전체를 깨끗한 것으로 잘못 보고하게 되기 때문입니다. 스코프 필터(<code>--include-url</code>, <code>--exclude-url</code>, <code>--out-of-scope</code>)는 중복 제거보다 <em>먼저</em> 적용되므로, 필터가 항상 먼저 구성원을 걸러낼 기회를 갖습니다.</p>
<pre><code class="language-bash hljs">gau target.app <span class="hljs-punctuation">|</span> dalfox scan --dedup-urls signature
<span class="hljs-comment"># INF dedup (signature): 8214 duplicate target(s) collapsed, 37 remaining — dropped e.g. …</span>
</code></pre>
<p><strong>요청 절약보다 커버리지 쪽이 더 큰 이득입니다.</strong> <code>--max-targets-per-host</code>는 호스트당 대상을 기본 100개로 제한하는데, 수집 도구의 덤프는 정렬된 상태로 나오기 때문에 한 엔드포인트에서 수집된 수천 개의 값이 연속으로 붙어 있고, 다른 엔드포인트에 닿기도 전에 예산을 다 써 버립니다. 5200개 URL 목록(<code>/html?q=N</code> 5000개 뒤에 서로 다른 <code>/users/N</code> 200개)으로 측정한 결과입니다:</p>
<table>
<thead>
<tr>
<th>모드</th>
<th>대상 100개 예산을 쓴 곳</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>exact</code> (기본값)</td>
<td><code>/html</code> 변형 100개 — <code>/users/N</code> 200개는 아예 스캔되지 않음</td>
</tr>
<tr>
<td><code>signature</code></td>
<td><code>/html</code> 1개 + 서로 다른 <code>/users/N</code> 99개</td>
</tr>
</tbody>
</table>
<p>잘림 자체는 어느 쪽이든 보고됩니다(<code>TRUNCATED_PER_HOST_CAP</code>). 다만 예산을 <em>서로 다른</em> 엔드포인트에 쓰는 것은 <code>signature</code>뿐입니다. 둘 다 원한다면 <code>--max-targets-per-host</code>도 함께 올리세요.</p>
<p>언제 안전할까요? 입력이 어디로 흘러가는지를 파라미터 <em>이름</em>이 결정하는 경우, 즉 대부분의 경우입니다. 반대로 값이 코드 경로를 고르는 경우에는 <strong>안전하지 않습니다.</strong> 같은 경로에서 다른 핸들러로 분기하는 <code>action=</code> / <code>mode=</code> / <code>template=</code> 같은 판별자, 라우팅 토큰, 렌더링 템플릿을 바꾸는 로케일 값이 그렇습니다. 이런 곳에서는 한 분기만 스캔하고 전체를 보고하게 되므로, <code>signature</code>는 옵트인으로 남겨 두었습니다.</p>
<p>모든 줄을 입력 그대로 스캔해야 하는 드문 경우에는 <code>--dedup-urls off</code>로 중복 제거를 완전히 끌 수 있습니다. 다만 대상별 리포팅은 URL을 키로 삼으므로, 같은 URL이 여러 번 나와도 <code>target_summary</code> 항목은 하나로 합쳐집니다.</p>
<h2 id="중단된-스캔-이어하기">중단된 스캔 이어하기</h2>
<p>Ctrl-C는 스캔을 깔끔하게 멈추고 그때까지 찾은 것도 정상적으로 출력합니다. 하지만 그것만으로는 부분 리포트 하나가 남을 뿐입니다. <strong>어떤 대상까지 끝났는지가 어디에도 기록되지 않으므로</strong>, 5만 URL 목록을 80%에서 멈췄다면 다시 돌릴 때 그 80%를 처음부터 훑습니다. 크래시, 끊어진 SSH 세션, 공용 서버의 OOM도 마찬가지입니다.</p>
<p><code>--state-file</code>은 옵트인입니다(지정하지 않으면 아무것도 기록하지 않습니다). 각 대상이 종료 상태에 도달할 때마다 기록해 두고, 다음 실행에서 끝난 것들을 건너뜁니다:</p>
<pre><code class="language-bash hljs">dalfox scan --input-type file urls.txt --state-file scan.state
<span class="hljs-comment"># ^C</span>
<span class="hljs-comment"># [!] Ctrl-C received — stopping in-flight tasks (press again to force exit)</span>

dalfox scan --input-type file urls.txt --state-file scan.state
<span class="hljs-comment"># INF resume: 6042 target(s) already completed per scan.state, 1958 left to scan</span>
</code></pre>
<p><strong>건너뛰는 것은 완료된 대상뿐입니다.</strong> 어디까지 검사됐는지 알 수 없는 것은 전부 다시 스캔합니다:</p>
<table>
<thead>
<tr>
<th>기록된 상태</th>
<th>언제</th>
<th>다음 실행</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>completed</code></td>
<td>세션이 살아 있는 상태로 끝까지 스캔됨</td>
<td>건너뜀</td>
</tr>
<tr>
<td><code>cancelled</code></td>
<td>Ctrl-C, <code>--scan-timeout</code> 만료, <code>--limit</code> 도달로 인한 중단, 스캔 도중 세션 끊김, 심각한 전송 손실(<code>meta.incomplete</code>)</td>
<td>재시도</td>
</tr>
<tr>
<td><code>error</code></td>
<td>프리플라이트에서 제외됨(도달 불가, content-type 불일치, <code>--max-targets-per-host</code> 상한), 또는 스캔 워커 크래시</td>
<td>재시도</td>
</tr>
</tbody>
</table>
<p>대상의 이어하기 식별자는 URL, 메서드, 그리고 요청에 실리는 데이터(본문, 헤더, 쿠키, user-agent)의 해시입니다. raw HTTP·HAR 입력에 캡처된 값뿐 아니라 <code>-H</code>, <code>--cookies</code>, <code>--user-agent</code>로 준 값도 포함되므로, 캡처가 바뀌면 다시 스캔합니다. <strong>실행 전체에 적용되는 자격증명 값은 제외됩니다</strong>: <code>--cookies</code>와 <code>--cookie-from-raw</code>의 모든 쿠키 값, 그리고 <code>-H</code>로 준 <code>Authorization</code>, <code>Proxy-Authorization</code>, <code>Cookie</code>, <code>X-Api-Key</code> / <code>*-Api-Key</code>, <code>X-Auth-Token</code> / <code>*-Token</code>, <code>X-CSRF-Token</code> / <code>X-XSRF-Token</code>, <code>*-Session-Id</code> / <code>X-Session-Token</code>, <code>X-Access-Key</code>, <code>X-JWT-Assertion</code>의 값입니다. 이 플래그들은 모든 대상에 적용되고 다시 로그인할 때 갱신하는 값이므로, 세션을 교체해도 다시 스캔하지 않고 이어서 진행합니다. 헤더나 쿠키를 추가·삭제하면 여전히 다시 스캔합니다. 반면 raw HTTP·HAR 캡처 <strong>안에</strong> 들어 있는 자격증명은 식별자에 포함됩니다. <code>Authorization</code>만 다른 두 캡처(테넌트 A와 B)는 서로 다른 요청이므로 따로 기록됩니다. 같은 플래그로 다른 <em>계정</em>을 넘겨도 이어서 진행되므로, 계정마다 별도의 <code>--state-file</code>을 쓰세요. 파일에는 해시만 저장되고 값 자체는 기록되지 않습니다. 같은 플래그로 실행하면 식별자도 같으므로, 하나의 state 파일로 <code>--input-type file</code> 한 번짜리 실행뿐 아니라 URL 하나씩 도는 셸 루프도 그대로 커버할 수 있습니다.</p>
<p><strong>설정이 바뀌면 처음부터 다시 시작합니다.</strong> 파일 헤더에는 스캔에 영향을 주는 설정의 해시가 들어 있습니다. 해시가 맞지 않으면 기록된 대상들은 이번 실행과 다른 설정에서 검사된 것이므로, Dalfox는 기존 파일을 <code>scan.state.bak</code>으로 옮기고 새 파일로 시작한 뒤 전부 다시 스캔합니다:</p>
<pre><code>Warning: scan configuration changed since 'scan.state' was written (recorded a5f8…, now 6447…) — starting fresh (previous state kept at 'scan.state.bak')
</code></pre>
<p>덮어쓰지 않고 옮겨 두는 이유는, 그 파일이 실제로 수행한 작업의 기록이기 때문입니다. 초기화 때문에 완료 기록 4만 건이 사라지는 쪽이 중복 스캔보다 훨씬 나쁩니다. 어떤 경우에도 기존 파일을 그 자리에서 덮어쓰거나 지우지 않습니다 — 해당 경로에 있는 파일이 Dalfox state 파일이 <strong>아니면</strong>(예: 대상 목록 파일을 오타로 지정한 경우) 아예 거부하고 멈춥니다.</p>
<p>출력·속도 관련 플래그는 의도적으로 이 해시에서 빠져 있습니다 — <code>--format</code>, <code>--output</code>, <code>--poc-type</code>, <code>--include-request</code> / <code>--include-response</code> / <code>--include-all</code>, <code>--silence</code>, <code>--no-color</code>, <code>--stream-findings</code>, <code>--only-poc</code>, <code>--baseline</code> / <code>--baseline-mode</code>, <code>--timeout</code>, <code>--scan-timeout</code>, <code>--delay</code>, <code>--rate-limit</code>, <code>--retries</code>, <code>--retry-delay</code>, <code>--workers</code>, <code>--max-concurrent-targets</code>, 그리고 대상 목록과 <code>--input-type</code> 자체입니다. 중단된 스캔을 이어가면서 타임아웃을 늘리거나 속도를 낮추는 것은 자연스러운 대응이고, 이미 완료된 대상이 무엇으로 검사됐는지는 그것들로 바뀌지 않기 때문입니다. 반대로 페이로드·탐색·커버리지·인증을 바꾸는 것은 파일을 무효화합니다 — <code>--deep-scan</code>, <code>--encoders</code>, <code>--custom-payload</code>, 마이닝/탐색 토글, WAF 옵션, <code>--limit</code>, <code>--headers</code> / <code>--cookies</code>의 헤더·쿠키 <em>이름</em>(과 자격증명이 아닌 헤더 값) 등이 여기에 해당합니다. 실행 전체에 적용되는 자격증명 값과 <code>--cookie-from-raw</code> 경로는 위에서 설명한 대로 해시에 들어가지 않습니다.</p>
<p>해시에는 Dalfox의 메이저 버전과 그 밖의 모든 스캔 옵션도 들어갑니다. 따라서 스캔 플래그가 추가된 버전으로 업그레이드하면 모든 state 파일이 한 번 처음부터 시작합니다. 조용히 건너뛰는 쪽이 아니라 중복 스캔 쪽으로 기우는 설계입니다.</p>
<p>파일은 append-only JSONL입니다. 헤더 한 줄 뒤에 대상당 한 줄이 붙습니다. 강제 종료로 잘릴 수 있는 것은 마지막 줄 하나뿐이고, 그 줄은 읽을 때 건너뛰되 앞의 온전한 기록은 모두 그대로 유효합니다. 지난 실행과 결과가 같은 대상은 다시 기록하지 않으므로, 계속 죽어 있는 호스트 때문에 파일이 실행마다 커지지 않습니다.</p>
<p><code>--dry-run</code>과 <code>--only-discovery</code>는 파일을 <strong>읽기 전용</strong>으로 엽니다. 계획에는 이어하기가 반영되지만 공격 페이로드를 보내지도, 무엇을 완료하지도 않으므로 파일을 만들거나 덧붙이거나 옮기지 않습니다 — <code>--dry-run</code>으로 <code>--deep-scan</code> 비용을 가늠해 보다가 진행 상황을 날릴 일이 없습니다. 이 필터가 적용되는 모든 출력(스캔, dry-run, only-discovery, Markdown)에는 state 파일 경로와 건너뛴 대상 수가 담긴 <code>resumed</code> 블록이 들어갑니다. 이어서 돌린 실행의 짧은 리포트를 입력 목록 전체에 대한 커버리지로 오해할 일이 없도록 하기 위해서입니다.</p>
<p>CLI 전용입니다. <code>dalfox server</code>와 MCP는 잡 단위로 각자의 수명 주기를 가지며, 이전 프로세스의 작업을 이어받지 않습니다.</p>
<h2 id="raw-http-모드">Raw HTTP 모드</h2>
<p>Burp, Caido, ZAP에서 캡처한 요청을 파일로 저장한 뒤 Dalfox에 넘겨줍니다:</p>
<pre><code class="language-bash hljs">dalfox scan --input-type raw-http request.txt
</code></pre>
<p>이 파일은 표준 raw HTTP 요청(메서드 + 경로 + 헤더 + 빈 줄 + 본문)입니다. Dalfox는 헤더, 쿠키, 본문 파라미터를 보존합니다. 꼭 필요한 헤더만 버리는데, URL에서 가져오는 <code>Host</code>, 주입할 본문마다 다시 계산하는 <code>Content-Length</code> / <code>Transfer-Encoding</code>, hop-by-hop 헤더와 <code>Accept-Encoding</code>, 그리고 HTTP/1.1 클라이언트가 보낼 수 없는 줄입니다. copy-as-cURL이나 HTTP/2 캡처에 흔히 섞이는 HTTP/2 가상 헤더(<code>:authority</code>, <code>:scheme</code>)는 호스트와 스킴을 정하는 데만 쓰고 전송하지 않습니다.</p>
<p>요청 라인에 경로만 있으면(<code>GET /search HTTP/1.1</code>) HTTP와 HTTPS 중 무엇으로 보낸 요청인지 알 수 없으므로, Dalfox는 캡처 내용에서 스킴을 정합니다. <code>:scheme</code> 가상 헤더가 가장 우선하고, 그다음 다른 HTTP/2 흔적(<code>HTTP/2</code> / <code>HTTP/3</code> 요청 라인이나 <code>:authority</code> 가상 헤더)이 있으면 HTTPS, 그다음 <code>Host</code>가 <code>:443</code>으로 끝나면 HTTPS이며, 그 밖에는 <code>http://</code>로 스캔합니다. 요청 라인에 절대 URL(<code>GET https://app/search HTTP/1.1</code>)이 있으면 그대로 사용합니다.</p>
<p>실시간 프록시 워크플로, 그중에서도 Caido Active Workflows는 전용 <a href="https://dalfox.hahwul.com/ko/integrations/caido/">Caido 연동 가이드</a>를 참고하세요. 정확한 셸 패턴, If/Else 노드에서의 Caido 불리언 함정, 결과를 자동으로 Findings로 전환하는 방법을 다룹니다.</p>
<h2 id="har-모드">HAR 모드</h2>
<p><a href="http://www.softwareishard.com/blog/har-12-spec/">HAR</a>(HTTP Archive) 익스포트는 브라우저 DevTools와 가로채기 프록시(Burp, Caido, ZAP, Charles, mitmproxy)가 만들어 내는 JSON 캡처입니다. 파일을 통째로 넘겨주면 그 안의 모든 요청을 각각의 URL, 메서드, 헤더, 쿠키, 본문을 보존한 채로 스캔합니다:</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># 파일 내용으로 자동 판별:</span>
dalfox scan capture.har
<span class="hljs-comment"># 또는 명시적으로:</span>
dalfox scan --input-type har capture.har
<span class="hljs-comment"># 또는 다른 도구에서 파이프로:</span>
mitmdump -nr flows -w /dev/stdout --set <span class="hljs-variable">hardump</span><span class="hljs-operator">=</span>- <span class="hljs-punctuation">|</span> dalfox scan -i har
</code></pre>
<p>HAR을 단순 URL 목록으로 평탄화하는 것(메서드, 헤더, 쿠키, 본문을 버리는 방식)과 달리, HAR 모드는 캡처된 각 요청의 전체 형태를 유지하므로 JSON 본문을 가진 POST나 인증된 세션도 충실하게 재생됩니다. 각 <code>log.entries[].request</code>는 하나의 대상이 되며, 다른 모든 모드와 동일한 스코프 필터를 거칩니다. 중복은 URL + 메서드로 판단하고 본문은 비교하지 않으므로, 같은 URL에 본문만 다른 POST 두 개는 첫 번째 것 하나로 합쳐집니다. 모든 항목을 남기려면 <code>--dedup-urls off</code>를 쓰세요. <code>http(s)</code>가 아닌 항목(<code>data:</code>, <code>blob:</code>, WebSocket, 브라우저 확장 URL)은 자동으로 건너뜁니다.</p>
<p>CLI 요청 플래그는 HAR과 raw HTTP 모두에서 그 위에 그대로 적용됩니다. <code>-X</code>, <code>-d</code>, <code>--user-agent</code>는 캡처된 각 요청의 메서드, 본문, User-Agent를 대체하고, <code>-H</code>와 <code>--cookies</code>는 요청에 추가됩니다(예: <code>-H &quot;Authorization: Bearer …&quot;</code>는 모든 항목에 붙습니다). <code>-H</code>는 캡처에 이미 있는 헤더를 대체하지 않고 두 값을 모두 보내므로, 오래된 헤더는 덮어쓰려 하지 말고 캡처에서 지우세요. 예외는 두 가지입니다. <code>-H 'User-Agent: …'</code>는 <code>--user-agent</code>처럼 캡처된 User-Agent를 대체하고, <code>-H 'Cookie: …'</code>는 캡처된 쿠키(와 <code>--cookies</code>로 준 쿠키)를 통째로 대체합니다. 캡처된 쿠키에 하나를 더하려면 <code>--cookies</code>만 쓰세요. 이 플래그들이 없으면 각 요청은 캡처된 형태를 그대로 유지합니다. <code>--include-url</code> / <code>--out-of-scope</code>는 대상 집합을 좁힙니다.</p>
<h2 id="저장형-xss-모드-sxss">저장형 XSS 모드 (SXSS)</h2>
<p>전형적인 &quot;폼 A에 주입하면 페이지 B에 페이로드가 나타난다&quot; 패턴을 테스트합니다:</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app/post-comment <span class="hljs-string">\
</span>  --sxss <span class="hljs-string">\
</span>  --sxss-url https://target.app/comments
</code></pre>
<p>Dalfox는 첫 번째 URL에 주입한 다음, 두 번째 URL을 가져와 페이로드가 도달했는지 확인합니다. 폼을 찾은 페이지, 폼의 <code>action</code>(같은 오리진일 때만), 주입 대상 자체도 함께 확인하므로 <code>--sxss-url</code>은 생략할 수 있습니다. 저장형 싱크는 즉시 응답에 값을 되돌려주지 않으므로, <code>--sxss</code>는 발견한 폼의 필드와, <code>-p</code>를 지정하지 않았다면 요청의 쿼리·<code>-d</code> 본문 파라미터까지 탐색 중 반사되지 않더라도 테스트합니다. 전체 흐름은 <a href="https://dalfox.hahwul.com/ko/guide/stored-xss/">저장형 XSS 가이드</a>를 참고하세요.</p>
<h2 id="blind-xss">Blind XSS</h2>
<p>나중에, 보이지 않는 곳(관리자 패널, 고객 지원 대시보드)에서 실행되는 페이로드라면 콜백 페이로드를 주입하세요:</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># 직접 운영하는 리스너: 호출은 그쪽으로 가고, Dalfox는 아무것도 기록하지 않습니다</span>
dalfox scan <span class="hljs-string">&#39;https://target.app/?q=1&#39;</span> -b https://your-callback.example

<span class="hljs-comment"># Dalfox가 관리하는 interactsh 세션: 콜백이 탐지 결과로 돌아옵니다</span>
dalfox scan <span class="hljs-string">&#39;https://target.app/?q=1&#39;</span> --blind-oob
</code></pre>
<p><code>--blind-oob</code>는 interactsh 서버(공개 메시, 또는 <code>--blind-oob=oast.fun</code>처럼 지정한 서버)에 등록하고, 페이로드마다 콜백 호스트를 새로 발급하며, 스캔이 끝난 뒤에도 <code>--blind-oob-wait</code>초(기본값 <code>30</code>) 동안 폴링을 계속합니다. 도착한 콜백은 <code>detection_method: oob</code>인 <code>V</code> 탐지 결과가 됩니다. Blind 페이로드는 저장되는 공격 트래픽이므로 <code>--dry-run</code>, <code>--only-discovery</code>, <code>--skip-xss-scanning</code>에서는 보내지 않습니다. 템플릿과 커스텀 페이로드는 <a href="https://dalfox.hahwul.com/ko/guide/payloads/#blind-xss">페이로드와 인코딩</a>에서 다룹니다.</p>
<h2 id="세션-모니터링">세션 모니터링</h2>
<p>정적 자격증명(<code>--cookies</code>, <code>-H 'Cookie: …'</code>, <code>-H 'Authorization: …'</code>, <code>--cookie-from-raw</code>, 또는 캡처된 raw HTTP / HAR 요청 안의 값)은 모든 요청에 그대로 붙을 뿐 유효성을 다시 확인하지 않습니다. 긴 스캔 도중 한 시간쯤 지나 세션이 만료되면 이후 모든 요청은 로그인 페이지를 받고, 아무것도 반사되지 않으며, Dalfox는 빈 리포트와 함께 <code>0</code>으로 종료합니다 — 진짜로 안전한 대상과 구분할 수 없습니다.</p>
<p>세션 모니터링이 이 간극을 메웁니다. 프리플라이트 단계에서 인증된 랜딩 응답의 지문(상태 코드, 리다이렉트 후 최종 도착지, 로그인 폼이 이미 있었는지)을 <strong>추가 요청 없이</strong> 확보합니다 — 프리플라이트가 이미 가져온 본문을 재사용합니다. 이후 각 대상의 주입 단계가 끝난 직후에 다시 프로브하고, 기준 지문이 30초 이상 지났다면 디스패치 경계에서도 한 번 더 프로브해 비교합니다. (짧거나 단일 대상 실행에서는 사후 프로브만 발생합니다. 방금 잡은 기준을 다시 확인해 봐야 알 수 있는 것이 없기 때문입니다.)</p>
<pre><code class="language-bash hljs"><span class="hljs-comment"># 별도 설정이 필요 없습니다. 자격증명이 있으면 자동으로 켜집니다.</span>
dalfox scan <span class="hljs-string">&#39;https://app.example.com/dashboard?q=1&#39;</span> --cookies <span class="hljs-string">&quot;sid=</span><span class="hljs-variable">$SESSION</span><span class="hljs-string">&quot;</span>
</code></pre>
<p>다음 중 하나라도 감지되면 세션이 끊어진 것으로 보고합니다:</p>
<table>
<thead>
<tr>
<th>신호</th>
<th>예시</th>
</tr>
</thead>
<tbody>
<tr>
<td>상태 코드가 <code>401</code> / <code>403</code>으로 바뀜</td>
<td>앱이 쿠키를 거부하기 시작</td>
</tr>
<tr>
<td>요청이 로그인 형태의 URL로 도착</td>
<td><code>302 → /users/sign_in</code></td>
</tr>
<tr>
<td>기준에는 없던 비밀번호 입력 필드가 등장</td>
<td>앱이 로그인 화면을 인라인으로 렌더링</td>
</tr>
</tbody>
</table>
<p><code>403</code>은 오리진이나 WAF가 스캐너를 차단하기 시작했을 때도 반환됩니다. 해당 대상에서 이미 WAF가 탐지된 경우 <code>403</code> 신호는 아예 무시합니다 — 만료된 세션보다 차단이 더 그럴듯한 설명이고, 이를 로그아웃으로 판단하면 WAF 규칙 하나 때문에 호스트 전체를 중단하게 되기 때문입니다. WAF 뒤에 있는 오리진에서 <code>403</code>을 만료 신호로 쓰려면 <code>--session-check</code>를 사용하세요.</p>
<h3 id="정확하게-지정하기">정확하게 지정하기</h3>
<p>휴리스틱은 의도적으로 좁게 잡혀 있습니다 — 기본 동작이 중단(abort)이므로 오탐 하나가 스캔 전체를 날립니다. 인증된 응답이 어떤 모습인지 정확히 안다면 그것을 지정하세요. 그러면 휴리스틱은 물러납니다:</p>
<pre><code class="language-bash hljs">dalfox scan <span class="hljs-string">&#39;https://app.example.com/dashboard?q=1&#39;</span> <span class="hljs-string">\
</span>  --cookies <span class="hljs-string">&quot;sid=</span><span class="hljs-variable">$SESSION</span><span class="hljs-string">&quot;</span> <span class="hljs-string">\
</span>  --session-check <span class="hljs-string">&#39;Signed in as&#39;</span> <span class="hljs-string">\
</span>  --session-check-url https://app.example.com/api/me
</code></pre>
<p>예외는 하나입니다. 프로브 응답이 불완전하게 돌아와서(<code>206</code>이거나 본문이 잘림) 기준 응답에서 마커가 있던 위치에 미치지 못한 경우에는 마커가 없다는 사실만으로 아무것도 증명할 수 없으므로, 휴리스틱을 대체 수단으로 참고합니다.</p>
<p>스캔 대상이 무겁거나, 페이지네이션이 있거나, 그 자체로 공개 페이지라면 <code>--session-check-url</code>로 가벼운 인증 엔드포인트를 따로 지정하세요. 이 경우 기준 지문도 해당 엔드포인트에서 잡습니다(플래그를 지정했을 때만, 대상당 프리플라이트 요청 1건 추가). 덕분에 <code>/auth/session</code>처럼 로그인 형태의 프로브 경로도 스캔 대상이 아니라 자기 자신의 인증된 응답과 비교됩니다.</p>
<h3 id="세션이-끊어졌을-때">세션이 끊어졌을 때</h3>
<p><code>--on-session-loss abort</code>(기본값)는 해당 대상을 중단하고 같은 호스트의 남은 대상도 건너뜁니다. 로그인 페이지를 상대로 요청 예산을 계속 쓰는 것은 아무 이득이 없기 때문입니다. <code>--on-session-loss continue</code>는 휴리스틱이 오탐하는 대상을 위해 스캔을 계속합니다.</p>
<p>어느 쪽이든 실행 결과는 이 사실을 숨기지 않습니다:</p>
<ul>
<li><strong>stderr</strong>에 <code>SESSION LOST</code> 한 줄 — 구조화된 stdout은 그대로 파싱 가능</li>
<li>해당 대상은 <code>incomplete</code>(또는 <code>skipped</code>) 상태에 <code>error_code: SESSION_LOST</code>, 그리고 어떤 신호가 감지됐는지 <code>error_message</code>에 기록</li>
<li><a href="https://dalfox.hahwul.com/ko/guide/output/#%EC%8A%A4%EC%BA%94-%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%94%EB%B2%A8%EB%A1%9C%ED%94%84">스캔 메타데이터 엔벨로프</a>의 <code>meta.incomplete: true</code></li>
<li>탐지 결과가 없는 실행에 한해 <code>abort</code>에서 종료 코드 <code>2</code> — 따라서 로그아웃된 상태에서 <code>dalfox scan … &amp;&amp; echo &quot;no XSS found&quot;</code>가 그 줄을 출력할 수 없습니다. 탐지 결과가 <em>있었다면</em> 여전히 <code>1</code>로 종료합니다. 발견된 취약점은 어쨌든 실재하고, 불완전하다는 사실은 <code>meta.incomplete</code>가 전달하기 때문입니다. <code>continue</code>는 종료 코드를 전혀 바꾸지 않습니다</li>
</ul>
<p><strong>프리플라이트</strong> 응답부터 이미 미인증으로 보이거나, <code>--session-check</code> 마커가 기준 응답에 애초에 없었던 경우(오타이거나 다른 페이지에 있는 마커)도 표시합니다. 두 경우 모두 단순 로그가 아니라 <code>SESSION_LOST</code>로 보고합니다: 그런 기준에서는 이후 어떤 프로브도 <em>변화</em>를 감지할 수 없으므로, 만료된 자격증명이 조용하고 완벽하게 &quot;깨끗한&quot; 실행을 만들어 내기 때문입니다. 대상 스캔 자체는 그대로 진행되며, 결과를 정직하게 만드는 것은 이 표시와 종료 코드입니다.</p>
<p>&quot;이미 미인증으로 보인다&quot;는 것은 로그인 페이지 그 자체, 즉 <code>401</code>이나 페이지에 직접 렌더링된 비밀번호 입력란, <code>/login</code>, <code>/signin</code>, <code>/users/sign_in</code>으로의 리다이렉트를 뜻합니다. 단지 인증<em>처럼 생긴</em> 경로로 리다이렉트되는 것만으로는 부족합니다. 인증된 홈을 <code>/auth/home</code>이나 <code>/sso/dashboard</code>에서 서빙하는 앱이 많고, 그걸 끊어진 세션으로 판정하면 멀쩡한 세션의 스캔을 실패시키게 됩니다. 이 경우 Dalfox는 대신 <code>SESSION?</code> 참고 메시지를 출력합니다 — 눈에는 보이지만 <code>SESSION_LOST</code> 항목도, <code>meta.incomplete</code>도, 종료 코드 변화도 없습니다. 앱이 여기 해당하는데도 검사를 정확히 하고 싶다면 <code>--session-check</code>로 확정하세요.</p>
<p>요청에 쿠키도, <code>Cookie</code> / <code>Authorization</code> 헤더도 없고 <code>--session-check</code> 계열 플래그도 지정하지 않으면 모니터링은 꺼져 있으며 비용도 들지 않습니다. 자동 로그인은 범위 밖입니다. 이 기능은 감지만 담당합니다.</p>
<p><code>dalfox server</code>와 MCP 잡도 같은 감지 규칙을 쓰며, 잡의 기준 응답과 스캔이 끝난 뒤 한 번 확인합니다. 세션이 끊어졌다면 잡은 <code>SESSION_LOST:</code> 메시지와 함께 <code>error</code>로 끝납니다. 잡에는 <code>--session-check</code>에 해당하는 옵션이 없으므로 휴리스틱만 사용합니다.</p>
<h2 id="서버-모드">서버 모드</h2>
<p>Dalfox를 장시간 유지되는 HTTP 서비스로 실행합니다. REST로 스캔을 제출하고, 결과를 폴링하고, 실행 중인 작업을 취소합니다:</p>
<pre><code class="language-bash hljs">dalfox server --port <span class="hljs-number">6664</span> --api-key <span class="hljs-string">&quot;</span><span class="hljs-variable">$DALFOX_API_KEY</span><span class="hljs-string">&quot;</span>
</code></pre>
<p>엔드포인트와 요청 형태는 <a href="https://dalfox.hahwul.com/ko/integrations/server/">REST API 서버</a>를 참고하세요.</p>
<h2 id="mcp-모드">MCP 모드</h2>
<p>Dalfox를 <a href="https://modelcontextprotocol.io">Model Context Protocol</a> 서버로 노출하여 AI 에이전트와 IDE(예: Claude)가 스캔을 구동할 수 있게 합니다:</p>
<pre><code class="language-bash hljs">dalfox mcp
</code></pre>
<p>도구(<code>scan_with_dalfox</code>, <code>get_results_dalfox</code>, <code>list_scans_dalfox</code>, <code>cancel_scan_dalfox</code>, <code>delete_scan_dalfox</code>, <code>preflight_dalfox</code>)는 <a href="https://dalfox.hahwul.com/ko/integrations/mcp/">MCP 서버</a>에 설명되어 있습니다.</p>
<h2 id="payload-모드-유틸리티">Payload 모드 (유틸리티)</h2>
<p>스캔 모드는 아니지만 곁에 두면 유용합니다. 스캔을 실행하지 않고 페이로드를 출력하거나 가져옵니다.</p>
<pre><code class="language-bash hljs">dalfox payload event-handlers    <span class="hljs-comment"># DOM 이벤트 핸들러 목록</span>
dalfox payload useful-tags       <span class="hljs-comment"># 유용한 HTML 태그 목록</span>
dalfox payload portswigger       <span class="hljs-comment"># PortSwigger XSS 치트시트 가져오기</span>
dalfox payload payloadbox        <span class="hljs-comment"># PayloadBox XSS 목록 가져오기</span>
dalfox payload uri-scheme        <span class="hljs-comment"># javascript:/data: 페이로드 출력</span>
</code></pre>
<h2 id="모드-선택하기">모드 선택하기</h2>
<table>
<thead>
<tr>
<th>원하는 작업</th>
<th>사용할 모드</th>
</tr>
</thead>
<tbody>
<tr>
<td>URL 하나 테스트</td>
<td>Auto / URL</td>
</tr>
<tr>
<td>크롤러가 만든 목록 스캔</td>
<td>File이나 Pipe</td>
</tr>
<tr>
<td>특정 요청 재생</td>
<td>Raw HTTP</td>
</tr>
<tr>
<td>캡처한 세션 전체 재생(프록시/DevTools 익스포트)</td>
<td>HAR</td>
</tr>
<tr>
<td>다른 페이지에 기록하는 폼 테스트</td>
<td>SXSS</td>
</tr>
<tr>
<td>나중에 보이지 않는 곳에서 실행되는 페이로드 잡기</td>
<td>Blind (<code>-b</code> / <code>--blind-oob</code>)</td>
</tr>
<tr>
<td>대시보드나 CI에서 여러 스캔 실행</td>
<td>Server</td>
</tr>
<tr>
<td>AI 에이전트가 스캔을 구동하게 하기</td>
<td>MCP</td>
</tr>
<tr>
<td>공격 없이 Dalfox가 보낼 것만 확인</td>
<td>Payload 유틸리티(페이로드 목록)나 <code>--dry-run</code>(대상별 계획과 요청 수 추정)</td>
</tr>
</tbody>
</table>
]]></content:encoded>
    </item>
    <item>
      <title>저장형 XSS</title>
      <link>https://dalfox.hahwul.com/ko/guide/stored-xss/</link>
      <guid>https://dalfox.hahwul.com/ko/guide/stored-xss/</guid>
      <description>Dalfox로 한 URL에 저장형 XSS를 주입하고 다른 조회 URL에서 실행 여부를 검증합니다.</description>
      <content:encoded><![CDATA[<p>저장형 XSS는 서버에 남아 있습니다. 한 번 제출하면(댓글, 프로필 필드, 채팅 메시지) 누군가 해당 페이지를 볼 때마다 실행됩니다. Dalfox에는 이 패턴을 위한 전용 모드가 있습니다.</p>
<h2 id="기본-흐름">기본 흐름</h2>
<pre><code class="language-bash hljs">dalfox scan https://target.app/post-comment <span class="hljs-string">\
</span>  --sxss <span class="hljs-string">\
</span>  --sxss-url https://target.app/comments
</code></pre>
<p>Dalfox는 다음을 수행합니다.</p>
<ol>
<li>각 페이로드를 첫 번째 URL(<code>post-comment</code>)에 <strong>주입</strong>합니다.</li>
<li>두 번째 URL(<code>comments</code>)을 GET으로 <strong>조회</strong>합니다(<code>--sxss-method</code>로 바꿀 수 있음).</li>
<li>페이로드가 조회 응답에 반사되는지, 그리고 실제 DOM 요소를 생성했는지 <strong>검증</strong>합니다.</li>
</ol>
<p>조회 페이지에 다시 나타난 페이로드는 <code>R</code>로, 그곳에서 실제 DOM 요소까지 만든 페이로드는 <code>V</code>로 보고됩니다. 저장형 탐지 결과의 <code>inject_type</code>에는 <code>sxss-</code> 접두어가 붙으므로(<code>sxss-inHTML</code>), 리포트에서 반사형 결과와 구분됩니다.</p>
<p>저장형 모드는 철저히 순차적으로 동작합니다. 쓰기 순서와 조회 재시도가 이를 전제로 하므로 파라미터도 하나씩, 요청도 하나씩 보냅니다. <code>--workers</code>를 늘려도 빨라지지 않습니다.</p>
<p>쓰기 엔드포인트가 제출한 값을 돌려줄 필요는 없습니다. 제출 응답이 &quot;saved&quot;만 돌려줘도
괜찮으며, Dalfox는 그 응답으로 싱크가 어떤 문자를 거르는지 판단하지 않습니다.</p>
<p>모든 필드가 같은 페이로드를 받고 저장된 값은 페이지에 계속 남으므로, Dalfox는 각
파라미터의 페이로드를 보내기 전에 두 가지를 합니다.</p>
<ul>
<li><strong>스냅샷.</strong> 조회 페이지를 한 번씩 가져옵니다. 페이로드는 그 파라미터의 주입으로
스냅샷보다 <em>더 많이</em> 나타날 때에만 해당 파라미터에 귀속되므로, 다른 필드가 먼저
저장한 사본이 잘못 귀속되지 않습니다. 파라미터마다 조회 URL당 GET이 한 번 더
듭니다.</li>
<li><strong>저장 프로브.</strong> 마커를 주입합니다. 긴 마커를 먼저, 짧은 값만 보관하는 싱크를
위해 짧은 마커를 이어서 시도합니다. 둘 다 조회 페이지(또는 쓰기 응답)의 마커 수를
늘리지 못하면 이 필드는 저장되지 않는 것으로 보고 페이로드를 건너뜁니다.</li>
</ul>
<p>프로브는 저장이 <em>언제</em> 보이는지도 확인합니다. 바로 보이면 Dalfox는 페이로드마다
조회를 한 번만 합니다. 지연 후에만 보이는 쓰기 지연(write-behind) 저장이라면
페이로드마다 조회 재시도를 모두 유지합니다. 대상이 그보다도 느리게 저장한다면
<code>--sxss-retries</code>(기본값 <code>3</code>, 최대 <code>20</code>)를 높이세요. <em>n</em>번째 재시도는 500 ms × <em>n</em>만큼
기다리며, 한 번의 대기는 최대 5 s입니다. <code>--deep-scan</code>을 주면 저장 프로브를 건너뛰고
모든 필드에 모든 페이로드를 보냅니다.</p>
<h2 id="조회-url-선택">조회 URL 선택</h2>
<p>저장된 값을 <strong>읽어 오는</strong> 페이지를 선택하세요. 예시:</p>
<table>
<thead>
<tr>
<th>주입 URL</th>
<th>조회 URL</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>POST /comments/new</code></td>
<td><code>GET /post/123/comments</code></td>
</tr>
<tr>
<td><code>PATCH /profile</code></td>
<td><code>GET /u/myself</code></td>
</tr>
<tr>
<td><code>POST /support/ticket</code></td>
<td><code>GET /admin/tickets</code> (관리자 권한이 있는 경우)</td>
</tr>
</tbody>
</table>
<p>Dalfox는 후보 조회 페이지를 모두 읽으며, 순서는 <code>--sxss-url</code>(지정한 경우), 폼이 발견된 페이지, 폼의 <code>action</code> 엔드포인트(대상과 같은 오리진이거나 같은 호스트를 HTTPS로 올린 경우에만), 주입 대상 자신입니다. 중복된 URL은 한 번만 가져옵니다. <code>--sxss-url</code>이 없으면 뒤의 세 곳이 전부이므로, 저장된 값이 그 밖의 곳에 렌더링된다면 <code>--sxss-url</code>을 직접 지정하세요. <code>--sxss-url</code>은 <code>--sxss</code> 없이는 아무 효과가 없으며, 단독으로 주면 Dalfox가 경고합니다.</p>
<h2 id="조회-메서드">조회 메서드</h2>
<pre><code class="language-bash hljs">dalfox scan https://target.app/form --sxss <span class="hljs-string">\
</span>  --sxss-url https://target.app/list <span class="hljs-string">\
</span>  --sxss-method GET
</code></pre>
<p><code>GET</code>이 기본값입니다. 조회 엔드포인트가 필요로 한다면 <code>POST</code>나 다른 메서드를 사용하세요.</p>
<h2 id="인증">인증</h2>
<p>저장형 XSS에는 세션이 두 개 필요한 경우가 많습니다. 하나는 쓰는 쪽(사용자), 다른 하나는 읽는 쪽(관리자)입니다. 조회 GET이 작성해 둔 내용을 볼 수 있을 만큼 충분한 접근 권한을 주는 헤더/쿠키를 사용하세요.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app/profile <span class="hljs-string">\
</span>  --sxss --sxss-url https://target.app/admin/users <span class="hljs-string">\
</span>  -H <span class="hljs-string">&quot;Cookie: admin_session=abc; role=admin&quot;</span>
</code></pre>
<h2 id="블라인드-저장형">블라인드 + 저장형</h2>
<p>조회 페이지가 로그인 뒤에 있는데 그 로그인 정보가 없다면, 블라인드 XSS로 전환하세요. 페이로드는 관리자의 브라우저에서 실행되고, 콜백 서버가 이를 기록합니다.</p>
<pre><code class="language-bash hljs">dalfox scan https://target.app/support/ticket <span class="hljs-string">\
</span>  -b https://callback.interact.sh
</code></pre>
<p>여전히 누군가가 페이지를 볼 때까지 기다려야 하며, 콜백이 그 시점을 알려 줍니다. <code>-b</code> 대신 <code>--blind-oob</code>를 쓰면 Dalfox가 interactsh 세션을 직접 폴링해 콜백을 <code>V</code> 탐지 결과로 보고합니다. 다만 <code>--blind-oob-wait</code>이 끝나기 전에 도착한 콜백만 해당하며, 몇 시간 뒤의 조회는 직접 운영하는 리스너에만 닿습니다. <a href="https://dalfox.hahwul.com/ko/guide/scanning-modes/#blind-xss">Blind XSS</a>를 참고하세요.</p>
<h2 id="팁">팁</h2>
<ul>
<li><strong>범위를 좁게 설정하세요.</strong> <code>-p</code>를 사용해 조회 URL에서 렌더링된다고 알고 있는 필드를 지정하세요. 그러면 Dalfox가 모든 쿠키를 테스트하지 않습니다.</li>
<li><strong>새니타이즈 후 렌더링에 주의하세요.</strong> 저장형 XSS는 쓰기 시점의 HTML 새니타이저는 통과하지만 읽기 시점의 두 번째 새니타이즈에서 깨지는 경우가 많습니다. Dalfox의 mXSS 페이로드는 이에 맞춰 조정되어 있습니다.</li>
<li><strong>속도를 늦추세요.</strong> 일부 앱은 쓰기를 디바운스하거나 배치 처리합니다. 작은 <code>--delay</code> 값을 주면 조회가 페이로드를 보는 데 도움이 됩니다.</li>
</ul>
<h2 id="다음-단계">다음 단계</h2>
<ul>
<li>주입되는 페이로드를 조정하려면 <a href="https://dalfox.hahwul.com/ko/guide/payloads/">페이로드와 인코딩</a>을 참고하세요.</li>
<li>탐지 결과를 전달하려면 <a href="https://dalfox.hahwul.com/ko/guide/output/">출력과 리포트</a>를 참고하세요.</li>
</ul>
]]></content:encoded>
    </item>
  </channel>
</rss>
