1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

S3へのアクセスを特定の拡張子に限定する方法

1
Posted at

AWS CDKでS3の拡張子を.zipに限定する方法

概要

AWS S3バケットで特定の拡張子のファイルのみアップロードを許可したい場合、標準機能では制限できませんが、バケットポリシーを使って実現できます。

この記事では、明示的拒否(Explicit Deny)とNotResourceを組み合わせて、.zipファイル以外のアップロードを事前にブロックする方法を解説します。

解決アプローチの比較

方法 タイミング コスト 実装複雑度 ファイル内容検証
バケットポリシー アップロード前 無料 不可(キー名のみ)
Lambda + S3イベント アップロード後 Lambda実行料金 可能

バケットポリシーによる事前制御は、コスト効率と即座性の観点で優れています。

アーキテクチャ

実装のポイント

バケットポリシーの動作原理

  1. 明示的拒否の優先: AWS IAMでは DENY > ALLOW の優先順位
  2. NotResource の活用: 「〜以外」の指定により、除外対象を定義
  3. マルチパートアップロード対応: 大容量ファイルの分割アップロードも制御

制御対象のS3アクション

actions: [
  's3:PutObject',               // 単一オブジェクトアップロード
  's3:CreateMultipartUpload',   // マルチパート開始
  's3:UploadPart',              // パーツアップロード
  's3:CompleteMultipartUpload', // マルチパート完了
  's3:AbortMultipartUpload',    // マルチパート中断
]

CDK実装

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as iam from 'aws-cdk-lib/aws-iam';

export class ZipOnlyBucketStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const bucket = new s3.Bucket(this, 'ZipOnlyBucket', {
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      encryption: s3.BucketEncryption.S3_MANAGED,
      enforceSSL: true,
    });

    // .zip以外のアップロードを明示的に拒否
    const denyNonZipPolicy = new iam.PolicyStatement({
      sid: 'DenyNonZipUploads',
      effect: iam.Effect.DENY,
      principals: [new iam.AnyPrincipal()],
      actions: [
        's3:PutObject',
        's3:CreateMultipartUpload',
        's3:UploadPart',
        's3:CompleteMultipartUpload',
        's3:AbortMultipartUpload',
      ],
      // 重要: NotResourceで「*.zip以外」を対象にする
      notResources: [bucket.arnForObjects('*.zip')],
    });

    bucket.addToResourcePolicy(denyNonZipPolicy);
  }
}

動作確認

# 成功例
aws s3 cp archive.zip s3://bucket-name/archive.zip

# 拒否例(AccessDenied)
aws s3 cp document.txt s3://bucket-name/document.txt
aws s3 cp image.jpg s3://bucket-name/images/image.jpg

テスト結果

ファイル名 結果 備考
data.zip 許可 拡張子が.zip
DATA.ZIP 拒否 大文字小文字を区別
file.zip.txt 拒否 最終拡張子が.txt
path/to/file.zip 許可 パス内の.zipも有効

カスタマイズパターン

大文字小文字両対応

notResources: [
  bucket.arnForObjects('*.zip'),
  bucket.arnForObjects('*.ZIP'),
  bucket.arnForObjects('*.Zip'),
],

複数拡張子の許可

notResources: [
  bucket.arnForObjects('*.zip'),
  bucket.arnForObjects('*.tar.gz'),
  bucket.arnForObjects('*.7z'),
],

特定プレフィックス配下のみ制限

const denyNonZipInUploads = new iam.PolicyStatement({
  sid: 'DenyNonZipInUploadsFolder',
  effect: iam.Effect.DENY,
  principals: [new iam.AnyPrincipal()],
  actions: ['s3:PutObject'],
  resources: [bucket.arnForObjects('uploads/*')],
  notResources: [bucket.arnForObjects('uploads/*.zip')],
});

IAMプリンシパル限定

const denyNonZipForRole = new iam.PolicyStatement({
  effect: iam.Effect.DENY,
  principals: [
    iam.Role.fromRoleArn(this, 'SpecificRole', roleArn)
  ],
  actions: ['s3:PutObject'],
  notResources: [bucket.arnForObjects('*.zip')],
});

高度な設定

条件キーを使った制限

const denyNonZipWithCondition = new iam.PolicyStatement({
  effect: iam.Effect.DENY,
  principals: [new iam.AnyPrincipal()],
  actions: ['s3:PutObject'],
  resources: [bucket.arnForObjects('*')],
  conditions: {
    StringNotLike: {
      's3:object-key': '*.zip'
    },
  },
});

MFA必須でのアップロード

const requireMFAForUpload = new iam.PolicyStatement({
  effect: iam.Effect.DENY,
  principals: [new iam.AnyPrincipal()],
  actions: ['s3:PutObject'],
  resources: [bucket.arnForObjects('*.zip')],
  conditions: {
    BoolIfExists: {
      'aws:MultiFactorAuthPresent': 'false'
    },
  },
});

トラブルシューティング

よくある問題

症状 原因 解決方法
期待通りに拒否されない ポリシー評価順序の問題 他の許可ポリシーとの競合確認
正規表現が効かない S3はワイルドカード(*)のみ StringLike条件を使用
マルチパートが途中で失敗 一部アクションの制限漏れ 全マルチパート関連アクション追加

デバッグ方法

// CloudTrailでポリシー評価ログ確認
const trail = new cloudtrail.Trail(this, 'S3AccessTrail', {
  includeGlobalServiceEvents: false,
  isMultiRegionTrail: false,
  eventRuleActions: [
    's3:PutObject',
    's3:CreateMultipartUpload',
  ],
});

制限事項と考慮点

ファイル内容の検証なし

バケットポリシーはオブジェクトキーのみ評価するため、悪意のあるファイルが.zip拡張子で偽装される可能性があります。

大文字小文字の区別

デフォルトでは大文字小文字を厳密に区別します。必要に応じて複数パターンを定義してください。

ワイルドカードの制限

S3のリソース指定では、正規表現ではなくワイルドカード(*, ?)のみ使用可能です。

まとめ

AWS CDKとバケットポリシーを使用することで、S3バケットに特定拡張子のファイルのみをアップロードできるよう制限できます。

実装のキーポイント:

  • NotResourceを使った除外指定
  • 明示的拒否による確実な制御
  • マルチパートアップロードも含めた包括的な制御

この方法は追加コストなしで即座にブロックでき、セキュリティポリシーの実装に有効です。ただし、ファイル内容の検証が必要な場合は、Lambdaによる後処理との組み合わせを検討してください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?