REST API 서버
dalfox server는 스캔을 큐에 넣고 비동기로 실행하는 상시 구동 HTTP 서비스를 시작합니다. 스캔을 제출하면 scan_id를 돌려받으며, 원하는 대로 폴링하거나 취소할 수 있습니다.
서버 시작하기
dalfox server
# listens on http://127.0.0.1:6664 by default
자주 쓰는 옵션:
dalfox server \
--port 6664 \
--host 0.0.0.0 \
--api-key "change-me" \
--log-file /var/log/dalfox.log
인증
--api-key를 설정했거나 DALFOX_API_KEY를 export 했다면, 모든 요청에 다음이 들어가야 합니다:
X-API-KEY: change-me
API 키를 설정하지 않으면 서버는 인증 없는 요청도 받습니다. 그럴 때는 127.0.0.1에 바인딩하세요.
브라우저 요청
127.0.0.1 바인딩은 네트워크를 막아 줄 뿐, 브라우저를 막아 주지는 않습니다. 사용자가
방문한 웹페이지가 사용자 본인의 브라우저를 통해 루프백 API를 호출할 수 있기 때문입니다.
이 서버에서는 그 영향이 특히 큽니다 — GET /scan은 쿼리 파라미터만으로 스캔을 시작하고
callback_url은 결과를 임의의 주소로 POST 하므로, 공격자는 응답을 읽지 않고도 결과를
가져갈 수 있습니다.
그래서 서버는 브라우저가 크로스사이트로 표시한 요청을 거부합니다:
--allowed-origins에 없는Origin헤더, 또는Sec-Fetch-Site: cross-site/same-site(브라우저가<img>,<script>를 포함한 모든 서브리소스 로드에 붙이는 헤더).
두 경우 모두 403으로 응답합니다. curl, CLI, 에이전트, CI 작업 같은 비브라우저
클라이언트는 두 헤더를 보내지 않으므로 영향을 받지 않습니다.
Host 헤더도 같은 방식으로 검사하며, 이것이 DNS 리바인딩(공격자가 소유한 호스트명을
사용자 머신으로 재해석시켜 브라우저가 동일 출처로 취급하게 만드는 공격)을 막습니다.
IP 리터럴과 localhost는 항상 허용되고, 그 외 호스트명은 명시해야 합니다:
# 프록시가 공개 호스트명을 dalfox로 전달할 때만 필요합니다
dalfox server --allowed-hosts "dalfox.internal,scan.corp.example"
실제 웹 UI가 API를 호출하게 하려면 해당 출처를 지정하세요. 이것이 게이트를 통과하는 공식적인 방법입니다:
dalfox server --allowed-origins "https://app.example.com"
CORS
dalfox server \
--allowed-origins "https://app.example.com,https://admin.example.com" \
--cors-allow-methods "GET,POST,OPTIONS,DELETE" \
--cors-allow-headers "Content-Type,X-API-KEY,Authorization"
*는 와일드카드로 허용됩니다. 정규식은 regex:^https://.*\.example\.com$ 형태로 지원됩니다.
JSONP
커스텀 헤더를 설정할 수 없는 브라우저 클라이언트를 위해:
dalfox server --jsonp --callback-param-name callback
# then GET /scan?target=...&callback=myFunction
JSONP는 <script src> 로드로 전달되는데, 스크립트 로드에는 검증할 Origin이 없습니다.
따라서 이 플래그를 켜면 위에서 설명한 크로스사이트 게이트가 함께 꺼지고, 임의의 사이트가
이 API로 스캔을 실행하고 결과를 읽을 수 있게 됩니다. --api-key와 함께 쓰거나, 게이트가
유지되는 CORS(--allowed-origins)를 우선 고려하세요. API 키 없이 --jsonp을 켜면 서버가
시작 시 경고를 출력합니다.
엔드포인트
| 메서드 | 경로 | 기능 |
|---|---|---|
POST |
/scan |
새 스캔 제출 (JSON 본문) |
GET |
/scan?target=... |
새 스캔 제출 (쿼리 문자열) |
GET |
/scan/:id |
스캔 상태 및 결과 조회 |
DELETE |
/scan/:id |
큐에 있거나 실행 중인 스캔 취소 |
GET |
/scans |
모든 스캔 목록 조회 (선택적 ?status=) |
GET |
/result/:id |
/scan/:id의 별칭 |
POST |
/preflight |
페이로드를 보내지 않고 파라미터 탐색 |
GET |
/health |
서버 정보 + 기능 목록 |
스캔 제출
curl -X POST http://127.0.0.1:6664/scan \
-H "X-API-KEY: change-me" \
-H "Content-Type: application/json" \
-d '{
"target": "https://target.app?q=test",
"options": {
"worker": 50,
"timeout": 10,
"encoders": ["url", "html"],
"blind": "https://callback.interact.sh"
}
}'
스캔 대상 필드는 target입니다 (MCP scan_with_dalfox 도구 및 응답 페이로드와 동일). 레거시 필드명 url도 별칭으로 계속 받습니다. JSON 본문과 ?target= / ?url= 쿼리 문자열 모두에서 통하므로 기존 클라이언트는 그대로 동작합니다.
응답:
{
"code": 200,
"msg": "queued",
"data": {
"scan_id": "9f2c…",
"target": "https://target.app?q=test"
}
}
상태 폴링
curl -H "X-API-KEY: change-me" http://127.0.0.1:6664/scan/9f2c…
응답 (실행 중):
{
"code": 200,
"msg": "running",
"data": {
"target": "https://target.app?q=test",
"status": "running",
"results": [],
"progress": {
"params_total": 12,
"params_tested": 5,
"requests_sent": 234,
"findings_so_far": 1,
"estimated_completion_pct": 41,
"suggested_poll_interval_ms": 3000
}
}
}
완료되면 status는 done이 되고 results가 채워집니다.
스캔 목록 조회
curl -H "X-API-KEY: change-me" 'http://127.0.0.1:6664/scans?status=running'
스캔 취소
curl -X DELETE -H "X-API-KEY: change-me" http://127.0.0.1:6664/scan/9f2c…
프리플라이트 (공격 없음)
curl -X POST http://127.0.0.1:6664/preflight \
-H "X-API-KEY: change-me" \
-H "Content-Type: application/json" \
-d '{"target":"https://target.app"}'
응답에는 params_discovered, estimated_total_requests와 파라미터 목록이 담겨 있어, 실제 스캔에 들어가기 전에 범위를 정할 수 있습니다.
헬스
curl http://127.0.0.1:6664/health
버전, auth_required, 지원되는 엔드포인트 목록을 반환합니다. 가동 상태 확인에 유용합니다.
ScanOptions 참조 (요청 본문)
{
"target": "https://target.app",
"options": {
"worker": 50,
"delay": 0,
"timeout": 10,
"rate_limit": 0,
"scan_timeout": 0,
"blind": "https://callback.interact.sh",
"method": "POST",
"data": "user=test",
"header": ["Authorization: Bearer token"],
"user_agent": "Custom",
"encoders": ["url", "html"],
"remote_payloads": ["portswigger"],
"remote_wordlists": ["burp"],
"include_request": false,
"include_response": false,
"callback_url": "https://your-webhook.example/dalfox",
"param": ["q", "id:query"],
"proxy": "http://127.0.0.1:8080",
"insecure": true,
"follow_redirects": false,
"skip_mining": false,
"skip_discovery": false,
"deep_scan": false,
"skip_ast_analysis": false,
"analyze_external_js": false,
"detect_outdated_libs": false,
"waf_bypass": "auto",
"skip_waf_probe": false,
"force_waf": "cloudflare",
"waf_evasion": false,
"waf_min_confidence": 0.3,
"max_payloads_per_param": 0
}
}
필드는 CLI 플래그와 대응됩니다. 의미와 기본값은 CLI 참조를 보세요.
detect_outdated_libs는 옵트인 방식입니다 (기본값 false). true로 설정하면
오래되었거나 알려진 취약점이 있는 JS 라이브러리도 정보성 [I] 탐지 결과로
보고합니다 (CWE-1104, 추가 요청 0건). 같은 키를 GET /scan 쿼리 파라미터로도 사용할 수 있습니다.
insecure는 기본값이 true입니다 (CLI 스캐너 기본값과 동일하게 TLS 인증서 검증을
건너뜁니다). 인증서 검증을 강제하려면 "insecure": false (또는 GET /scan에서
?insecure=false)를 보내세요.
analyze_external_js는 옵트인 방식입니다 (기본값 false). true로 설정하면
프리플라이트 시점에 동일 출처의 <script src> 번들을 가져와 DOM XSS를 위한 AST
분석을 수행합니다. 싱크(sink) 로직이 전부 외부 번들에 들어 있는 SPA에 유용합니다.
추가 요청 비용이 들기 때문에 기본적으로 꺼져 있습니다.
rate_limit은 스캔의 초당 아웃바운드 요청 수를 제한합니다 (0 = 무제한, 기본값).
모든 워커 태스크에 걸쳐 적용됩니다. 서버 전역 --rate-limit 플래그는 상한선입니다.
요청은 더 낮은 속도를 지정할 수는 있으나 이를 초과하거나 비활성화할 수는 없습니다.
max_payloads_per_param은 발견된 각 파라미터를 테스트할 페이로드 수의 상한입니다
(기본값 0 = 명시적 상한 없음. 내장 페이로드 안전 상한은 그대로 적용됩니다).
스모크 스캔에는 작은 값(예: 10~50)을 쓰세요. MCP 스캔 도구의 동명 필드와
대응됩니다.
WAF 관련 다섯 개 필드는 CLI의 WAF 플래그와 대응되며 모두 선택 사항입니다. 생략하면
스캐너 기본값이 적용됩니다. waf_bypass는 처리 모드를 고릅니다: "auto"(탐지 후
우회, 기본값), "force"(force_waf를 사용), "off"(탐지만). skip_waf_probe는
(기본값 false) WAF 핑거프린팅 프로브를 아예 건너뜁니다. force_waf는 WAF를
탐지하는 대신 특정 프로필(예: "cloudflare")을 고정합니다. waf_evasion은
(기본값 false) 적응형 우회를 켭니다. waf_min_confidence는 [0.0, 1.0] 범위의
탐지 신뢰도 하한입니다 (기본값 0.3). 이 값보다 낮은 핑거프린트는 버려집니다.
method와 encoders는 CLI가 허용하는 것과 동일한 값 집합으로 검증됩니다.
method는 자동으로 대문자로 바뀌며("post" → "POST"), 지원하지 않는 메서드나
모르는 인코더 이름은 400으로 거부합니다. 잘못된 메서드를 보내거나 인코딩을 건너뛰는
스캔이 조용히 돌아가는 것보다 낫기 때문입니다.
scan_timeout은 스캔 전체의 벽시계 시간 예산(초)입니다 (기본값 0 = 무제한).
요청당 timeout과는 구별됩니다. 예산에 도달하면 스캔이 중단되고, 그때까지 수집한
부분 탐지 결과를 유지하며, scan_timeout을 언급하는 error_message와 함께
cancelled 상태로 끝납니다 (그래서 타임아웃인지 클라이언트가 건 취소인지 구별할 수
있습니다). 서버 전역 --scan-timeout 플래그는 --rate-limit과 마찬가지로 제출된 모든
스캔에 동일하게 상한을 적용합니다.
설정해 둘 만한 서버 플래그
--rate-limit <rps>— 모든 스캔의 아웃바운드 요청 속도를 제한합니다 (대상을 보호).--scan-timeout <secs>— 스캔당 강제 벽시계 시간 예산. 길거나deep_scan인 작업을 제한하여 하나의 대상이 워커를 무한정 점유하지 못하게 합니다.--max-concurrent-scans <n>—n개의 스캔이 큐에 있거나 실행 중이면 새 제출을503으로 거부합니다 (기본값100,0= 무제한). 제출 폭주에 대비해 메모리와 블로킹 풀을 제한합니다.--max-body-bytes <n>—POST /scan및/preflight의 명시적 요청 본문 상한 (기본값1048576= 1 MiB). 크기를 초과하는 본문은413을 받습니다.--max-retained-scans <n>— 메모리에 보관하는 종료된 스캔 수 상한 (기본값1000,0= 무제한).--max-concurrent-scans는 활성 스캔만 세기 때문에, 이 상한이 없으면 짧은 스캔이 몰릴 때 모든 결과가(include_response를 켰다면 응답 본문까지) 1시간 보존 TTL이 만료될 때까지 유지됩니다. 상한에 도달하면 가장 오래된 종료 스캔부터 제거되며, 큐에 있거나 실행 중인 스캔은 절대 제거되지 않습니다.--allowed-hosts <names>— 요청Host헤더에서 추가로 허용할 호스트명. 바인딩 호스트,localhost, 모든 IP 리터럴은 기본으로 허용됩니다. 리버스 프록시가 공개 호스트명을 전달할 때 필요합니다. 브라우저 요청 참고.
작업(job) 수명 주기
queued → running → done
↘ error
↘ cancelled
종료 상태(done, error, cancelled)는 고정되어 변하지 않습니다.
연결할 수 없는 대상(DNS 실패, 연결 거부, TLS 오류, 타임아웃)은
target unreachable: connection failed (CONNECTION_FAILED)라는 error_message와
함께 error로 종료됩니다 — 탐지 결과가 0건인 done이 아니므로 "스캔했으나 아무것도
찾지 못함"과 "호스트에 도달하지 못함"을 구별할 수 있습니다. 스캔을 실행하지 않고
도달 가능성만 확인하려면 먼저 POST /preflight를 쓰세요. url은 http://나
https://로 시작해야 하며, 그 외 스킴은 400으로 거부됩니다 (/preflight와 동일).
끊어진 세션도 같은 규칙을 따릅니다. 스캔 요청이 자격증명(cookie, 또는 header의
Cookie / Authorization 항목)을 담고 있으면, Dalfox는 스캔 전에 인증된 응답의 지문을
잡아 두고 스캔이 끝날 때 다시 확인합니다. 그 사이에 세션이 만료됐다면 (이후 모든 요청이
로그인 페이지를 받고 아무것도 반사되지 않는 상태) 탐지 결과 0건의 done이 아니라
SESSION_LOST:로 시작하는 error_message와 함께 error로 종료됩니다. 부분 탐지
결과는 그대로 유지됩니다. 자격증명이 없는 스캔에서는 모니터링이 꺼져 있으며 비용도
들지 않습니다.
systemd에서 실행하기
# /etc/systemd/system/dalfox.service
[Unit]
Description=Dalfox scanner service
After=network.target
[Service]
ExecStart=/usr/local/bin/dalfox server --port 6664 --host 127.0.0.1 --log-file /var/log/dalfox.log
Environment=DALFOX_API_KEY=change-me
Restart=on-failure
User=dalfox
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now dalfox
보안 참고 사항
- 로컬호스트에 바인딩하세요. 원격 접근이 꼭 필요할 때만 예외입니다. 다만 이것은
네트워크를 막는 조치일 뿐 보안 경계가 아닙니다. 사용자가 방문한 웹페이지는 본인의
브라우저를 통해 루프백 API에 도달할 수 있으며, 이를 막는 것이
브라우저 요청의 크로스사이트·
Host게이트입니다. - 원격 바인딩에는 항상
--api-key를 설정하세요. - API 키를 로그에 남기지 마세요. Dalfox는 키를 기록하지 않지만, 리버스 프록시는 기록할 수 있습니다.
- 네트워크로 노출한다면 TLS 뒤에 두세요 (nginx, Caddy, Traefik).
callback_url과 스캔 대상은 서버 측 요청입니다. Dalfox는 URL 스캐너입니다. 제출한 대상이 무엇이든 접속하며, 완료 시 결과 JSON을callback_url로 POST 합니다.http(s)스킴만 접속하지만 호스트는 필터링되지 않습니다 — 루프백, 링크 로컬 (예:169.254.169.254의 클라우드 메타데이터), 사설 주소가 모두 도달 가능합니다. 인증 없는 바인딩에서는 스캔을 제출할 수 있는 누구에게나 이것이 서버 측 요청 위조 + 데이터 유출 프리미티브가 되므로, 신뢰할 수 없는 호출자에게 API를 노출할 때는--api-key를 설정하고 아웃바운드 트래픽을 제한하세요.--jsonp는GET엔드포인트를<script>로 교차 출처에서 읽을 수 있게 만들며, 이는 CORS 허용 목록의 적용을 받지 않습니다. 또한 스크립트 로드에는 검증할Origin이 없으므로 크로스사이트 게이트도 함께 꺼집니다. 의도한 경우에만 켜고,--api-key와 함께 쓰세요.--scan-timeout으로 스캔 실행 시간을 제한하세요. 요청당timeout은 단일 HTTP 요청만 제한합니다. 파라미터와 페이로드가 많은 스캔(또는deep_scan)은 여전히 오랫동안 실행될 수 있습니다.--scan-timeout <secs>를 설정하여 제출된 모든 스캔에 강제 벽시계 시간 예산을 두면, 느린 대상 하나가 워커를 무한정 묶어 둘 수 없게 됩니다.