0
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?

「リンクを知っている人だけ」にHTMLを公開する 〜S3 + CloudFront 署名付きURLを初学者向けにやさしく解説(料金つき)〜

0
Posted at

S3+CloudFront署名付きURLの全体像(各要素がどのAWSリソースかを矢印で注記)

この記事のゴール

1枚のHTMLファイルを、URLを知っている人だけが見られる形でネットに公開したい。
でもコストはかけたくないし、閲覧期間も自分で設定したい、そしてせっかくなのでAWSの勉強もしたい。

この記事では、その実現方法として 「S3 + CloudFront 署名付きURL」 を選び、構築するまでの流れを書きます。あわせて「他の方法はなぜ採用しなかったのか」「途中の設計判断(WAF・料金プラン・ファイル名・独自ドメイン・鍵の置き場所)をどう決めたのか」も、初学者向けに理由つきで残します。

この記事の構成・設計判断は、実際に手を動かしながら整理したものです。専門用語にはたとえ話を添えています。


まずは全体像を「お姫様と城」でイメージ

技術用語を並べる前に、完成形をたとえ話で掴みましょう。

最初に載せたイラスト画像が、どのAWSリソースに相当するかを対応させると次のとおりです。

イラストの要素 対応するAWSリソース/概念
🏰 門のない密室のお城 非公開のS3バケット(入口がない=直接アクセス不可)
👑 お城の中のお姫様 公開したいHTML(S3オブジェクト)
🛎️ お城の前の受付 CloudFront(唯一の窓口・CDN)
🔑 受付が照合する「印章の見本」 公開鍵(CloudFrontに登録/キーグループ)
📜 封蝋つきの招待状 署名付きURL(手元の秘密鍵で封をしたもの)
✅ 招待状を見せて中へ通る人 正しい署名つきURLでアクセス → 表示OK(200)
❌ 受付で赤い×で断られる人 署名なしのアクセス → 403で拒否(MissingKey)
☁️ 背景の空と雲 インターネット/世界中に広がるCDN配信網
  • 公開したいHTML = お姫様
  • お姫様が住む 門のない密室の城 = 非公開のS3バケット(外から入る入口がない)
  • 城の前にある 唯一の公式受付 = CloudFront(世界中に窓口を持つ案内所)
  • 「この受付の人だけ奥に通す」という約束 = OAC(後述)
  • 王様の印章(門外不出) = 秘密鍵
  • 受付に預けた 印章の見本 = 公開鍵
  • 封蝋つきの招待状(期限つき) = 署名付きURL

閲覧者は、王様(あなた)が印章で封をした招待状を持って受付(CloudFront)に行きます。受付は印章の見本と照合し、本物で期限内なら奥のお姫様(HTML)に会わせます。招待状を持たない人は門前払い。これがこれから作る仕組みの正体です。


そもそもどんな方法がある?(採用・不採用の比較)

「リンクを知ってる人だけに公開」を実現する方法はいくつもあります。最初に候補を並べ、なぜCloudFront署名付きURLにしたかを書きます。

候補A:Cloudflare Pages / Netlify / Vercel などのホスティングサービス

ドラッグ&ドロップで即公開でき、HTTPSもCDNも自動。一番ラク。
不採用の理由:手軽すぎて「AWSの勉強がしたい」という今回の目的に合わない。また、ランダムURLでの限定公開は「推測されにくい」だけで、暗号的に守るわけではない。

候補B:GitHub Pages

リポジトリにpushするだけ。
不採用の理由:URLが推測されやすく、リポジトリ名から公開がバレやすい。やはり「URLを知ってる人だけ」を厳密には守れない。

候補C:S3 の静的ウェブサイトホスティング(公開設定)

定番。ただし「URLを知ってる人だけ」を厳密にやろうとすると、バケットを公開する必要があり、設定ミスで全公開事故が起きやすい
不採用の理由:厳密な限定公開には向かない。事故リスクが高い。

候補D:S3 署名付きURL(presigned URL)

S3が直接発行する期限つきURL。新しい鍵を作らず、既存のAWS認証情報で署名できるのが利点。
一部不採用の理由:CloudFrontを通らずS3直アクセスになるため、CDNや拡張性の利点がない。さらに 有効期限が最大7日。今回は「もっと長い期限」「CDN経由」「AWSをちゃんと学ぶ」を重視したので見送り。
(※「鍵を作りたくない」が最優先なら、これは有力な代替案です)

候補E:CloudFront 署名付きURL ← ★採用

S3を完全非公開にしてCloudFront経由のみアクセス可能にし、RSA鍵で署名したURLを知っている人だけが開ける方式。
採用の理由

  • URLを推測されても署名がなければ403で拒否=厳密な「リンクを知ってる人だけ」を満たす
  • 有効期限を自由に長く設定できる(presignedの7日制限がない)
  • CDN・HTTPS・独自ドメイン拡張など発展性がある
  • 構成要素(S3 / CloudFront / OAC / 署名)がAWS学習の王道

「厳密さ × AWS学習 × 発展性」のバランスで、今回はこれを選びました。


構築手順(ダイジェスト)

実際の手順を、初学者がたどれる粒度で並べます。リージョンは東京(ap-northeast-1)、CloudFrontはグローバルです。

手順0:署名用のRSA鍵ペアを作る

ローカルのターミナルで2つの鍵を作ります。

openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
  • private_key.pem … 署名用の秘密鍵。王様の印章。絶対に公開しない
  • public_key.pem … CloudFrontに登録する公開鍵。印章の見本。

🔑 RSA鍵ペアとは:対になった2つの鍵。片方(秘密鍵)で封をし、もう片方(公開鍵)で「本物の封か」を確認できる。封はできるが偽造はできない、という非対称性がミソ。

手順1:S3バケットを非公開で作る

S3で新しいバケットを作成。「パブリックアクセスをすべてブロック」はONのまま(デフォルト)。これで「門のない城」になります。

S3バケット作成:パブリックアクセス全ブロック+ACL無効(非公開)

手順2:HTMLをアップロード

作ったバケットにHTMLを1つアップロード。これでお姫様が城に入りました(まだ誰も会えません)。

手順3:CloudFrontにパブリックキーを登録

CloudFront →「キー管理 > パブリックキー」で、手順0の public_key.pem を貼り付けて登録。発行される キーID(例:KXXXXXXXXXX をメモ。これが署名時に使う「印章の見本のラベル」。

CloudFrontパブリックキー:RSA 2048として登録

手順4:キーグループを作る

「キー管理 > キーグループ」で、登録した公開鍵を含むグループを作成。CloudFrontは「このグループの鍵で署名されていればOK」と判断します。

手順5:CloudFrontディストリビューションを作る

  • 料金プランPay as you go(従量課金)を選択(理由は後述)
  • オリジン:手順1のバケットを選択し、「Allow private S3 bucket access to CloudFront」にチェック
    → これが OAC。CloudFrontが自動でバケットポリシーを書き換え、「このCloudFront経由のときだけS3を読める」ようにしてくれる
  • WAF:今回は無効(理由は後述)

🛡 OAC(Origin Access Control)とは:S3を完全非公開のまま、「この特定のCloudFrontだけは読んでよい」と許可する仕組み。城が「この受付の人だけ通す」と決めるのに相当。設定後、S3のバケットポリシーには自動でこんな許可が入ります(一部伏字)。

{
  "Effect": "Allow",
  "Principal": { "Service": "cloudfront.amazonaws.com" },
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::<バケット名>/*",
  "Condition": { "StringLike": { "AWS:SourceArn": "arn:aws:cloudfront::<アカウントID>:distribution/<ディストリビューションID>" } }
}

手順6:ビヘイビアで「署名付きURLを必須」にする

ディストリビューションの「ビヘイビア」を編集し、「ビューワーのアクセスを制限する=Yes」、信頼された認可タイプ=Trusted key groups、で手順4のキーグループを指定。これで「招待状(署名)がないと通さない」受付になります。

ビヘイビア編集:ビューワーのアクセスを制限する=Yes/Trusted key group

手順7:署名付きURLを生成して動作確認

秘密鍵で署名したURLを発行します(仕組みは「ポリシーJSONをRSA-SHA1で署名し、URLのクエリに付ける」)。

  • 署名つきURL → HTMLが表示される ✅
  • 署名なしURL403 / MissingKey(招待状なしは門前払い)✅

この2つが確認できれば完成です。

署名なしURLは 403 / MissingKey で拒否される


細かい設計判断と「採用・不採用」の理由

ここが初学者にいちばん役立つ部分かもしれません。途中で迷ったポイントと、その決め方です。

① 料金プラン:Pay as you go を採用 / 定額プランは不採用

CloudFrontの作成時に「定額プラン($0〜$1,000/月)」と「Pay as you go(従量課金)」を選べます。

  • 採用:Pay as you go … 全機能が使え、後述の無料枠でほぼ$0に収まる。今回のような小規模・低トラフィック用途に最適。
  • 不採用:定額プラン … WAFやサーバーレス機能など“全部入り”だが、個人の限定公開には過剰。月額固定が発生しうる。

② WAF:不採用

WAF(Web Application Firewall)は、城の周りに傭兵を雇って怪しい攻撃を防ぐようなもの。

  • 不採用の理由:今回は署名付きURL(招待状)でアクセス自体を絞っているので、追加の傭兵は不要。作成画面の見積もりでも 月14 USD相当 と表示され、コスト的に見合わない。

🧱 WAFとは:Webへの不正アクセス(SQLインジェクション等)をフィルタする仕組み。公開Webサイトには有効だが、限定公開には必須ではない。

③ ファイル名:日本語名 → ランダムなASCII名に変更

最初は日本語ファイル名のままでしたが、これだとURLが
...com/%E7%89%A9%E4%BB%B6...(日本語をエンコードした長い文字列)になってしまう。

  • 採用ab6b974f.html のような中身が分からないランダム名に変更。
  • 注意:末尾の ?Expires=...&Signature=...&Key-Pair-Id=...署名の本体なので消せません(これが安全性そのもの)。

④ 独自ドメイン:検討したが不採用

cloudfront.net ではなく files.example.com のような独自ドメインにしたい、という案も検討。

  • 不採用の理由:独自ドメイン化には ドメインの保有が前提。今回のアカウントにはドメインもRoute 53ホストゾーンも無かった。
  • 「無料でドメインは?」も検討したが、昔の無料TLD(Freenom系)は実質終了、無料サブドメイン系も第三者サービスのアカウントが必要で実用性・信頼性が低い
  • 結論:cloudfront.net のままで十分(無料・HTTPS付き)。独自ドメインは、将来ドメインを取得したら ACM証明書(us-east-1)+ CloudFrontのCNAME追加 + DNS設定、で対応可能。

⑤ 秘密鍵の置き場所:ローカル → AWSへ。Parameter Store(無料)を採用

「ローカルの鍵ファイルを無くしそう/忘れそう。AWSで管理したい」という要望から、鍵の保管先をAWSに移しました。

  • 採用:SSM パラメータストア(SecureString) … 暗号化保存でき、標準枠は無料
  • 不採用:Secrets Manager … 専用サービスで機能は豊富だが 1シークレットあたり月約$0.40。今回の用途なら無料のParameter Storeで十分。
  • 実行役:CloudShell … URL生成(署名)を動かす場所として採用。ブラウザ内のターミナルで、opensslawsコマンドも最初から使え、追加インストール不要。Lambdaも候補だったが、署名ライブラリの同梱が手間なので、学習・手軽さでCloudShellを選択。

これで「秘密鍵=Parameter Store」「生成スクリプト=CloudShell」となり、ローカルには何も残さずURLを作り直せます。CloudShellなら次のように1コマンド。

bash ~/gen_url.sh 90   # 90日有効なURLを生成

🗝 SecureStringとは:パラメータストアで値をKMS(鍵管理サービス)で暗号化して保存する形式。金庫に入れて預ける感覚。
💻 CloudShellとは:AWSコンソール内で使える無料のLinuxターミナル。自分のPCに何も入れずにコマンドを実行できる。

パラメータストア:SecureStringで秘密鍵を保管(標準枠は無料)


料金まとめ(2026年6月時点)

サービス 課金 今回の目安
S3 ストレージ 保存量課金(東京 約$0.025/GB・月) 数MBなので月$0.01未満
CloudFront 転送・リクエスト課金 毎月1TB転送+1000万リクエストが恒久無料枠。個人共有なら実質**$0**
WAF 今回無効 $0(有効なら月14 USD相当)
SSM パラメータストア 標準枠は無料 $0
(比較)Secrets Manager $0.40/シークレット・月 不採用
ACM 証明書 無料 (独自ドメイン化する場合のみ)
(参考)独自ドメイン 登録 年12〜15 USD、Route 53ホストゾーン 月$0.5 今回は不要

待機コスト(使っていなくても発生する固定費)がほぼ無いのがこの構成の強み。アクセスが少なければ月額ほぼゼロです。


用語集(やさしい版)

  • S3:ファイルを置くAWSの倉庫。今回は「門のない城」として完全非公開に。
  • オブジェクト:S3に置いた個々のファイル(今回のHTML=お姫様)。
  • CloudFront:世界中に拠点を持つ配信網(CDN)。今回は「唯一の公式受付」。
  • OAC:S3を非公開のまま「この受付だけ通す」と許可する仕組み。
  • 署名付きURL:秘密鍵で封をした、期限つきの「招待状」。これを持つ人だけが開ける。
  • 鍵ペア(公開鍵/秘密鍵):封をする印章(秘密)と、本物か確かめる見本(公開)のセット。
  • キーグループ:CloudFrontが「この公開鍵の署名なら信頼する」とまとめた束。
  • WAF:怪しいアクセスを弾く傭兵。今回は雇わず。
  • パラメータストア(SecureString):秘密の値を暗号化して預けるAWSの金庫(無料枠あり)。
  • CloudShell:ブラウザ内の無料Linuxターミナル。鍵を取り出して署名する作業場。

まとめ

  • 「URLを知っている人だけ」を厳密にやるなら、CloudFront署名付きURLが王道。
  • S3は非公開のまま、CloudFront(OAC)経由のみ許可。署名がないと403
  • WAFは無効、料金はPay as you goで、無料枠内ならほぼ$0
  • ファイル名はランダムASCIIにして個人情報を隠す
  • 秘密鍵はパラメータストア(無料)に、生成はCloudShellで。ローカルに鍵を残さない運用に。

一番大事なのは、秘密鍵(印章)を絶対に他人に渡さないこと。これさえ守れば、招待状を配った相手だけがお姫様に会えます。

0
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
0
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?