VirusTotal이 무료 API를 제공 — 파일 및 URL을 70개 이상의 안티바이러스 엔진으로 스캔

발행: (2026년 3월 25일 AM 08:36 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

Alex Spinov

VirusTotal은 파일과 URL을 동시에 70개 이상의 안티바이러스 엔진으로 스캔합니다. API는 하루 최대 500회 요청까지 무료입니다.

한 번의 스캔. 70개의 판정. 무료.

이야기

친구가 GitHub에서 도구를 다운로드했습니다. 백신 프로그램은 깨끗하다고 표시했지만, 한 엔진만으로는 충분하지 않다는 불안감이 있었습니다.

그들은 파일을 직접 VirusTotal에 업로드했습니다. 세 개의 엔진이 트로이목마로 표시했습니다. “깨끗한” 바이너리는 실제로 그들의 특정 백신이 탐지하지 못한 악성코드였습니다.

이제 그들은 모든 다운로드에 대해 이 검사를 자동화했습니다. 방법은 다음과 같습니다.

무료 API 키 받기

  1. 회원가입하기
  2. 프로필 → API 키 로 이동
  3. 키 복사 (무료 티어: 하루 500 조회, 분당 4 요청)

URL 스캔

import requests
import time

API_KEY = "your-api-key-here"
headers = {"x-apikey": API_KEY}

# Submit URL for scanning
url_to_scan = "https://example.com/suspicious-download"
response = requests.post(
    "https://www.virustotal.com/api/v3/urls",
    headers=headers,
    data={"url": url_to_scan},
)
analysis_id = response.json()["data"]["id"]
print(f"Scan submitted: {analysis_id}")

# Wait for results
time.sleep(15)

# Get results
result = requests.get(
    f"https://www.virustotal.com/api/v3/analyses/{analysis_id}",
    headers=headers,
).json()
stats = result["data"]["attributes"]["stats"]
print(f"Malicious: {stats['malicious']}")
print(f"Suspicious: {stats['suspicious']}")
print(f"Clean: {stats['undetected']}")
print(f"Timeout: {stats['timeout']}")

파일 해시 확인

파일을 업로드하지 마세요—해시만 확인하면 됩니다. 더 빠르고 개인적입니다.

import hashlib
import requests

API_KEY = "your-api-key-here"
headers = {"x-apikey": API_KEY}

def check_file(filepath):
    """Check if a file is known malware by its SHA‑256 hash."""
    # Calculate hash
    sha256 = hashlib.sha256()
    with open(filepath, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            sha256.update(chunk)
    file_hash = sha256.hexdigest()

    # Query VirusTotal
    response = requests.get(
        f"https://www.virustotal.com/api/v3/files/{file_hash}",
        headers=headers,
    )

    if response.status_code == 200:
        attrs = response.json()["data"]["attributes"]
        stats = attrs["last_analysis_stats"]

        print(f"File: {filepath}")
        print(f"SHA‑256: {file_hash}")
        print(f"Detection: {stats['malicious']}/{sum(stats.values())} engines")
        print(f"First seen: {attrs.get('first_submission_date', 'Unknown')}")

        if stats["malicious"] > 0:
            print("⚠ MALWARE DETECTED!")
            # Show which engines flagged it
            for engine, result in attrs["last_analysis_results"].items():
                if result["category"] == "malicious":
                    print(f"  └─ {engine}: {result['result']}")
        else:
            print("✓ File appears clean")
    elif response.status_code == 404:
        print("File not in VirusTotal database (never scanned)")

# Example usage
check_file("suspicious_file.exe")

도메인 스캔

도메인이 악성코드와 연관되어 있는지 확인합니다.

import requests

API_KEY = "your-api-key-here"
headers = {"x-apikey": API_KEY}

def check_domain(domain):
    """Check domain reputation on VirusTotal."""
    response = requests.get(
        f"https://www.virustotal.com/api/v3/domains/{domain}",
        headers=headers,
    )

    if response.status_code == 200:
        attrs = response.json()["data"]["attributes"]
        stats = attrs.get("last_analysis_stats", {})

        print(f"Domain: {domain}")
        print(f"Malicious: {stats.get('malicious', 0)}")
        print(f"Suspicious: {stats.get('suspicious', 0)}")
        print(f"Clean: {stats.get('undetected', 0)}")

        # WHOIS data
        print(f"Registrar: {attrs.get('registrar', 'Unknown')}")
        print(f"Created: {attrs.get('creation_date', 'Unknown')}")
    else:
        print(f"Could not retrieve data for {domain}")

# Example usage
check_domain("example.com")

배치 스캐너 — 여러 IOC 확인

import base64
import time
import requests

API_KEY = "your-api-key-here"
headers = {"x-apikey": API_KEY}

def batch_scan(iocs, ioc_type="url"):
    """Scan multiple indicators of compromise."""
    results = []

    for ioc in iocs:
        if ioc_type == "url":
            # URL ID is the base64url‑encoded URL (without padding)
            url_id = base64.urlsafe_b64encode(ioc.encode()).decode().strip("=")
            r = requests.get(
                f"https://www.virustotal.com/api/v3/urls/{url_id}",
                headers=headers,
            )
        elif ioc_type == "hash":
            r = requests.get(
                f"https://www.virustotal.com/api/v3/files/{ioc}",
                headers=headers,
            )
        elif ioc_type == "ip":
            r = requests.get(
                f"https://www.virustotal.com/api/v3/ip_addresses/{ioc}",
                headers=headers,
            )
        else:
            print(f"Unsupported IOC type: {ioc_type}")
            continue

        if r.status_code == 200:
            stats = r.json()["data"]["attributes"].get("last_analysis_stats", {})
            mal = stats.get("malicious", 0)
            status = "⚠ THREAT" if mal > 0 else "✓ Clean"
            results.append({"ioc": ioc, "malicious": mal, "status": status})
        else:
            results.append({"ioc": ioc, "malicious": 0, "status": "? Not found"})

        time.sleep(15)  # Respect free‑tier rate limit

    return results

# Example usage
iocs = [
    "https://malicious.example.com",
    "d41d8cd98f00b204e9800998ecf8427e",  # example hash
    "8.8.8.8",
]
for r in batch_scan(iocs, ioc_type="url"):
    print(f"{r['status']}: {r['ioc']} (malicious engines: {r['malicious']})")
print(f"| {r['ioc']} | {r['malicious']} detections")

의심스러운 IP 확인

batch_scan(["8.8.8.8", "1.1.1.1"], ioc_type="ip")

구축할 수 있는 것

  • Download scanner – 파일을 열기 전에 자동으로 모든 파일을 검사합니다
  • Email security gateway – 첨부 파일과 링크를 스캔합니다
  • Incident response tool – 위협 보고서에서 IOCs를 대량 검사합니다
  • Browser extension backend – 악성 URL에 대해 사용자에게 경고합니다
  • CI/CD security check – 배포 전에 빌드 아티팩트를 스캔합니다

무료 티어 제한

기능무료프리미엄
일일 조회 수50030,000+
분당 요청 수4720
파일 업로드 크기32 MB650 MB
헌팅 규칙없음있음

일일 500회 조회는 개인 보안 도구와 소규모 프로젝트에 충분히 관대합니다.

보안 도구를 만들고 있나요?
더 많은 무료 API 튜토리얼 및 개발자 도구는 제 GitHub에서 확인하세요.

0 조회
Back to Blog

관련 글

더 보기 »