淡々と備忘録を綴る

IT周りの行き詰まったところやメモを備忘録として記録します

AWS WAFとAthenaを使って、リアルタイム攻撃ログ観測のためのやられサイトを作ってみた。

はじめに

直近でAWSのSpeciality資格を取得し、AWSを触ってみたくなったので、WEBページに対する攻撃ログを収集するやられサイトを簡単に構築してみることにしました。
筆者は業務でセキュリティに関わっており、世の中に飛び交っている攻撃ログを観測しておきたいと常々感じていたので、手元環境で持っておこうと思った次第です。
これにより、Log4jのときのような脆弱性が出てきたときに、生の攻撃ログを観測しながら対策に繋げたり、定常的に行われている攻撃の種類を推測することを目的としています。
(あと、最近業務周りでAWS WAFの話題が出ることが多かったので実際に触ってみたかったという理由もあります。)  

使用したリソースの説明

今回のやられサイトの構築には、AWSの以下の4つの主要リソースを活用しました。それぞれについて簡単に説明します。

  • S3
    Amazon Simple Storage Service(S3)は、ウェブからのアクセスを想定したオブジェクトストレージサービスです。おとりサイトは簡単な静的サイトなので、HTMLファイルのホスティングに使用しました。 また、アクセスログもここに保存し、後述のAthenaでクエリを発行できるようにしておきます。
  • CloudFront
    Amazon CloudFrontは、Amazonコンテンツ配信ネットワークCDN)サービスです。CloudFrontはAWS WAFのデプロイ先になるため、WAFの前提条件として利用しています。
  • AWS WAF
    AWS WAF(Web Application Firewall)は、サイトを不正アクセスから守るためのウェブアプリケーションファイアウォールです。
    特定のトラフィックパターンをブロックしたり、特定のIPアドレスからのリクエストを制御したりします。
    今回のケースでは、AWS WAFはやられサイトに対する不正なアクセスや攻撃の詳細を収集するために使用しています。
    また、AWS WAF自体の挙動を実機で確認することも目的の一つです。
  • Athena
    Amazon Athenaは、クエリサービスで、AWS S3に保存されたデータに対してSQLクエリを実行できます。
    サーバーの設定や管理は不要で、手元にある攻撃ログやIoCデータに対して直接クエリを投げることができます。
    これにより、収集したIoCデータの分析や検索が迅速かつ容易に行えます。

やられサイトの構成

今回構築するやられサイトのアーキテクチャ図は以下の通りです。
awsアーキテクチャ図 また、今回このサイトはCDNにCloudFrontにデプロイされているので、cloudfront.netドメインとしたURLを持っています。(単一のグローバルIPではありません。)
攻撃者がこのサイトに気づいてくれるように、Pastebinを使用しました。

Pastebinとは

Pastebinとは、ユーザーがテキストデータを投稿し、それを公開できるウェブサービスの一つです。
正規の利活用としては主にプログラマーコードスニペットを共有するために使用されますが、その手軽さから様々な情報の共有に広く利用されています。
また、悪意のある攻撃者がファイルレスマルウェアなどのC2サーバーとして利用していることでも非常に有名です。

この特性を利用し、やられサイトのURLをPastebinに投稿し、攻撃者がそのURLを見つけてアクセスしてくることを期待しています。
これは一種のハニーポット戦略で、攻撃者を誘引し、その行動を観察することを目的としています。

ロギングの設定

今回はAWS WAFのログを主に収集対象にします。ロギングの対象自体はAllow/Denyに関わらず行い、分析の際に任意でフィルタをかけることにしました。
レピュテーションサービスのクローラなどの情報も取れたら面白いかなと思ったからです。 使用するWebACLのルールは以下とします。

  • AWS-AWSManagedRulesAmazonIpReputationList  
  • AWS-AWSManagedRulesCommonRuleSet
  • AWS-AWSManagedRulesKnownBadInputsRuleSet
  • AWS-AWSManagedRulesBotControlRuleSet

今回はログの保存先をS3にしているので、CloudWatch Log Insightsを使用できませんでした。こちらについてはまたどこかで試してみたいと思います。

ログの分析について

今回の第一目的は攻撃通信のログを観測することなので、あまり条件を絞りきらずにログを整形するだけのクエリを用意しました。
特徴のある攻撃通信が流行ったときは適宜絞り込みをかけていくことを想定しています。
ただ、自分がAthena/SQLをあまり触ったことがないので、ちょっと概念の把握に手間取りました。以下に備忘録として残しておきます。

DB、テーブルの作成

AthenaでS3に保存されているデータを分析するためには、Athena上にRDSとして持ってくる必要があります。
そのために、以下の手順でクエリを発行しました。

  • DBの作成
CREATE DATABASE test;
  • テーブルの作成
CREATE EXTERNAL TABLE `waflogs`(
  `timestamp` bigint,
  `formatversion` int,
  `webaclid` string,
  `terminatingruleid` string,
  `terminatingruletype` string,
  `action` string,
  `terminatingRuleMatchDetails` array < struct <
    conditionType: string,
    location: string,
    matchedData: array < string >
    > >,
  `httpsourcename` string,
  `httpsourceid` string,
  `ruleGroupList` array < struct <
                    ruleGroupId: string,
                    terminatingRule: struct < ruleId: string, action: string >,
                    nonTerminatingMatchingRules: array < struct < action: string, ruleId: string > >,
                    excludedRules: array < struct < exclusionType: string, ruleId: string > 
   > >,
  `ratebasedrulelist` array<
                      struct<
                        ratebasedruleid:string,
                        limitkey:string,
                        maxrateallowed:int
                            >
                           >,
  `nonterminatingmatchingrules` array<
                                struct<
                                  ruleid:string,
                                  action:string
                                      >
                                     >,
  `httprequest` struct<
                      clientip:string,
                      country:string,
                      headers:array<
                              struct<
                                name:string,
                                value:string
                                    >
                                   >,
                      uri:string,
                      args:string,
                      httpversion:string,
                      httpmethod:string,
                      requestid:string
                      > 
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://<バケットID>/AWSLogs/'

サーチクエリの発行

Athenaには、任意のクエリを保存してあとからコールできる機能がありました。(便利!)
ただし、一度保存したクエリは編集できず、一度消して再度登録する必要があるみたいです。(不便!)
普段みるクエリと、何かあったとき用にWHERE句で条件指定できる準備だけ整えているクエリを用意しておきました。

  • 普段みるクエリ
SELECT from_unixtime(timestamp/1000, 'Asia/Tokyo') AS JST,
       httpsourcename,
       action,
       httpsourceid,
       httprequest.clientip,
       httprequest.country,
       httprequest.uri,
       httprequest.args,
       httprequest.httpmethod,
       httprequest.headers

FROM
       "waflogs"
ORDER by JST desc
;
  • 特定の痕跡を探すクエリ
SELECT from_unixtime(timestamp/1000, 'Asia/Tokyo') AS JST,
       httpsourcename,
       httpsourceid,
       httprequest.clientip,
       httprequest.country,
       httprequest.uri,
       httprequest.args,
       httprequest.httpmethod,
       httprequest.headers,
       header,
       header.value
FROM
       "waflogs",
       UNNEST(httprequest.headers) t(header)
WHERE
       header.value like '%<hoge>%'
;

また、Athenaでのクエリは以下のサイトを参考にしました。
https://dev.classmethod.jp/articles/waf-query-pattern-in-athena/
https://dev.classmethod.jp/articles/analyzing-waflogs-with-athena/
https://runble1.com/aws-waf-athena-array-struct-json/#toc14

まとめと今後の展望

以上が今回のやられサイト構築の概要となります。
AWSを使用することで短期間でやられサイトを構築し、実際の攻撃ログを収集することができました。
今後の展望としては、以下の実装を考えています。

  • アーキテクチャを変更して、グローバルIPを持たせてIPでのクロールの対象に入れる。
  • Athenaでログ状況のサマリを出力し、Lamda経由でDiscordBOTと連携する

以上。

AWS Certified Security - Specialty(SCS-C01)に実質一週間で合格した話

はじめに

今回、実質一週間の勉強時間でAWS Certified Security - Specialty(SCS-C01)に合格するという(自分の中では)快挙を成し遂げたので、合格体験記という形で残したいと思います。
なぜこんな無茶なスケジュールで挑戦したかというと、SCS-C01が2023年の7月に終了してSCS-C02に試験問題が変わるため、過去問が豊富なうちに取得してしまおうと思い立ったからです。
急にやる気が舞い降りてきたのが6月末。そこから(半額バウチャー目当てで)AWS認定クラウドラクティショナー(CLF)を受験するのに一週間。SCSの勉強に一週間。AWS周りの勉強期間という言う意味だと合計2週間でAWS Certified Security - Specialty(SCS-C01)に合格できました。

前提知識

  • AWSはほぼ触ったことがない。数年前に一度簡単なWebアプリケーションを作って遊んでいただけ。
  • Azureはちょっとだけ触った経験あり。ただ、オンプレのクラウド移行のアーキテクチャを検討したことがある程度。
  • セキュリティを生業にしている。インシデントレスポンスやハンドリングなどは慣れている(つもり)

AWS SCSとは何か?

AWS SCS、すなわちAWS Certified Security - Specialty(AWS認定 セキュリティ - スペシャリティ)は、Amazon Web Services (AWS)のセキュリティ技術とプラクティスに関する深い知識を持つプロフェッショナルを認定する試験です。

AWS SCSの概要とその重要性:

AWS SCSは、AWSでのデータ保護とセキュリティの実装に関して、深い理解とスキルを持つことを証明する認定試験です。
これは、AWSを使用する企業や組織でのセキュリティ関連の役割に従事する人々、特にセキュリティエンジニア、セキュリティアーキテクト、情報セキュリティ専門家などにとって重要です。
この認定を持つことで、AWS環境でのデータ保護とセキュリティプロトコルの専門的な知識とスキルを持っていることを示すことができます。

AWS SCSがカバーする主要なトピックと要件:

AWS SCSの試験は、以下の5つのドメインに焦点を当てています:

  • AWSワークロードのインシデントレスポンス
    AWS環境でのセキュリティインシデントに対応するための能力。  
  • AWSワークロードの脅威と脅威の軽減策
    AWSのワークロードに対する潜在的な脅威を理解し、それらを軽減するための戦略と手段。
  • AWSのセキュリティ運用とリスク管理
    AWSにおけるセキュリティプロトコルの運用と、リスクを評価および管理するための技術と戦略。
  • セキュアなアーキテクチャと自動化、ロギング、モニタリング
    セキュリティを考慮に入れたAWSアーキテクチャの設計と、自動化、ロギング、モニタリングを利用したセキュリティの維持。
  • AWSのデータ保護とデータプライバシー
    AWSのデータ保護メカニズムとプライバシー関連の概念に関する理解。

試験を受けるには、AWS Certified Cloud PractitionerまたはAssociate-level認定を既に取得していること、および最低2年以上のAWSのハンズオンセキュリティ経験が推奨されています。が、前述の通り筆者は2年も触ってない状態で受験しました。CLFについては一週間前に取得したから許してほしい。

学習計画

勉強に費やせる時間が実質一週間しかなかったので、毎朝1時間毎晩2-3時間を勉強に費やしました。
学習リソースとしては、UdemyのAWS SCS対策講座を選びました。
この講座は試験のすべてのドメインを網羅しているため、体系的な知識を得るための主要なリソースとしました。
また、この講座はlab問題も含まれており、labに沿う形で実機を触って確認できたのも良いポイントでした。
文章だけの詰め込みと比較して、実機の画面を触って学習した方がイメージが付きやすかったです。
試験対策で触るくらいなら、AWSの無料枠に収まると思うので金銭面でも気にすることはないでしょう。

結果と総評

問題文が長文なので、頭の中でアーキテクチャ図がイメージしきれない時はホワイトボードで書き出していました。
Speciality試験の何が一番やっかいかというと、この問題文の長さだった気がします。勉強しているときから体力を使いましたが、試験中はなおさら疲れました。
点数は、合格点が750点に対して、結果は840での余裕を持っての合格となりました。
これは正直、AWSの知識ではなく普段使っているセキュリティ分野の知識で選択肢を狭めることができた結果だと思います。
最後に、試験対策を経てよく聞かれるなーと感じたトピックを並べて合格体験記を締めたいと思います。  

  • ロールの信頼関係
  • CMKの管理方法。ローテーション期間などに対するベストプラクティス。
  • Amazon Configの対応範囲
  • Cloud WatchやCloud Trailを使ったログ追跡の実装

Intel NUC12WSHi50Z RNUC12WSHI50Z00 にESXiをインストールする

NUCの物理構成

追加で購入したのは以下。

BIOSのアップデート

工場出荷時のままだとBIOSが最新ではないので、諸々の操作を行う前にNUCのBIOSを最新版に更新しておく。

ESXiインストールイメージの作成

PowerCLIのインストール

Install-Module -Name VMware.PowerCLI -Scope CurrentUser

途中で聞かれる質問にはぜんぶy。
ここで、以下のようなエラーが発生した。

Import-Module : モジュール ディレクトリに有効なモジュール ファイルが見つからなかったため、指定されたモジュール 'VMware.
PowerCLI' は読み込まれませんでした。
発生場所 行:1 文字:1
+ Import-Module VMware.PowerCLI
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (VMware.PowerCLI:String) [Import-Module], FileNotFoundException
    + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand

PackageManagement\Install-Package : 以下のコマンドは、このシステムで既に使用可能になっています: 'Export-VM,Get-VM,Get-V
MHost,Move-VM,New-VM,Remove-VM,Restart-VM,Set-VM,Set-VMHost,Start-VM,Stop-VM,Suspend-VM'。このモジュール 'VMware.VimAut
omation.Core' では既存のコマンドが上書きされる可能性があります。このモジュール 'VMware.VimAutomation.Core' をインストー
ルする場合は、-AllowClobber パラメーターを使用してください。
発生場所 C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1809 文字:21
+ ...          $null = PackageManagement\Install-Package @PSBoundParameters
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (Microsoft.Power....InstallPackage:InstallPackage) [Install-Package]、E
    xception
    + FullyQualifiedErrorId : CommandAlreadyAvailable,Validate-ModuleCommandAlreadyAvailable,Microsoft.PowerShell.Pack
   ageManagement.Cmdlets.InstallPackage

エラーを読む限り、すでに一部のコマンドがインポートされているから上書きを許可しないといけないらしい。
おそらくHyper-Vをコマンドレッドから色々弄ったときに入ったコマンドだろう。
指示通り-AllowClobberオプションを指定して再度コマンドを実行したら無事インストールできた。

次に、Vmwareのパートナープログラムみたいな奴に加入する設定を投入する。 これはやってもやらなくてもいい。が、途中で色々聞かれるのがめんどくさいので加入しておく。

Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $true

最後にVMwareのイメージビルダーをインポートする。

Import-Module VMware.ImageBuilder

イメージファイルの作成

以下リソースをDLしてくる

  • VMware-ESXi-7.0U3g-20328353-depot.zip
    • イメージファイルのオフラインバンドル版。公式サイトよりDL。
  • Net-Community-Driver_1.2.7.0-1vmw.700.1.0.15843807_19480755.zip
    • コニュニティドライバ。Intel NUCは公式のイメージファイルだけではNICを認識しないため。
  • nvme-community-driver_1.0.1.0-3vmw.700.1.0.15843807-component-18902434.zip
    • コニュニティドライバ。おなじくNVMeを認識しない可能性があると聞いたので

以下手順で、各ドライバを内包したISOイメージを作成する。

PS E:\VM\ESXi> Add-EsxSoftwareDepot -DepotUrl .\VMware-ESXi-7.0U3g-20328353-depot.zip

PS E:\VM\ESXi> Get-EsxImageProfile

Name                           Vendor          Last Modified   Acceptance Level
----                           ------          -------------   ----------------
ESXi-7.0U3g-20328353-no-tools  VMware, Inc.    2022/08/23 3... PartnerSupported
ESXi-7.0U3g-20328353-standard  VMware, Inc.    2022/09/01 0... PartnerSupported

PS E:\VM\ESXi> $ip = (Get-EsxImageProfile)[1]
PS E:\VM\ESXi> Add-EsxSoftwareDepot -DepotUrl .\nvme-community-driver_1.0.1.0-3vmw.700.1.0.15843807-component-18902434.zip
PS E:\VM\ESXi> Add-EsxSoftwareDepot -DepotUrl .\Net-Community-Driver_1.2.7.0-1vmw.700.1.0.15843807_19480755.zip
PS E:\VM\ESXi> $ip2 = New-EsxImageProfile -CloneProfile $ip -Name VMware-ESXi-7.0U3g -Vendor kwmtlab -AcceptanceLevel CommunitySupported
PS E:\VM\ESXi> Add-EsxSoftwarePackage -ImageProfile $ip2 -SoftwarePackage nvme-community,net-community

PS E:\VM\ESXi> Export-EsxImageProfile -ImageProfile $ip2 -FilePath .\VMware-ESXi-7.0U3g.zip -ExportToBundle
PS E:\VM\ESXi> Export-EsxImageProfile -ImageProfile $ip2 -FilePath .\VMware-ESXi-7.0U3g.iso -ExportToIso

ESXiをインストールするための準備はこれで終了。 ISOファイルをUSBに書き込んでNUCに挿した状態で起動する。

インストール

インストールを進めていると、途中でパープルスクリーンが出て作業が中断されてしまった。
内容を読んでいると、CPUのコアがどうのこうので怒られている様子。
今回購入したNUCはIntel第12世代のCPUを使用しているので、AlderLake特有のアーキテクチャによるものだと予想した。
以下サイトに同様の事象が存在したので、参考にして回避コマンドを追加してみる。 https://williamlam.com/2022/02/esxi-on-intel-nuc-12-extreme-dragon-canyon.html

cpuUniformityHardCheckPanic=FALSE

このコマンドによって、パープルスクリーンを突破してインストールが正常に完了した。

余談

ESXiがインストールされても、再起動を行うとまたパープルスクリーンが出現する。 永続的に回避するためには、以下コマンドを実行することが必要だった。

esxcli system settings kernel set -s cpuUniformityHardCheckPanic -v FALSE

古いPCをWindows11にアップデートする

Windows11に上げるための要件

  1. TPM2.0以上
  2. セキュアブートが有効であること

古いマザーボードでは、上記二機能が無効化されていることがあるのでUEFIメニューから有効化してあげる必要がある。

(そもそも当該機能が搭載されていないマザーボードの場合はどうしようもないので、諦めて買い換えるべし)


TPM

TPM(Trusted Platform Module)の有効化をUEFIメニューから有効化する。方法はマザーボードのメーカによって異なるので各メーカの公式サイトを参照するべし。

ついでに、マザーボードインテル系の場合、TPMではなくPPTという機能になっている場合がある。
PPTはファームウェア型のTPMであるので、この機能で代替できる。ただし、PPTを有効化すると次回起動時にWindowsのブートセクタに影響がでてブルースクリーンになることがある。
ファイルシステム全体が壊れているわけではないので、回復環境を経由すれば復旧するみたい。

セキュアブート

曲者。

UEFIメニューからセキュアブートを有効にする必要がある。
初期状態だとシステムモードという状態になっていてこの機能を有効化できないので、UEFIメニュー上でKeyを発行するとユーザモードに遷移してから設定を行う。

注意点

セキュアブートは「CSM」機能と競合を起こすため、セキュアブートを有効化したい場合CSMを無効化する必要がある。

CSMとは、レガシーモードのファイルシステムUEFI上で動かす為に必要な変換機能のこと。

ストレージのパーティションシステムにはMBR方式とGPT方式が存在し、現在はGPT方式が主流である。そしてMBR方式を採用しているファイルシステムをレガシーモードという(らしい)。

MBR方式はBIOS時代のパーティション方式であり、これはそのままではUEFI上では起動できない。

つまり、CSMが有効にないる状態から無効化すると、ファイルシステムを読み込めないことによりWindowsが起動すらしなくなる。

これの回避の為には、パーティションをGPT方式に変換してからCSMを無効化する必要がある。

powershellでパラメータ付きエイリアスを登録する

やりたいこと

powershellでもエイリアスを使いたい。(bashの.bashrcみたいなことをしたい)

やったこと

  • profileの記述
  • profileの実行

profileの記述

poweshellでのAliasの記述は以下のパスに記述される。 このパスは環境変数に登録されているので$PROFILEで呼び出し可能

PS C:\Users\XXXX> $PROFILE
C:\Users\XXXX\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1  

イメージ的にはこれがbashでいう.bashrcみたいなもの。。。ではなかった。※後述
このファイルの中にエイリアスに登録したい内容を記載することで実現できる。フォーマットは以下の通り。

function vm(){
  C:\WINDOWS\system32\vmconnect.exe localhost Ubuntu
}

例示してあるのはHyper-v上に構築したUbuntuをコマンド一発で起動するためのエイリアス。(いちいちHyper-vマネージャを起動するのが面倒だった。
ついでに、ここではコマンドにパラメータを渡しているためfunctionを宣言しているが、 パラメータが必要ないならもっと簡単にかける。

Set-Alias [エイリアス名] [コマンド]

profileの実行

powershellのprofleを.bashrcのように考えているとここで躓いた。profileにエイリアスを記載しても反映されないのである。
これは考えてみたら当たり前で、実態は「Microsoft.PowerShell_profile.ps1」であるから。
ps1ってこれスクリプトファイルやん。だったら実行してどこかに読み込ませるんだと思ってさっくり実行。

PS C:\Users\XXXX> .$PROFILE

なにもエラーがでなければ無事読み込みが完了しているのでエイリアスが実行できるはず。
普段スクリプト動かさない人だとExecutePlicy関係で怒られるかもしれないので、そのときはスクリプトの実行権限与えて再実行すべし、以上。

Hyper-V上に構築したUbuntu20.04の画面サイズを大きくする

目的

Hyper-v上にGUIベースのLinuxマシンを構築するにあたって、全人類がぶち当たる問題が 「画面サイズが小さいこと」である。 今回はHyper-V上に立てたUbuntuを全画面表示できるように設定したのでここにその作業を残す。

使用環境

  • ホストOS:Windows10 Pro
  • 仮想化基盤:Hyper-v
  • ゲストOS:Ubuntu LTS 20.04

    やったこと

  • grubの設定の変更
  • linux-vm-toolsのインストール

grubの設定の変更

まずはじめにgrubの設定ファイルを編集する方法を取った。。
結論から言うと、これは効果なし。
どうやらこの手法が有効なのはUbuntu16系までらしい。残念。
一応やったことを残しておくと、/etc/default/grubファイルを書き換えました、なむ。

sudo vi /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT の行の末尾に video=hyperv_fb:[変更したい解像度] を追記
sudo update-grub
sudo reboot

linux-vm-toolsのインストール

こっちの方法で解決した。 Ubuntuにxrdpプロトコルで接続するためにlinux-vm-toolsのインストールを行う

sudo apt install git
git clone https://github.com/microsoft/linux-vm-tools.git
cd linux-vm-tools/
git fetch --all
git pull origin pull/106/head:pull_106
git checkout pull_106
cd ubuntu/20.04/
sudo bash ./install.sh
sudo reboot

再起動後、もう一度shを叩くらしい(?)

cd linux-vm-tools/ubuntu/20.04/
sudo bash ./install.sh

これでUbuntu側の準備は完了。 仮想OS:をシャットダウンして、ホスト側のPowerShellで以下のコマンドを実行

Set-VM -VMName <VMの名前> -EnhancedSessionTransportType HvSocket

これで次回実行時xrdpセッションが立ち上がります。

補足

無事拡張セッション(xrdpセッション)が立ち上がったが、ログインして見ると画面が真っ暗。真っ黒。なんで。
原因を調べてみると、Hyper-vの基本セッション上でログインしているユーザを使用してxrdpでログインしたことが原因。
さっくり基本セッションからログオフすると無事画面が表示された。
ちゃんとログオフしておくか、別のユーザを使用してxrdpログオンしましょう。
ついでに、xrdp有効化にしてから基本セッションでターミナルが開かなくなるという致命的な状況になった。が、xrdp経由だとターミナルは使えるので一旦放置している。
いつかこれが原因でドツボに嵌りそうでこわい。

API GatewayでAPIにIAM認証をかけて、SigV4署名を作成してPOSTを実行する

API GatewayでIAM認証を設定して、SigV4署名を実装する機会があったので、備忘録としてまとめる。

IAM認証とは

AWS APIGatewayのAPIを叩くときに使用する認証方法のうちの一つ。事前に権限を絞ったIAMユーザを用意しておき、APIの実行の際にIAMユーザの認証を要求する。認証されたIAMユーザが当該のAPIの実行を認可されている場合に、正常にAPIが応答を返す。

SignV4署名とは

上記IAM認証を実現するための手法。IAMユーザの認証にはアクセスキーとシークレットキーが必要だが、シークレットキーを外部にハードコーディングする訳にもいかないのでSignV4署名を発行して、署名をヘッダに入れることで認証を行う。

注意点

  • Signv4署名のハッシュはリクエスト全体のハッシュを計算する
    SignV4署名の際、IAMユーザの認証のみではなくリクエスト全体の認証も同時に行う。
    つまり、APIを叩く際のリクエストすべてに対して署名を発行することで、実際にAPIを叩くときには署名発行時と全く同じリクエストであるかを確認しているような挙動となる。
    このリクエスト全体というのは、メソッドに種類や(POSTやPUTSメソッドを使用する場合)ペイロードの中身も含まれる。(最初このことを知らなくて多くの時間を無駄にした。。。)
    署名発行の際にはちゃんとペイロードの情報を受け渡しておこうねという訳である。
  • 署名の際に受け渡すデータは文字列であることが必要
    実際にPOSTメソッドでAPIを叩くとき、ペイロードJSON形式で作成することが多くあると想定される。
    上記のペイロードを署名発行の際に受け渡すにあたって、JSONオブジェクト形式では署名発行時にエラーが発生する。 署名発行時にはJSON.stringfyなどを使用して文字列形式にしてから署名を作成することでこのエラーを回避した。

    ソースコード

    以下に、Node.jsで作成したSginv4署名のソースコードを記載する。 なお、この記事/コードを書くにあたって以下のブログを参考にした。 dev.classmethod.jp

const core = require('aws-sdk/lib/core');
const aws = require('aws-sdk');

// アクセスキーとシークレットアクセスキーを設定
const accessKey = 'XXXXXXXXX';
const secretKey = 'XXXXXXXXX';
const credential = new aws.Credentials(accessKey, secretKey);

exports.handler = async (event) =>{
    let method = event.method;
    let url = event.url;
    const res = main(url,method,event.requestBody);
    return res; 
};
function main(apiurl,method,requestBody) {
    // サービス名は、API GatewayのAPIの場合は、execiute-api固定です。
    const serviceName = "execute-api"; 
    // Signers.V4クラスのコンストラクタに渡すオプションを作成します。
    const options = {
        // api gatewayのURL
        url: apiurl,
        headers: {}
    };
    // api gatewayのURLからホスト、パス、クエリストリングを抽出
    const parts = options.url.split('?');
    const host = parts[0].substr(8, parts[0].indexOf("/", 8) - 8);
    const path = parts[0].substr(parts[0].indexOf("/", 8));
    const querystring = parts[1];

    // V4クラスのコンストラクタの引数に沿う形でoptionsを作成
    const now = new Date();
    options.headers.host = host;
    options.pathname = () => path;
    options.methodIndex = method;
    options.search = () => querystring ? querystring : "";
    options.region = 'ap-northeast-1';
    options.method = method;
    if(method == "POST"){
         options.body = requestBody;
         options.headers['Content-Type'] = "application/json";
         }
    
    // V4クラスのインスタンスを作成
    const signer = new core.Signers.V4(options, serviceName);
    // SigV4署名
    signer.addAuthorization(credential, now);
    //署名されたヘッダーを出力
    console.log(options.headers);
    const response = {
        statusCode: 200,
        // body: JSON.stringify(array2responseData(responseArray))
        body: options.headers
    };
    return response;
}

このlamdaにPOSTする内容は以下の通り

{
  "method": "XXXX",
  "url": "XXXXXXXXX"
  "requestBody":XXXX
}