ユーザが利用する画面では, Carrierwaveを利用してS3に配置している画像のURLをそのままimgタグで指定し表示する実装をしていた.
そのため, URLさえ分かっていればどこにでも表示することができる.
意図しない形で画像を他サイトで利用されたりは困るが, 画像自体が他の人に見られて困るものではない.
しかし, 本人確認書類の画像などは他の人に閲覧されては困る部類である.
だが, CSなど開発者以外の人が管理者画面などで閲覧したいため, ただただプライベートにすれば良いというものではない.
調べたときにいい感じの記事がなかったので残しておく.
僕の構成の場合は以下のような対応をした.
とりあえず, Block public access
は全てOFFにする.
そしてBucketポリシーを以下のように設定.
1つ目のStatementでは, Bucketの中身の全てを匿名ユーザでも閲覧できるように設定.
2つ目のStatementでは, 特定のディレクトリへのアクセスを特定のユーザ以外アクセスできないように設定.
【Bucket】
┗ UUID(ユーザ識別子)
┣ ユーザのプロフィール画像を保持するディレクトリ
┣ ユーザの投稿した画像を保持するディレクトリ
┣ ユーザの投稿した動画を保持するディレクトリ
┗ private ※このprivateディレクトリ配下はpublicにしたくない
┗ 本人確認書類の画像を保持するディレクトリ
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::【Bucket名】/*"
},
{
"Sid": "BlockReadPrivateDirectory",
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::【Bucket名】/*/private/*",
"Condition": {
"StringNotEquals": {
"aws:username": "【IAMユーザ名】"
}
}
}
]
}
バケットポリシーは上から順番に評価され, 上書きされると思っていたため,
{
"Sid": "AllowUserRead",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::【IAMユーザの数字】:user/【IAMユーザ名】"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::【Bucket名】/*/private/*"
}
みたいなことをやっていた...
うまくいかなくて調べたら, 初めて評価優先度があることを知った...
以下の記事はとても参考になりました.
【AWS S3】S3 bucket policy を使ったアクセス制限方法 Effectの評価優先度を考える
S3バケットへのアクセスを特定IAMユーザにだけ許可して他は弾く
誰でも見ても良い画像に関しては, Carrierwaveが作成してくれたURLを利用する.
問題は匿名ユーザでは閲覧できないように設定した画像.
こちらは今まで通りURLを渡して表示とはいかない.
軽く調べても, ほとんどの記事がURLを利用して表示させているため, 他の表示手段を残しておこうと思う.
gem 'aws-sdk-s3'
Aws.config.update(
{
credentials: Aws::Credentials.new(
【S3に接続できるIAMユーザのアクセスキーID】,
【S3に接続できるIAMユーザのシークレットアクセスキー】
),
region: 【接続したいS3があるリージョン】
}
)
※わかりやすく直接文字列で記載しているところがあるが, 実際はオブジェクトから取得したり, 環境変数から取得する部分があるので, 適時解釈してください.
# Carrierwaveから取得した, S3に配置している画像へのURL
url = "https://hoge.s3-ap-northeast-1.amazonaws.com/hogehoge.png"
# S3の画像の配置場所を取得
s3_object_key = url.delete("https://【バケット名】.s3-ap-northeast-1.amazonaws.com/")
# S3のオブジェクト取得
s3_object = Aws::S3::Client.new.get_object(bucket: 【S3のバケット名】, key: s3_object_key)
# Base64に変換
base64_image = Base64.encode64(s3_object.body.read)
上記で作成した画像情報をAPIで返したり, そのまま表示する場合は以下のようにすることで表示することができる.
<img src="data:image/png;base64,<%= base64_image %>" class="rounded float-left" width="200" height="200" />
他の言語だとこういった記事はよく見かけた記憶があるが, Railsだと見当たらなかったので記事にしてみた.
Twitterフォロー待ってます!