[アップデート]AWS LambdaでAWS Signerを利用してコードの署名ができるようになったので試してみた

Lambdaでコード署名ができるようになりました。AWS Signerを利用して署名するフローを入れることにより、適切なコード更新フローを組むことが可能です。
2020.11.30

こんにちは、臼田です。

みなさん、セキュアな開発していますか?(挨拶

今回はAWS Lambdaでコードの署名が可能になりましたので試していきたいと思います。

概要

以下の発表でAWS Lambdaのコードを署名して、デプロイ時にそれを検証することが可能になりました。

AWSLambdaの信頼と整合性の制御であるコード署名を発表

機能としては、以下のようなことが可能です。

  • AWS Signerを利用してコードに署名を行う
  • Lambda関数で署名の設定を行う
  • 検証検証ポリシーとして、署名が行われていないコードのデプロイ時の扱いは以下2種類選択する
    • Warn: デプロイは成功するがCloudWatchメトリクスに記録する
    • Enforce: デプロイをブロックする

これによりコードが適切な署名フローを元に作成・更新されたものかを確認することができます。

やってみた

詳細は以下のブログで解説されています。

New –コード署名、AWSLambdaの信頼と整合性の制御| AWSニュースブログ

ざっくり手順としては以下のようになります。

  • LambdaのコードをS3にアップロードする
  • AWS Signerで署名プロファイルを作成する
  • AWS Signerで署名ジョブを実行しコードを署名する
  • Lambdaのコード署名設定を行う
  • Lambda関数の作成時にコード署名設定を選択する

ついでにEnforce時のアップロードの失敗やWarn時のCloudWatchメトリクスも見ていきます。

コードに署名する

まずLambdaのコードをzipにしてS3にアップロードしておきます。ここで非常に重要なポイントがあります。

コードをアップロードするS3はバージョニングを有効化する必要があります。

AWS Signerで署名を行う際のStartSigningJobのAPIドキュメントにも書かれています。

Your S3 source bucket must be version enabled.

これが最初のハマりどころなので気をつけましょう。

S3 URIとバージョンIDを控えておきます。

AWS Signerのコンソールにアクセスして署名プロファイルの作成をします。

署名プロファイルは名前と有効期限を設定します。有効期限は1日から135か月の署名有効期間を割り当てることができます。今回はデフォルトの135ヶ月にして作成します。

署名プロファイルを作成したら、実際に署名をします。

署名は署名ジョブにて行います。以下の署名ジョブ作成画面から作れると思うのですが、今回私の手元でうまく動かなかったのでAWS CLIで実行しました。

AWS CLIではaws signer start-signing-jobコマンドを実行します。

aws signer start-signing-job --source "s3={bucketName=your-bucket-bane,key=hello.zip,version=xxxxxxxxxxxxxxxxxxxxxxxxxxx}" --destination "s3={bucketName=your-bucket-name,prefix=hello-signed/}" --profile-name test_sign
{
    "jobId": "xxxxxxxxx-3410-4458-913a-xxxxxxxxxxx",
    "jobOwner": "999999999999"
}

--sourceは先ほど控えたバケット名、key、バージョンIDを入力します。

--destinationは署名したファイルを保存するバケット名とprefixを指定します。

--profile-nameは適当に。

指定したdestinationのprefixにファイルが保存されていることが確認できます。

署名ジョブ側でも保存したkeyなどが確認できます。

ダウンロードして解凍してみるとわかりますが、コード自体と一緒にPKCS7形式のファイルが格納されています。

続いてLambdaでコード署名設定を作成していきます。Lambdaのコンソールの左カラムから「Code signing configurations」を開き作成を開始します。

名前を適当に入れ、先程作成した署名プロファイルを選択します。署名検証ポリシーは先程説明したとおり2種類あります。今回はまずEnforceにします。

Lambda関数を作成します。詳細のコード署名部分でコード署名設定と署名済みのS3オブジェクトを指定します。

作成完了したらコード署名設定欄も表示されます。

これで署名したコードをアップロードできました。

Enforceで署名なしコードをアップロードした場合

それではEnforceで署名がないコードのアップロードが失敗するか確認します。

失敗しました。いいですね。

この場合は失敗する代わりにCloudWatch Logsにはメトリクスは出ません。

もし失敗を検知する場合には、CloudTrailに記録されるのでそれを検知するのが良さそうです。CodeVerificationFailedExceptionのエラーコードになります。

WarnでCloudWatch Logsへの記録を確認する

Lambdaのコード署名設定でWarnに変更します。

これでアップロードしてみます。更新されました。

CloudWatchメトリクスを見てみます。SignatureValidationErrorsとして記録されます。

活用方法

最後に活用方法について考察してみます。

元々このアップロードを見たときの私の感想として、攻撃者によるLambdaを利用した権限昇格の手法を一部ブロックできるのでは?というものがあったのですが、現状の機能では直接署名がないコードのアップロードを止めることはできません。

この機能で設定できるのは、更新時に署名されていないコードを止めたり検知することなので、新規のLambda関数作成についてコードの署名を強制することはできません。

IAM Policyでこれを強制するような表現が可能であればIAMやSCPで強制も考えられますが、そのあたりのアナウンスは公式から出ていないので、私の思っている活用方法はできなさそうです。

あくまで「組織は管理者と開発者の間で職務の分離を実装できます」というところの使い所になるかなーと思いました。

まとめ

Lambdaのコード署名をやってみました。署名されていないコードの更新を検知したり止めるのには利用できるので、より硬い運用に利用できると思います。

ぜひこのような運用を行う際には活用してみてはいかがでしょうか?