皆さんこんにちは。
今年で2年目になりました開発本部の太田です。本ブログ2回目の投稿となります!
早速ですが、今回はタイトル通り、AWS Lambdaを使ってJVN iPediaから脆弱性情報を取得し、Google Chatに通知する方法をお伝えしていければと思います。
はじめに
私は昨年末に業務で社内用に「脆弱性情報自動収集ツール」を作成しました。
これは、JVN iPediaで公開されているAPIを利用し、そこで管理されている脆弱性情報を入手します。
脆弱性とは、コンピュータのOSやソフトウェアにおいて、プログラムの不具合や設計上のミスが原因となって発生した情報セキュリティ上の欠陥のことを言います。脆弱性が残された状態でコンピュータを利用していると、不正アクセスに利用されたり、ウイルスに感染したりする危険性があります。
その脆弱性の情報を社内の人間に通知し、逸早く脆弱性に対応できるようにする目的があります。
このツールは、CloudWatch Eventsでスケジュール式に設定し、毎日情報を自動で取得しchatに流しています。
Amazon S3(オブジェクトストレージサービス)にデータを保存、また脆弱性情報のキーワードをまとめた収集リストもS3で管理しており、チャットツールから収集リストをメンテナンスできるようにしています。
そして最近、このツールの簡易版(手動実行版)を人に教える機会があったので、この記事ではそちらをご紹介しています。
※本開発ではこちらの記事を非常に参考にしています!
JVN iPedia とは
JVN iPediaとは、JVNに掲載される脆弱性対策情報のほか、国内外問わず日々公開される脆弱性対策情報のデータベースです。
JVNとは、「Japan Vulnerability Notes」のことで、「JPCERT/CC」と「IPA」という組織が共同で運営している、脆弱性に関する情報を公開しているホームページのことです。
そしてこのJVN iPediaの情報を取得するために、Webを通じて利用するためのソフトウェアインターフェースが「MyJVN API」です。MyJVN が提供するAPIを利用して様々な脆弱性情報を取得することができます。
今回は「getVulnOverviewList」というAPIのみ使用します。
getVulnOverviewListは、フィルタリング条件に当てはまる脆弱性対策の概要情報リストを取得します。
製品名やベンダID、製品ID、CVSS(深刻度)、更新日の範囲指定など様々にフィルタリングが可能です。
【HP URL】※2020年現在
■ JVN iPedia
https://jvndb.jvn.jp/
■ JVN
https://jvn.jp/
■ MyJVN API
https://jvndb.jvn.jp/apis/index.html
簡易版ツールの流れ
タイトル通りです。
エクセルで簡単に作成した図です。↑の流れになります。
まずLambda関数を作成し、Lambda functionにソースを記述していきます。今回はPythonを使用しています。JVN iPediaを参照し、MyJVN APIを用いて脆弱性情報を取得します。その後はJSON形式に変換してPOSTしてあげればGoogle Chatにデータが転送されます!
※もともとSlackというチャットツールに通知させていましたが、部内都合でGoogle Chatに移行することになりました。
Lambda
Lambdaを構築します。本来はCloudWatch Eventsでスケジュール式(毎日自動で実行)などが良いのでしょうが、今回は手動でLambdaを実行させて脆弱性情報を取得します。
構築を終えたら、Lambda functionにPythonでソースを入力していきます。必要な処理は大きく分けて3つです。
① リクエストURLを作成し、脆弱性情報を取得(XML形式)
今回使用するAPIは「getVulnOverviewList」のみです。
リクエストURLは、以下になります。
・https://jvndb.jvn.jp/myjvn?method=getVulnOverviewList&feed=hnd&パラメタ名=パラメタ値&…
※パラメタ名、パラメタ値については下記URLを参照してください(MyJVN API)
https://jvndb.jvn.jp/apis/getVulnOverviewList_api_hnd.html
設定しているソースです。
1 2 3 4 5 6 7 8 9 10 |
url = os.environ["URL_PATH"] + "?method=" + os.environ["VULN_OVERVIEW_LIST_METHOD"] + "&feed=hnd" \ + "&keyword=" + keyword \ + "&severity=" + severity \ + "&dateFirstPublishedStartY=" + str(start.year) \ + "&dateFirstPublishedStartM=" + str(start.month) \ + "&dateFirstPublishedStartD=" + str(start.day) \ + "&dateFirstPublishedEndY=" + str(end.year) \ + "&dateFirstPublishedEndM=" + str(end.month) \ + "&dateFirstPublishedEndD=" + str(end.day) \ + "&rangeDatePublished=n&rangeDatePublic=n" |
パラメタ名までのURLは環境変数に定義して使用しています。(URL_PATH、VULN_OVERVIEW_LIST_METHOD)
keywordには脆弱性情報をフィルタリングするためのキーワード、severityにはCVSSを入力しています。
日にちの指定範囲は、発行日開始~終了を選択しており、値は自由に設定してください。
※今回は前日のみの指定としています。
1 2 3 4 5 |
# 特定のURLをオープン res = urllib.request.urlopen(url).read().decode("utf-8") # XML文字列をパース dom = minidom.parseString(res) |
リクエストURLを用いて、XML形式の脆弱性情報を取得し、パースしています。
② 脆弱性情報を整形しメッセージにする
パースした脆弱性情報を、Google Chat通知用に整形します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# itemタグの数(脆弱性情報のヒット数)だけループさせ、必要な情報を取得 for item in dom.getElementsByTagNameNS(os.environ["NS"], "item"): vul = {} # URLデコード vul["keyword"] = urllib.parse.unquote(keyword) if len(item.getElementsByTagNameNS(os.environ["NS_SEC"], "identifier")) == 1: vul["id"] = item.getElementsByTagNameNS(os.environ["NS_SEC"], "identifier")[0].childNodes[0].nodeValue if len(item.getElementsByTagNameNS(os.environ["NS"], "link")) == 1: vul["link"] = item.getElementsByTagNameNS(os.environ["NS"], "link")[0].childNodes[0].nodeValue # 脆弱性情報が存在しない場合、linkに値を入れさせない if vul["link"] == "https://jvndb.jvn.jp/apis/myjvn": vul["link"] = "" if len(item.getElementsByTagNameNS(os.environ["NS"], "title")) == 1: vul["title"] = item.getElementsByTagNameNS(os.environ["NS"], "title")[0].childNodes[0].nodeValue if len(item.getElementsByTagNameNS(os.environ["NS"], "description")) == 1: vul["description"] = item.getElementsByTagNameNS(os.environ["NS"], "description")[0].childNodes[0].nodeValue if len(item.getElementsByTagNameNS(os.environ["NS_SEC"], "cvss")) == 2: cvss = item.getElementsByTagNameNS(os.environ["NS_SEC"], "cvss")[0] vul["cvss_score"] = cvss.getAttribute("score") vul["cvss_severity"] = cvss.getAttribute("severity") # linkに値が入っている場合のみ、配列に情報を追加 if vul["link"]: vulnerabilities.append(vul) |
getElementsByTagName() で指定したタグ名の要素のリストを取得しています。
タイトルタグやCVSSタグの情報を、それぞれ格納し、最後に配列に追加しています。
③ Google Chatに通知
あとはメッセージをJSON構造に変換し、Google Chat WebhookのURLにPOSTすればOKです。
Webhookの設定を画像を用いてご説明いたします。
1.チャットルームの作成
ルーム名は自由に設定してください。こちらでは「webhook_test」としています。
2.Webhookの設定
①~③の順番で選択し、「Webhookを管理」を選択してください。
Webhook名は、こちらも自分の分かりやすいように設定してください。
アバターのURLはアイコン画像を設定するものです。今回は省略します。
確認後、保存を押してください。
WebhookのURLが作成されました。右側の赤丸で囲っている部分をクリックし、コピーできます。
コピーしたURLをLambdaで使用し、送ってみましょう!
1 2 3 |
requests.post(os.environ["GOOGLE_CHAT_NOTIFY_URL"], data = json.dumps({ "text": msg, }), timeout=10) |
msgに整形した脆弱性情報を入れています。
先程コピーしたWebhookのURLは、環境変数に設定しています。キーは「GOOGLE_CHAT_NOTIFY_URL」です。
無事に送れていることを確認できました!
さいごに
今までは担当部署が手動で脆弱性情報を収集していましたが、自動化を図り、
業務でよく使うチャットツールなどに情報を流すことにより、今まで以上に意識するようになりました。
数が多く、該当する対策情報を探すのも大変でしたが、検索条件を変えることで容易に探し当てることができるので、利便性が増しました。
みなさんも、ツールを作成して利便化を図ってみましょう!