行動すれば次の現実

ほどよくモダンなシステム開発を目指しています。メインテーマは生産性、Ruby、Javascriptです。

CarrierWaveでAWS S3 にファイルをアップロードしようとするときに発生する403エラーについて | Rails

carrierwaveとfog-awsを使用してファイルをアップロードしようとしたところ以下のエラーが発生しました。

 Excon::Error::Forbidden:
       Expected(200) <=> Actual(403 Forbidden)

このエラーの原因と対処法を整理しましたので同様のことでお悩みの方はぜひ参考にしてみてください。

エラーの原因

AWS S3のバケットには「ブロックパブリックアクセス」について設定する項目があります。 ブロックパブリックアクセスとは、リソースへのアクセスについてセキュリティ高めることができる設定です。 説明すると長くなるので、この記事では説明は省略します。

ブロックパブリックアクセスについてはこのの記事が参考になると思います。 aws s3のパブリックアクセスについてまとめる - Qiita

デフォルトではブロックパブリックアクセスは全てオンになっています。

f:id:furu07yu:20211105143552j:plain
ブロックパブリックアクセス

ブロックパブリックアクセスが全てオンなのにも関わらず、carrierwave.rbにはfog_public = trueが設定されています。 fog_publicとはリソースを公開する場合はtrue、公開しない場合はfalseを設定します。(デフォルトはtrueが設定されます)

S3バケットのブロックパブリックアクセスとcarrierwaveの設定内容が矛盾しているためエラーが発生しています。

require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'

CarrierWave.configure do |config|
  config.storage :fog
  config.fog_provider = 'fog/aws'
  config.fog_directory = ENV['S3_BUDGET_NAME']
  # アップロードしたリソースを公開する場合はtrue
  config.fog_public = true
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: ENV['S3_BUDGET_ACCESS_KEY'],
    aws_secret_access_key: ENV['S3_BUDGET_SECRET_ACCESS_KEY'],
    region: 'ap-northeast-1',
    path_style: true
  }
end

対処方法

対処方法は2つあります。いずれかの方法を採ると良いでしょう。

fog_public = falseにする

fog_public = falseにすることで、リソースを非公開状態でアップロードできます。 ブロックパブリックアクセスは有効なままですのでバケット内のセキュリティが保たれます。

デメリットとしては、S3上のリソースをブラウザ等からURLを指定して直接ダウンロードすることが出来ないことです。 そのため、ダウンロード用のアクションを配置するなどして、サーバー上でダウンロードしてからクライアントに返却する必要があります。

しかしながら、fog-awsではfog_publicがfalseの場合、fog_authenticated_url_expirationというオプションが自動で有効になります。 600秒間有効な署名付きURLを自動で発行するという機能です。

これを活用することで「URLを指定して直接ダウンロードできない」というデメリットがほとんど解決されるのではないかと思います。

ブロックパブリックアクセスをオフにする(非推奨)

もう一つの方法はブロックパブリックアクセスをオフに変更することです。 オフにすることで、fog_public = trueとの矛盾がなくなり、エラーが発生しなくなります。

しかし、セキュリティホールが発生してしまうリスクが伴いますので個人的にはオススメしません。 セキュリティ要件に応じて、アップロードリソース毎にパブリックアクセスの可否を設定する必要が生じるので管理コストも増えてしまいます。

参考

carrierwave/fog.rb at master · carrierwaveuploader/carrierwave · GitHub