sherlock user123 한 줄로 400개가 넘는 플랫폼에 동시에 접속해, 그 아이디가 살아 있는지 몇 초 만에 알려주는 Python CLI 도구입니다. OSINT(공개정보 수집)·펜테스팅·CTF에서 사실상 표준 도구죠. (저장소: sherlock-project/sherlock · ⭐76.6k · Python 97.3% · MIT)Sherlock은 400개 이상의 소셜 미디어 플랫폼에서 사용자명(username)을 동시에 검색하여 계정 존재 여부를 탐지하는 Python CLI 도구입니다. sherlock user123 한 줄이면 Instagram, Twitter, GitHub, Reddit 등 수백 개 사이트에서 해당 유저네임이 사용 중인지 몇 초 만에 알려줍니다.
보통은 사이트마다 검색창에 닉네임을 넣어 일일이 확인해야 합니다. Sherlock은 400여 곳에 같은 질문을 한꺼번에 보내고, "이 닉네임이 여기 등록돼 있나요?"라는 답만 모아 옵니다.
핵심은 사이트마다 다른 '계정 없음' 신호를 어떻게 읽어내느냐입니다. 어떤 곳은 404를 던지고, 어떤 곳은 본문에 "페이지 없음"이라 적습니다. Sherlock은 이 판별 규칙을 코드가 아니라 데이터(JSON)로 들고 있어, 새 사이트를 코드 수정 없이 추가합니다.
Sherlock은 OSINT(Open Source Intelligence, 공개정보 수집) 분야의 사실상 표준 도구입니다. 사이버보안, 펜테스팅, CTF 대회에서 빠지지 않는 필수 도구로 자리잡았고, ⭐76.6k·9k 포크로 Python 보안 도구 중 최상위 인기를 자랑합니다. Docker·pipx·패키지 매니저 등 다양한 설치 방식으로 접근성을 극대화했고, {?} 와일드카드 문법으로 유저네임 변형까지 자동 검색합니다.
| 비교 항목 | Sherlock | Maigret | WhatsMyName |
|---|---|---|---|
| 지원 사이트 수 | 400+ | 3000+ | 500+ |
| 설치 난이도 | 매우 쉬움 (pipx 한 줄) | 보통 | API 의존 |
| 커뮤니티 크기 | ⭐76.6k (압도적) | ⭐12k | ⭐2k |
| 탐지 정확도 | 보수적 (거짓양성 최소화) | 공격적 | 중간 |
| Tor 지원 | ✅ 내장 | ✅ | ❌ |
| 클라우드 실행 | Apify Actor | ❌ | ❌ |
사이트 수는 Maigret보다 적지만, 거짓양성(false positive)을 줄이는 데 집중합니다. exclusion 리스트를 적극 관리하고, WAF(웹 방화벽) 감지 로직까지 내장해 "방화벽에 막힌 것"을 "계정 없음"으로 오판하지 않습니다.
| 기술 | 역할 | 버전 |
|---|---|---|
| Python | 메인 언어 | 3.9+ (권장 3.10~3.13) |
| requests | HTTP 클라이언트 | — |
| requests-futures | 비동기 HTTP (ThreadPoolExecutor 기반) | — |
| PySocks | SOCKS 프록시 지원 (Tor 연동) | — |
| stem | Tor 제어 프로토콜 라이브러리 | — |
| colorama | 터미널 컬러 출력 | — |
| certifi | SSL 인증서 번들 | — |
| 기술 | 역할 |
|---|---|
| pandas | 결과 데이터 구조화 |
| openpyxl | Excel(.xlsx) 출력 |
| csv (stdlib) | CSV 출력 |
| json (stdlib) | 사이트 매니페스트 파싱 |
| argparse (stdlib) | CLI 인자 파싱 |
| 기술 | 역할 |
|---|---|
| Docker | Python 3.12-slim-bullseye 기반 멀티스테이지 빌드 |
| Poetry | 빌드 시스템 (poetry-core.masonry.api) |
| pytest | 테스트 프레임워크 |
| pytest-xdist | 병렬 테스트 실행 |
| tox | 다중 Python 버전 테스트 |
| jsonschema | 사이트 데이터 스키마 검증 |
| Apify | 클라우드 실행 플랫폼 (선택) |
핵심 포인트: pyproject.toml 하나에 빌드·의존성·스크립트·메타데이터를 모두 통합하는 현대적 Python 패키징 방식입니다. setup.py나 setup.cfg 없이 pyproject.toml만으로 패키지를 관리합니다.
# SherlockFuturesSession — requests-futures 확장
class SherlockFuturesSession(FuturesSession):
def request(self, method, url, hooks=None, *args, **kwargs):
start = time.monotonic()
def timing_hook(r, *h_args, **h_kwargs):
r.elapsed = time.monotonic() - start
# 기존 훅 체인에 추가 (덮어쓰지 않음!)
hooks["response"] = hooks.get("response", []) + [timing_hook]
return super().request(method, url, hooks=hooks, *args, **kwargs)
requests-futures는 진짜 asyncio가 아니라 ThreadPoolExecutor(스레드 풀) 기반 비동기입니다. 최대 20개 워커로 400+개 사이트에 동시 요청을 보내죠. 기존 requests API를 그대로 쓰면서 비동기 효과를 얻는 게 장점입니다.계정 존재 여부를 판단하는 4단계 체인입니다.
각 사이트의 errorType 설정에 따라 어떤 단계를 쓸지 결정됩니다. 이것이 데이터 주도 설계(Data-Driven Design)의 핵심 — 탐지 로직을 코드가 아니라 data.json 설정으로 제어합니다.
class QueryNotify: # 추상 베이스
def start(self, message): ...
def update(self, result): ...
def finish(self, message): ...
class QueryNotifyPrint(QueryNotify): # 구체 구현
def update(self, result):
if result.status == QueryStatus.CLAIMED:
print(f"[+] {site_name}: {url}") # 초록색
elif self.print_all:
print(f"[-] {site_name}: Not Found") # 빨간색
이 패턴 덕분에 터미널 출력을 웹소켓 알림·로그 파일 등으로 쉽게 교체할 수 있습니다.
def interpolate_string(input_object, username):
if isinstance(input_object, str):
return input_object.replace("{}", username)
elif isinstance(input_object, dict):
return {k: interpolate_string(v, username) for k, v in input_object.items()}
elif isinstance(input_object, list):
return [interpolate_string(item, username) for item in input_object]
return input_object
중첩된 딕셔너리/리스트 안의 모든 {}를 유저네임으로 치환합니다. JSON 설정 파일에서 동적 값을 주입할 때 매우 유용한 패턴입니다.
| 파일 | 줄 수(추정) | 핵심 역할 |
|---|---|---|
| sherlock.py | ~930 | 전체 흐름: 인자 파싱 → 사이트 로드 → 동시 요청 → 결과 수집 |
| sites.py | ~260 | 데이터 소스 추상화: URL/파일/기본값에서 사이트 정보 로드 |
| result.py | ~90 | 5가지 상태를 가진 Enum + 결과 컨테이너 |
| notify.py | ~280 | 옵저버 패턴으로 결과 출력 분리 |
| data.json | ~103KB | 모든 사이트의 URL 패턴, 탐지 방법, 에러 메시지 등 |
스레드 풀 기반 비동기 HTTP — 동기 방식과 비교하면 차이가 한눈에 보입니다.
# 일반적인 동기 요청 (느림)
for site in sites:
response = requests.get(site.url) # 하나씩 순서대로
# Sherlock의 방식 (빠름)
session = SherlockFuturesSession(max_workers=20)
futures = {}
for site in sites:
futures[site] = session.get(site.url) # 즉시 반환 (Future)
for site, future in futures.items():
response = future.result() # 여기서 대기
탐지 로직을 코드가 아니라 JSON 데이터로 제어합니다.
{
"GitHub": {
"url": "https://www.github.com/{}",
"errorType": "status_code",
"errorCode": 404,
"username_claimed": "torvalds"
},
"Instagram": {
"url": "https://www.instagram.com/{}",
"errorType": "message",
"errorMsg": "Sorry, this page isn't available.",
"username_claimed": "instagram"
}
}
사이트마다 탐지 방식이 다릅니다 — 어떤 곳은 404, 어떤 곳은 본문 메시지. if문으로 하드코딩하면 400개마다 분기가 필요하지만, JSON 매니페스트로 분리하면 코드 한 줄 안 바꾸고 새 사이트를 추가할 수 있습니다.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {
"required": ["url", "urlMain", "errorType", "username_claimed"],
"if": { "properties": { "errorType": { "const": "message" } } },
"then": { "required": ["errorMsg"] }
}
}
errorType이 "message"이면 errorMsg가 필수 — 이런 조건부 검증을 스키마로 표현하고, CI에서 PR마다 자동 검증해 잘못된 데이터 머지를 막습니다.
[build-system]
requires = ["poetry-core>=1.2.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "sherlock-project"
[tool.poetry.dependencies]
python = "^3.9"
[tool.poetry.scripts]
sherlock = "sherlock_project.sherlock:main"
이 한 파일로 빌드 시스템 지정·의존성 관리·CLI 엔트리포인트 등록·메타데이터 관리를 모두 처리합니다.
def pytest_generate_tests(metafunc):
if "chunked_sites" in metafunc.fixturenames:
sites = fetch_local_manifest()
metafunc.parametrize("chunked_sites",
list(sites.items()),
ids=[s for s in sites])
사이트 하나당 테스트 케이스 하나가 자동 생성됩니다. pytest --chunked-sites=GitHub,Instagram처럼 특정 사이트만 골라 테스트할 수도 있습니다.
# Stage 1: pip 업그레이드만 수행
FROM python:3.12-slim-bullseye AS build
RUN pip install --upgrade pip
# Stage 2: 런타임 환경 — pip install로 직접 설치 (Stage 1 파일 복사 없음)
FROM python:3.12-slim-bullseye
RUN pip install sherlock-project==${VERSION_TAG}
WORKDIR /sherlock
ENTRYPOINT ["sherlock"]
Sherlock은 응답 본문에서 Cloudflare·PerimeterX·AWS WAF의 고유 핑거프린트를 검사합니다. WAF에 막힌 결과를 "계정 없음"으로 오판하면 안 되기 때문에, 막혔다는 사실 자체를 별도 상태(WAF)로 분류합니다.
| 항목 | 최소 | 권장 |
|---|---|---|
| Python | 3.9 | 3.12+ |
| RAM | 256MB | 512MB+ |
| 네트워크 | 인터넷 필수 | 안정적인 브로드밴드 |
| 디스크 | ~50MB | ~100MB (Docker 이미지 포함) |
| OS | Linux/macOS/Windows | Linux (Docker 또는 WSL) |
Tor 사용 시: Tor 서비스 설치 필요 (sudo apt install tor).
# 설치
pipx install sherlock-project
# 자신의 유저네임 검색
sherlock 내유저네임 --print-all --csv
# 결과 CSV 분석
python -c "
import pandas as pd
df = pd.read_csv('내유저네임.csv')
print(f'발견: {len(df[df.status == \"Claimed\"])}개')
print(f'미발견: {len(df[df.status == \"Available\"])}개')
"
data.json에 새 사이트를 추가하고 --json local_data.json 옵션으로 로드해 테스트해보세요.
{
"DCInside": {
"url": "https://gallog.dcinside.com/{}",
"urlMain": "https://www.dcinside.com",
"errorType": "status_code",
"errorCode": 404,
"username_claimed": "test",
"tags": ["community"]
}
}
스레드 풀(requests-futures) 대신 진짜 async(aiohttp) 프로토타입을 만들어 성능을 벤치마크해보세요.
import aiohttp, asyncio
async def check_site(session, site_name, url):
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as resp:
return site_name, resp.status, await resp.text()
except Exception as e:
return site_name, 0, str(e)
async def sherlock_async(username, sites):
async with aiohttp.ClientSession() as session:
tasks = [check_site(session, name, url.format(username))
for name, url in sites.items()]
return await asyncio.gather(*tasks)
Flask + SSE(Server-Sent Events)로 검색 결과를 실시간 스트리밍하는 웹 대시보드를 만들어보세요.
from flask import Flask, Response
import json
app = Flask(__name__)
def generate_results(username):
for site_name, result in sherlock(username):
yield f"data: {json.dumps({'site': site_name, 'status': result.status.value})}\n\n"
@app.route('/search/<username>')
def search(username):
return Response(generate_results(username), mimetype='text/event-stream')
Sherlock의 pyproject.toml 구조를 참고해 자신만의 CLI 도구를 PyPI에 배포해보세요.
1. pyproject.toml 작성 (build-system, project, scripts)
2. pipx install . 로 로컬 테스트
3. TestPyPI 배포:
pip install build twine
python -m build
twine upload --repository testpypi dist/*
| 일 | 학습 내용 |
|---|---|
| 1–2 | threading vs multiprocessing vs asyncio 개념 이해 |
| 3–4 | concurrent.futures (ThreadPoolExecutor, ProcessPoolExecutor) 실습 |
| 5 | requests-futures 소스 읽기 — Sherlock이 이걸 어떻게 활용하는지 |
| 6–7 | 미니 프로젝트: 100개 URL 상태 체커 (동기 vs 비동기 비교) |
| 일 | 학습 내용 |
|---|---|
| 1–2 | pyproject.toml 상세 스펙 (PEP 517, 518, 621) |
| 3–4 | Poetry vs Hatch vs PDM 비교, 각각으로 프로젝트 셋업 |
| 5 | pipx로 CLI 도구 배포하는 전체 플로우 |
| 6–7 | TestPyPI에 실제 패키지 배포 |
| 일 | 학습 내용 |
|---|---|
| 1–2 | pytest 고급: fixture scope, parametrize, conftest |
| 3–4 | tox로 다중 Python 버전 테스트 환경 구성 |
| 5–6 | JSON Schema 검증: jsonschema 라이브러리 + CI 통합 |
| 7 | Sherlock 테스트 코드 전체 분석 및 커버리지 측정 |
| 일 | 학습 내용 |
|---|---|
| 1–2 | Docker 멀티스테이지 빌드 패턴 |
| 3–4 | GitHub Actions CI/CD 파이프라인 작성 |
| 5–6 | Apify Actor 등 서버리스 플랫폼에 CLI 도구 배포 |
| 7 | 전체 워크플로우 통합: 코드 → 테스트 → Docker → 배포 |
| 일 | 학습 내용 |
|---|---|
| 1–2 | OSINT 방법론 개요, Tor/프록시 네트워크 이해 |
| 3–4 | WAF 탐지 기법, HTTP 핑거프린팅 |
| 5–6 | 나만의 미니 OSINT 도구 만들기 (예: 도메인 정보 수집기) |
| 7 | 윤리적 해킹/보안 연구 가이드라인 학습 |
| 용어 | 설명 |
|---|---|
| OSINT | Open Source Intelligence. 공개된 정보원에서 유용한 정보를 수집·분석하는 기법 |
| WAF | Web Application Firewall. 웹 앞단에서 악성 트래픽을 차단하는 보안 장치 (Cloudflare, AWS WAF 등) |
| Future | 아직 완료되지 않은 비동기 작업의 결과를 나타내는 객체. future.result()로 결과를 가져옴 |
| ThreadPoolExecutor | 스레드 풀을 관리해 여러 작업을 병렬 실행하는 Python 표준 라이브러리 클래스 |
| requests-futures | requests에 Future 패턴을 적용한 라이브러리. 요청을 보내면 Future를 반환 |
| pyproject.toml | 빌드 시스템·의존성·메타데이터를 통합 관리하는 설정 파일 (PEP 518/621) |
| Poetry | Python 의존성 관리 + 패키지 빌드 도구. pyproject.toml 기반 |
| pipx | Python CLI 도구를 격리된 가상환경에 설치하는 도구. 시스템 Python을 더럽히지 않음 |
| JSON Schema | JSON 데이터의 구조와 유효성을 정의하는 표준. API 문서화·데이터 검증에 사용 |
| Chain-of-Responsibility | 처리 가능한 핸들러 체인을 만들어 첫 번째로 처리 가능한 핸들러가 처리하는 디자인 패턴 |
| 옵저버 패턴 | 객체의 상태 변화를 구독자에게 자동 통지하는 디자인 패턴. 결과 알림에 사용 |
| 멀티스테이지 빌드 | Docker에서 빌드 환경과 런타임을 분리해 최종 이미지 크기를 줄이는 기법 |
| Tor | The Onion Router. 여러 노드를 거쳐 IP를 숨기는 익명 네트워크 |
| SOCKS 프록시 | TCP 수준에서 동작하는 범용 프록시 프로토콜. Tor는 SOCKS5 사용 |
| 핑거프린팅 | 시스템/서비스의 고유 특성을 분석해 종류·버전을 식별하는 기법 |
| 데이터 주도 설계 | 동작을 코드 로직이 아니라 외부 데이터(JSON/YAML 등)로 제어하는 설계 방식 |
| PEP 561 | Python 패키지의 타입 힌트 지원을 선언하는 표준. py.typed 마커 파일 사용 |