セキュアスカイによる安全なWebサイト運営のためのセキュリティ情報

  • ホーム
  • エンジニアブログ
  • Dredger × FutureVuls 連携ガイド:外部公開資産の"発見"から"脆弱性管理"までワンストップで効率化する
エンジニアブログ
  • shareSNSでシェア
  • Facebookでシェアする
  • Xでシェアする
  • Pocketに投稿する
  • はてなブックマークに投稿する

Dredger × FutureVuls 連携ガイド:外部公開資産の"発見"から"脆弱性管理"までワンストップで効率化する

Dredger × FutureVuls 連携ガイド:外部公開資産の"発見"から"脆弱性管理"までワンストップで効率化する

はじめに

こんにちは、Dredger開発チームの小野里です。
近年、多くの企業でクラウドサービスの利用拡大やリモートワークの普及などにより、管理すべきIT資産が急速に増加しています。それに伴い、外部に公開されるシステムやサービスも多様化し、結果として攻撃者から狙われうる領域、いわゆる「Attack Surface(攻撃対象領域)」が拡大する傾向にあります。特に、組織内で把握しきれていないIT資産が攻撃の対象となるケースも増加しており、これらの資産を特定し、適切に管理することがこれまで以上に重要になっています。

このような背景から、外部公開されているIT資産を発見・管理するEASM(External Attack Surface Management)ツールや、システム内の脆弱性を効率的に管理する脆弱性管理ソリューションへの注目が高まっています。これらをうまく連携させることができれば、攻撃対象領域の可視化から脆弱性の特定、そして対策までを一気通貫で行い、セキュリティ運用を大幅に効率化できる可能性があります。

そこで本記事では、私たち株式会社セキュアスカイ・テクノロジーが開発・提供しているEASMサービス「Dredger(ドレッジャー)」と、フューチャー株式会社が提供する脆弱性管理ソリューション「FutureVuls」を連携させる具体的な手順について解説します。

この連携により、以下の実現を目指します。

  • 発見から対策までをワンストップで: Dredgerで発見した”想定外の資産”も、即座にFutureVulsの脆弱性管理フローに乗せる。
  • リスク判断の精度向上: 外部の視点(Dredger)と内部の視点(FutureVuls)を統合し、組織全体の脆弱性リスクを正確に把握・評価する。
  • 運用工数の大幅な削減: 手作業による情報集約や棚卸しから解放され、”本当に対応すべき脆弱性”への対処に集中する。

この連携手順を通じて、外部から見える自社のIT資産とその脆弱性をFutureVuls上で一元的に管理し、より効果的なセキュリティ対策を実現する方法をご紹介できればと思います。

想定読者

本記事は、以下のような方々を対象としています。

  • 企業の情報システム部門やセキュリティ担当者の方
  • 自社のIT資産管理や脆弱性対策の効率化に関心がある方
  • DredgerやFutureVulsの導入、またはより効果的な活用方法を検討している方

前提条件

本記事ではDredgerとFutureVulsのアカウントが作成済みであり、基本的な設定が完了、及び必要な権限を有していることを前提としています。また、本記事の内容は2025年6月時点での情報に基づいています。各サービスのUIやAPI仕様は将来的に変更される可能性があるため、詳しい操作手順については各サービスの最新のマニュアルや公式ドキュメントを参照してください。

各製品の紹介

Dredgerとは

Dredger(ドレッジャー)は、株式会社セキュアスカイ・テクノロジーが提供する国産のEASM(External Attack Surface Management)サービスです。​このサービスは、インターネットからアクセス可能な自社のIT資産を探索・発見し、攻撃対象領域(Attack Surface)の可視化と管理を実現します。

FutureVulsとは

FutureVulsは、フューチャー株式会社が提供する脆弱性管理ソリューションで、OSSの脆弱性スキャナ「Vuls」をベースに開発されています。​このサービスは、システム内の脆弱性を一元的に管理し、検知から対応までのプロセスを効率化します。

DredgerとFutureVulsの連携の意義

この2つのサービスを連携させることには、以下のような意義があると考えています。

連携前の課題 (Before) 連携後の効果 (After)
外部資産の棚卸しは手作業。情報が古く、抜け漏れも発生。 Dredgerが自動で外部から見えるIT資産を発見。常に最新の状態で管理。
発見した資産のリスク評価は担当者の経験頼み。 発見した資産をFutureVulsに自動連携して脆弱性の可視化とリスク評価。
脆弱性情報が複数のツールに散在。全体像の把握が困難。 FutureVuls上で内外の脆弱性を一元管理。統一された基準で評価。
どの脆弱性から対応すべきか判断に時間がかかり、対応が後手に。 FutureVulsのトリアージ機能で対応の優先順位が明確になり、迅速なアクションが可能に。

このように、DredgerとFutureVulsを連携させることで、組織は自社の攻撃対象領域の全体像をより正確に把握し、発見された脆弱性に対して迅速かつ効果的な対策を実施するための基盤を強化することができます。

3ステップで実現!DredgerとFutureVulsの連携手順

ここからは、実際の連携手順を解説します。
「API連携」と聞くと難しそうに感じるかもしれませんが、ご安心ください。今回はコピー&ペーストで実行できるサンプルスクリプトをご用意しましたので、どなたでも簡単に試すことができます。

Step 1: 連携の準備

1. FutureVulsのトークン設定画面でトークンを設定する

FutureVulsのオーガニゼーション設定より、トークン→トークン追加を選択してトークンを追加します。
トークンの名前は任意、API権限は「読み込み,更新」とします。

 

 

2. Dredgerでアセット情報のエクスポートを行う

Dredgerでアセットディスカバリー・リスクスキャン共に完了した状態のアセットをチェックし、その他の操作→データをエクスポート→JSONファイルとしてエクスポート を選択し、エクスポートを行ってください。アセットは複数選択いただいて構いません。エクスポート後はダウンロードセンターよりJSONファイルのダウンロードを行ってください。

 

 

Step 2: サンプルスクリプトによる連携実行

この後はDredgerのJSONファイルからアセット情報を抜き出し疑似サーバとして登録、そしてCVE番号を抜き出し登録していく流れになりますが、手動で行うには少々骨の折れる作業です。そこで、今回はその作業を自動的に行うサンプルスクリプトをご用意させていただきました。
requestsをインストールした状態のPython3.10以降で実行いただけます(uvを導入済みの方はuv runで自動的に環境構築されます)。
FutureVuls APIキーの記入後、以下のように第一引数にDredgerのJSONエクスポートファイルのパスを指定してあげると自動的にアセットを疑似サーバとして登録、CVEIDを抽出して外部スキャン結果としてインポートします。

pip install requests
python main.py <JSONエクスポートファイルのパス>
または
uv run main.py <JSONエクスポートファイルのパス>

実行後スクリプトと同階層にdomain_and_id_mapping.txtというファイルが作成されます。このファイルにはスキャン対象のドメイン名と追加した外部スキャン結果のID、外部スキャン結果が追加されたサーバIDの組み合わせが記録されます。このファイルが存在する状態で同一のアセットに対してスクリプトを実行した場合、疑似サーバが新規作成されることなく以前の疑似サーバのスキャン結果を更新する処理になります。

以下にサンプルスクリプトのソースコードを記載します。

# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.32.3",
# ]
# ///
import argparse
import json
from typing import TypedDict
import requests

VULS_ACCESS_TOKEN = "{FutureVulsのAPIアクセストークン}"

MAPPING_FILE = "domain_and_id_mapping.txt"
VULS_BASE_URL = "https://rest.vuls.biz"


class DredgerAsset(TypedDict):
    domain_name: str
    cves: list[str]


def add_pseudo_server(server_name: str):
    """
    FutureVuls APIを経由して疑似サーバを追加する関数
    """
    headers = {
        "Accept": "application/json",
        "Authorization": VULS_ACCESS_TOKEN,
        "Content-Type": "application/json",
    }
    data = {"serverName": server_name}
    response = requests.post(
        VULS_BASE_URL + "/v1/server/pseudo", headers=headers, data=json.dumps(data)
    )

    return response.json()


def add_scan_import_data(server_id: int, dredger_asset: DredgerAsset):
    """
    FutureVuls APIを経由して外部スキャンデータをインポートする関数
    """
    headers = {
        "Accept": "application/json",
        "Authorization": VULS_ACCESS_TOKEN,
        "Content-Type": "application/json",
    }
    data = {
        "cveList": dredger_asset["cves"],
        "isExternalScan": True,
        "scanImportName": dredger_asset["domain_name"],
        "serverID": server_id,
    }
    response = requests.post(
        VULS_BASE_URL + "/v1/scanImport", headers=headers, data=json.dumps(data)
    )

    return response.json()


def update_scan_import_data(scan_import_id: int, dredger_asset: DredgerAsset):
    """
    FutureVuls APIを経由して外部スキャンデータを更新する関数
    """
    headers = {
        "Accept": "application/json",
        "Authorization": VULS_ACCESS_TOKEN,
        "Content-Type": "application/json",
    }
    data = {"cveList": dredger_asset["cves"]}
    response = requests.put(
        VULS_BASE_URL + f"/v1/scanImport/{scan_import_id}",
        headers=headers,
        data=json.dumps(data),
    )

    return response.json()


def parse_dredger_json(filepath: str) -> list[DredgerAsset]:
    """
    DredgerのJSONファイルをパースしてドメイン名とCVE番号を抽出する関数
    """
    result_assets: list[DredgerAsset] = []
    with open(filepath) as f:
        json_dict: list[dict] = json.load(f)
        for asset in json_dict:
            domain_name = asset["domain_name"]
            cves: set[str] = set()
            for risk in asset["assets_on_risks"]:
                if risk["risk"]["categoryName"] == "CVE-REGISTERED-VULNERABILITY":
                    cves.add(risk["risk"]["id"])

            result_assets.append(
                {
                    "domain_name": domain_name,
                    "cves": sorted(list(cves)),
                }
            )
    return result_assets


def save_id_to_file(response: dict, domain_name: str):
    """
    POSTリクエストの結果からIDを取得し、ファイルに保存
    """
    scan_import_id = response.get("id")
    server_id = response.get("serverID")

    if isinstance(scan_import_id, int) and isinstance(server_id, int):
        with open(MAPPING_FILE, "a") as file:
            file.write(f"{domain_name} {scan_import_id} {server_id}")
        print(
            f"ドメイン: {domain_name} スキャン結果ID: {scan_import_id} サーバID: {server_id}をファイルに追記しました"
        )
    else:
        print("スキャン結果の登録に失敗しました")
        raise ValueError("スキャン結果の登録に失敗しました")


def read_ip_and_scan_import_id_from_file():
    """
    ファイルからドメイン名と外部スキャン結果のIDのペアを読み取る
    """
    ip_id_pairs: dict[str, int] = {}
    try:
        with open(MAPPING_FILE, "r") as file:
            for line in file:
                parts = line.strip().split()
                if len(parts) == 3:
                    domain_name, scan_import_id, server_id = parts
                    if scan_import_id.isdigit():
                        ip_id_pairs[domain_name] = int(scan_import_id)
                    else:
                        print(
                            f"無効な外部スキャン結果のIDが見つかりました: {scan_import_id}"
                        )
    except FileNotFoundError:
        pass
    except Exception as e:
        raise e

    return ip_id_pairs


def main():
    parser = argparse.ArgumentParser(
        description="DredgerのアセットJSONエクスポートデータから、FutureVulsの疑似サーバを登録し外部スキャンデータをインポートするスクリプト"
    )
    parser.add_argument(
        "export_file", help="DredgerのアセットJSONエクスポートファイルのパス"
    )

    args = parser.parse_args()

    filename: str = args.export_file

    assets = parse_dredger_json(filename)
    ip_id_pairs = read_ip_and_scan_import_id_from_file()
    for asset in assets:
        if scan_import_id := ip_id_pairs.get(asset["domain_name"]):
            # 実行履歴があった場合
            print(f"疑似サーバ{asset['domain_name']}の外部スキャンデータを更新します")
            update_scan_import_data(scan_import_id, asset)
            print("外部スキャンデータを更新しました")
        else:
            # 実行履歴が無かった場合
            print(f"{asset['domain_name']}の疑似サーバを作成します")
            add_pseudo_server_response = add_pseudo_server(asset["domain_name"])
            print("疑似サーバを作成しました")
            server_id = add_pseudo_server_response["id"]
            print("外部スキャンデータをインポートします")
            add_scan_import_data_response = add_scan_import_data(server_id, asset)
            print("外部スキャンデータをインポートしました")
            print("実行履歴データを記録します")
            save_id_to_file(add_scan_import_data_response, asset["domain_name"])
            print("実行履歴データを記録しました")


if __name__ == "__main__":
    main()

Step 3: FutureVulsでの連携結果の確認

スクリプト実行後、FutureVulsにログインすると、Dredgerで発見されたアセットが「サーバ」として登録され、関連するCVE情報が紐付いていることが確認できます。これにより、他の管理対象サーバと全く同じように、FutureVuls上で脆弱性の一元管理が可能になります。
FutureVuls上での確認方法はFutureVuls Blog: 内部と外部をつなぐ脆弱性管理|FutureVulsでASM・Nmap・Nessusなどの診断結果を活用する方法 を参照してください。

 

まとめ

本記事では、EASMツールであるDredgerと継続的脆弱性管理サービスであるFutureVulsを連携させる具体的な手順について、サンプルスクリプトを交えながら解説しました。

実施した手順を改めてまとめると以下のようになります。

  1. Dredgerからのアセット情報エクスポート: Dredgerで外部公開されているIT資産とそのリスク情報(CVE)を収集し、JSON形式でエクスポートしました。
  2. FutureVulsへの疑似サーバとしてのインポート: エクスポートしたJSONデータをPythonスクリプトで処理し、各アセットをFutureVulsに疑似サーバとして登録しました。これにより、Dredgerで発見した外部資産をFutureVulsの管理体系に組み込むことができました。
  3. 脆弱性情報の外部スキャン結果としての取り込み: スクリプトはさらに、各疑似サーバに対して関連するCVE情報をFutureVulsの外部スキャン結果としてインポートしました。また、再実行時には既存の情報を更新する仕組みも実装しました。

DredgerとFutureVulsの連携のポイント

  1. 攻撃対象領域の完全な可視化: Dredgerが、自社でも把握していなかったIT資産まで洗い出します。
  2. 脆弱性管理の一元化: 発見した資産の脆弱性情報をFutureVulsに集約。内外のリスクを同一プラットフォームで管理できます。
  3. 対応の自動化と迅速化: FutureVulsのトリアージ機能により、”本当に危険な脆弱性”から対処する、効率的で迅速な運用体制を構築します。

今回の連携にご興味をお持ちいただけましたら、ぜひ無料トライアルでその効果を直接お試しください。設定や運用に関するご相談も、両社で歓迎しております。

なお、将来的にはDredgerの標準機能として、今回ご紹介したようなFutureVulsとの連携機能を組み込み、画面操作だけで自動的にデータ連携を行えるようにすることも計画しています。具体的な実装時期については現時点では未定ですが、今後のアップデートにご期待いただければと思います。

今回の連携手順が、皆様のセキュリティ運用をより強化・効率化するための一助となれば幸いです。

  • shareSNSでシェア
  • Facebookでシェアする
  • Xでシェアする
  • Pocketに投稿する
  • はてなブックマークに投稿する

この記事の筆者

筆者:小野里亮祐

小野里亮祐

2021年4月にSSTへ新卒入社。研究開発部所属。
ついに入社4年目になってしまった。まだまだ新人でいたかったと思っている。