<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>S3 on Tarragon</title><link>https://tarrragon.github.io/blog/tags/s3/</link><description>Recent content in S3 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 26 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/s3/index.xml" rel="self" type="application/rss+xml"/><item><title>儲存上 IaC — S3 bucket 的安全與生命週期</title><link>https://tarrragon.github.io/blog/infra/05-core-services/storage-s3/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/infra/05-core-services/storage-s3/</guid><description>&lt;p>S3 bucket 描述的是物件儲存的存在、命名、加密設定、版本控制與存取政策。bucket 本身沒有重建代價意義上的狀態問題 — 困難在它「裝的東西」。空 bucket 可隨時重建，裝了正式資料的 bucket 與 RDS 一樣不可隨意 destroy。把安全設定與生命週期規則寫進 IaC，讓這些防線成為可版本控制、可審查的程式碼，而非散落在 Console 的隱性設定。&lt;/p>
&lt;h2 id="bucket-的四道安全防線">bucket 的四道安全防線&lt;/h2>
&lt;p>一個 S3 bucket 在 IaC 裡至少要描述四個獨立資源，各自對應一道防線。Terraform 把它們拆成獨立資源是設計選擇 — 每道防線可以單獨 review、單獨調整、單獨追蹤變更歷史。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_s3_bucket&amp;#34; &amp;#34;assets&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="n"> bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;acme-${var.env}-assets&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="n"> tags&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="n"> { service&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="n"> &amp;#34;cdn-origin&amp;#34;, env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">var&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">env&lt;/span> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_s3_bucket_versioning&amp;#34; &amp;#34;assets&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="n"> bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_s3_bucket&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">assets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="n"> versioning_configuration { status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Enabled&amp;#34;&lt;/span> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_s3_bucket_server_side_encryption_configuration&amp;#34; &amp;#34;assets&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="n"> bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_s3_bucket&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">assets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="k">rule&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="k">apply_server_side_encryption_by_default&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="n"> sse_algorithm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;aws:kms&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_s3_bucket_public_access_block&amp;#34; &amp;#34;assets&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="n"> bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_s3_bucket&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">assets&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="n"> block_public_acls&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="n"> block_public_policy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="n"> ignore_public_acls&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="n"> restrict_public_buckets&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="versioning">versioning&lt;/h3>
&lt;p>&lt;code>versioning&lt;/code> 讓物件的每次覆寫都保留前一版。誤覆寫時可以從版本歷史回退到前一個正確版本，誤刪時物件只是被標記為 delete marker、前一版仍然存在。這道防線對承載正式資料的 bucket 是必要的 — 沒有 versioning 的 bucket，一次誤操作就是資料永久遺失。&lt;/p>
&lt;p>versioning 開啟後會累積歷史版本的儲存量。搭配生命週期規則設定 &lt;code>noncurrent_version_expiration&lt;/code> 可以控制保留多少天的舊版本，避免儲存成本無限成長。這個天數是「保留能力」跟「儲存成本」的取捨 — 保留 30 天通常足以涵蓋發現問題到回退的時間差，受合規要求的資料則依規定延長。&lt;/p>
&lt;h3 id="server-side-encryption">server-side encryption&lt;/h3>
&lt;p>&lt;code>server_side_encryption&lt;/code> 確保物件在 S3 落地時加密。&lt;code>aws:kms&lt;/code> 使用 KMS 管理的金鑰，加密操作對應用程式透明 — 寫入時自動加密、讀取時自動解密，不需要改應用程式碼。選 &lt;code>aws:kms&lt;/code> 而非 &lt;code>AES256&lt;/code>（SSE-S3）的判斷依據是存取控制粒度：KMS 金鑰可以獨立設定 key policy，讓「誰能解密」這件事跟「誰能讀 bucket」分開管理，適合跨帳號或跨團隊的場景。&lt;/p>
&lt;p>使用 KMS 加密的 bucket 在跨帳號存取時，目標帳號除了要有 bucket 的讀取權限，還需要 KMS key 的 &lt;code>kms:Decrypt&lt;/code> 權限 — 少了這一步會拿到 &lt;code>AccessDenied&lt;/code>，錯誤訊息通常指向 S3 權限而非 KMS，排查時容易走錯方向。&lt;/p>
&lt;h3 id="public-access-block">public access block&lt;/h3>
&lt;p>&lt;code>public_access_block&lt;/code> 的四個布林全設 true，等於從 bucket 層級封死對外公開的可能。即使有人之後誤加了一條公開的 bucket policy 或 ACL，這個 block 也會擋住。它是一道兜底機制 — 擋的是設定錯誤，不是正常操作。&lt;/p>
&lt;p>靜態掃描工具（checkov / tfsec）會標記缺少 public access block 的 bucket。這正是&lt;a href="https://tarrragon.github.io/blog/infra/07-infra-as-pr/" data-link-title="模組七：infra 走 PR 流程與自動化護欄" data-link-desc="infra 變更走 PR → plan → review diff → 合併 → apply，配 fmt / validate / tflint / checkov / tfsec 與 Atlantis 自動化，讓基礎設施可審查、可回溯、可交接">模組七：infra 走 PR 流程&lt;/a>裡自動化護欄的典型攔截對象 — 漏設的 bucket 會在 PR 階段被擋下，而非部署到線上才發現。&lt;/p></description><content:encoded><![CDATA[<p>S3 bucket 描述的是物件儲存的存在、命名、加密設定、版本控制與存取政策。bucket 本身沒有重建代價意義上的狀態問題 — 困難在它「裝的東西」。空 bucket 可隨時重建，裝了正式資料的 bucket 與 RDS 一樣不可隨意 destroy。把安全設定與生命週期規則寫進 IaC，讓這些防線成為可版本控制、可審查的程式碼，而非散落在 Console 的隱性設定。</p>
<h2 id="bucket-的四道安全防線">bucket 的四道安全防線</h2>
<p>一個 S3 bucket 在 IaC 裡至少要描述四個獨立資源，各自對應一道防線。Terraform 把它們拆成獨立資源是設計選擇 — 每道防線可以單獨 review、單獨調整、單獨追蹤變更歷史。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket&#34; &#34;assets&#34;</span> {
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">  bucket</span> <span class="o">=</span> <span class="s2">&#34;acme-${var.env}-assets&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">  tags</span> <span class="o">=</span><span class="n"> { service</span> <span class="o">=</span><span class="n"> &#34;cdn-origin&#34;, env</span> <span class="o">=</span> <span class="k">var</span><span class="p">.</span><span class="k">env</span> }
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">}
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket_versioning&#34; &#34;assets&#34;</span> {
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">  bucket</span> <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">  versioning_configuration { status</span> <span class="o">=</span> <span class="s2">&#34;Enabled&#34;</span> }
</span></span><span class="line"><span class="ln">10</span><span class="cl">}
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket_server_side_encryption_configuration&#34; &#34;assets&#34;</span> {
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">  bucket</span> <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">rule</span> {
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">apply_server_side_encryption_by_default</span> {
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">      sse_algorithm</span> <span class="o">=</span> <span class="s2">&#34;aws:kms&#34;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    }
</span></span><span class="line"><span class="ln">18</span><span class="cl">  }
</span></span><span class="line"><span class="ln">19</span><span class="cl">}
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket_public_access_block&#34; &#34;assets&#34;</span> {
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="n">  bucket</span>                  <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="n">  block_public_acls</span>       <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="n">  block_public_policy</span>     <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="n">  ignore_public_acls</span>      <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="n">  restrict_public_buckets</span> <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">}</span></span></code></pre></div><h3 id="versioning">versioning</h3>
<p><code>versioning</code> 讓物件的每次覆寫都保留前一版。誤覆寫時可以從版本歷史回退到前一個正確版本，誤刪時物件只是被標記為 delete marker、前一版仍然存在。這道防線對承載正式資料的 bucket 是必要的 — 沒有 versioning 的 bucket，一次誤操作就是資料永久遺失。</p>
<p>versioning 開啟後會累積歷史版本的儲存量。搭配生命週期規則設定 <code>noncurrent_version_expiration</code> 可以控制保留多少天的舊版本，避免儲存成本無限成長。這個天數是「保留能力」跟「儲存成本」的取捨 — 保留 30 天通常足以涵蓋發現問題到回退的時間差，受合規要求的資料則依規定延長。</p>
<h3 id="server-side-encryption">server-side encryption</h3>
<p><code>server_side_encryption</code> 確保物件在 S3 落地時加密。<code>aws:kms</code> 使用 KMS 管理的金鑰，加密操作對應用程式透明 — 寫入時自動加密、讀取時自動解密，不需要改應用程式碼。選 <code>aws:kms</code> 而非 <code>AES256</code>（SSE-S3）的判斷依據是存取控制粒度：KMS 金鑰可以獨立設定 key policy，讓「誰能解密」這件事跟「誰能讀 bucket」分開管理，適合跨帳號或跨團隊的場景。</p>
<p>使用 KMS 加密的 bucket 在跨帳號存取時，目標帳號除了要有 bucket 的讀取權限，還需要 KMS key 的 <code>kms:Decrypt</code> 權限 — 少了這一步會拿到 <code>AccessDenied</code>，錯誤訊息通常指向 S3 權限而非 KMS，排查時容易走錯方向。</p>
<h3 id="public-access-block">public access block</h3>
<p><code>public_access_block</code> 的四個布林全設 true，等於從 bucket 層級封死對外公開的可能。即使有人之後誤加了一條公開的 bucket policy 或 ACL，這個 block 也會擋住。它是一道兜底機制 — 擋的是設定錯誤，不是正常操作。</p>
<p>靜態掃描工具（checkov / tfsec）會標記缺少 public access block 的 bucket。這正是<a href="/blog/infra/07-infra-as-pr/" data-link-title="模組七：infra 走 PR 流程與自動化護欄" data-link-desc="infra 變更走 PR → plan → review diff → 合併 → apply，配 fmt / validate / tflint / checkov / tfsec 與 Atlantis 自動化，讓基礎設施可審查、可回溯、可交接">模組七：infra 走 PR 流程</a>裡自動化護欄的典型攔截對象 — 漏設的 bucket 會在 PR 階段被擋下，而非部署到線上才發現。</p>
<p>定期用 CLI 掃一遍帳號內所有 bucket 的公開狀態，命中的每個 bucket 都要能回答「這個公開是故意的、理由是什麼」：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws s3api list-buckets --query <span class="s1">&#39;Buckets[].Name&#39;</span> --output text <span class="p">|</span> tr <span class="s1">&#39;\t&#39;</span> <span class="s1">&#39;\n&#39;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  <span class="k">while</span> <span class="nb">read</span> b<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="nv">status</span><span class="o">=</span><span class="k">$(</span>aws s3api get-public-access-block --bucket <span class="s2">&#34;</span><span class="nv">$b</span><span class="s2">&#34;</span> 2&gt;/dev/null <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>      jq -r <span class="s1">&#39;.PublicAccessBlockConfiguration | to_entries[] | select(.value==false) | .key&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$status</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$b</span><span class="s2">: </span><span class="nv">$status</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="k">done</span></span></span></code></pre></div><h2 id="生命週期規則">生命週期規則</h2>
<p>儲存成本隨物件數量與保留時間線性成長。生命週期規則讓 IaC 描述「某類物件多久後搬到更便宜的儲存層、再多久後刪掉」，把成本控制變成可版本控制的設定。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket_lifecycle_configuration&#34; &#34;assets&#34;</span> {
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">  bucket</span> <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="k">rule</span> {
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">    id</span>     <span class="o">=</span> <span class="s2">&#34;archive-old-logs&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">    status</span> <span class="o">=</span> <span class="s2">&#34;Enabled&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">    filter { prefix</span> <span class="o">=</span> <span class="s2">&#34;logs/&#34;</span> }
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">transition</span> {
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">      days</span>          <span class="o">=</span> <span class="m">30</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">      storage_class</span> <span class="o">=</span> <span class="s2">&#34;GLACIER_IR&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    }
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">    expiration { days</span> <span class="o">=</span> <span class="m">365</span> }
</span></span><span class="line"><span class="ln">14</span><span class="cl">  }
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="k">rule</span> {
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">    id</span>     <span class="o">=</span> <span class="s2">&#34;cleanup-old-versions&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="n">    status</span> <span class="o">=</span> <span class="s2">&#34;Enabled&#34;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">filter</span> {}
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="k">noncurrent_version_expiration</span> {
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="n">      noncurrent_days</span> <span class="o">=</span> <span class="m">30</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    }
</span></span><span class="line"><span class="ln">24</span><span class="cl">  }
</span></span><span class="line"><span class="ln">25</span><span class="cl">}</span></span></code></pre></div><h3 id="儲存層的取捨">儲存層的取捨</h3>
<p>S3 提供多個儲存層，各自在存取延遲與儲存單價之間取捨：</p>
<table>
  <thead>
      <tr>
          <th>儲存層</th>
          <th>存取延遲</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Standard</td>
          <td>毫秒級</td>
          <td>頻繁讀取的熱資料</td>
      </tr>
      <tr>
          <td>Standard-IA</td>
          <td>毫秒級</td>
          <td>不常存取但需要時立即讀到</td>
      </tr>
      <tr>
          <td>Glacier Instant</td>
          <td>毫秒級</td>
          <td>每季存取一次的歸檔</td>
      </tr>
      <tr>
          <td>Glacier Flexible</td>
          <td>分鐘到小時級</td>
          <td>稽核留存、年度查閱</td>
      </tr>
      <tr>
          <td>Glacier Deep Archive</td>
          <td>12 小時級</td>
          <td>法規留存、極少存取</td>
      </tr>
  </tbody>
</table>
<p><code>transition</code> 規則的日數設定要回推自業務需求：log 在除錯期間需要即時讀取（Standard），超過 30 天後幾乎只在事故回顧時才翻（Glacier Instant Retrieval 或 Standard-IA），超過一年可以淘汰或移到更深的歸檔層。把這些規則寫進 IaC，「為什麼 logs 只留一年」就是一個能在 PR 上被討論的決定，而非某人在 Console 點了不知道大家知不知道的設定。</p>
<h2 id="bucket-policy-與跨帳號存取">bucket policy 與跨帳號存取</h2>
<p>bucket policy 描述誰能對這個 bucket 做什麼操作，是 bucket 層級的存取控制。它跟 IAM policy 的差別在施力點：IAM policy 貼在身分上、定義「這個身分能做什麼」；bucket policy 貼在資源上、定義「這個 bucket 允許誰來」。兩者同時生效 — 一個請求要同時被身分端和資源端允許才會放行（除非有顯式 deny）。</p>
<p>跨帳號存取是 bucket policy 最常見的使用場景。一個帳號的 S3 bucket 要讓另一個帳號的 IAM role 讀取，需要兩端同時授權：bucket policy 允許那個 role 的 ARN，對方帳號的 IAM policy 也允許對這個 bucket 操作。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket_policy&#34; &#34;cross_account_read&#34;</span> {
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">  bucket</span> <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">  policy</span> <span class="o">=</span> <span class="k">jsonencode</span><span class="p">(</span>{
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">    Version</span> <span class="o">=</span> <span class="s2">&#34;2012-10-17&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">    Statement</span> <span class="o">=</span> <span class="p">[</span>{
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">      Sid</span>       <span class="o">=</span> <span class="s2">&#34;AllowCrossAccountRead&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">      Effect</span>    <span class="o">=</span> <span class="s2">&#34;Allow&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">      Principal</span> <span class="o">=</span><span class="n"> { AWS</span> <span class="o">=</span> <span class="s2">&#34;arn:aws:iam::111222333444:role/data-reader&#34;</span> }
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">      Action</span>    <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;s3:GetObject&#34;, &#34;s3:ListBucket&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">      Resource</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">arn</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="s2">&#34;${aws_s3_bucket.assets.arn}/*&#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">      <span class="p">]</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    }<span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  }<span class="p">)</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">}</span></span></code></pre></div><p>bucket policy 的常見陷阱是 <code>Principal: &quot;*&quot;</code> — 允許任何人存取。這跟 security group 的 <code>0.0.0.0/0</code> 是同一類風險。除了做為 CloudFront Origin Access Control（OAC）的配合設定，幾乎沒有合理場景需要把 Principal 設成 wildcard。checkov 的 <code>CKV_AWS_70</code> 規則專門攔這個。</p>
<p>把 bucket policy 寫進 IaC 的好處是每一條授權都有 PR 紀錄 — 誰在什麼時候加了一條跨帳號存取、為什麼加、reviewer 同意了沒有。散落在 Console 的 bucket policy 沒有這些追蹤，某天發現一條不認得的授權時，只能去翻 CloudTrail 猜它是什麼時候加的。</p>
<h2 id="事件通知">事件通知</h2>
<p>S3 事件通知讓 bucket 在物件被建立、刪除或還原時，自動觸發下游處理 — 寫入後自動縮圖、上傳後自動掃毒、刪除後自動通知。這些觸發關係寫進 IaC，讓「這個 bucket 會觸發什麼」成為可查詢的事實，而非散落在 Console 的隱性接線。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_s3_bucket_notification&#34; &#34;assets&#34;</span> {
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">  bucket</span> <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="k">lambda_function</span> {
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">    lambda_function_arn</span> <span class="o">=</span> <span class="k">aws_lambda_function</span><span class="p">.</span><span class="k">thumbnail</span><span class="p">.</span><span class="k">arn</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">    events</span>              <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;s3:ObjectCreated:*&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">    filter_prefix</span>       <span class="o">=</span> <span class="s2">&#34;uploads/&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">    filter_suffix</span>       <span class="o">=</span> <span class="s2">&#34;.jpg&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  }
</span></span><span class="line"><span class="ln">10</span><span class="cl">}
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_lambda_permission&#34; &#34;allow_s3&#34;</span> {
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">  statement_id</span>  <span class="o">=</span> <span class="s2">&#34;AllowS3Invoke&#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">  action</span>        <span class="o">=</span> <span class="s2">&#34;lambda:InvokeFunction&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="n">  function_name</span> <span class="o">=</span> <span class="k">aws_lambda_function</span><span class="p">.</span><span class="k">thumbnail</span><span class="p">.</span><span class="k">function_name</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">  principal</span>     <span class="o">=</span> <span class="s2">&#34;s3.amazonaws.com&#34;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">  source_arn</span>    <span class="o">=</span> <span class="k">aws_s3_bucket</span><span class="p">.</span><span class="k">assets</span><span class="p">.</span><span class="k">arn</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">}</span></span></code></pre></div><p>事件通知的兩個配置常被忽略。第一是權限：S3 要觸發 Lambda，Lambda 的 resource-based policy 必須允許 S3 呼叫它（上面的 <code>aws_lambda_permission</code>），少了這段 apply 會成功但事件不會觸發，除錯時不容易發現。第二是 filter：不設 prefix / suffix 的通知會對 bucket 裡每一個物件操作都觸發，包括生命週期搬遷產生的物件變動 — 流量遠超預期。用 filter 把觸發範圍收斂到需要處理的路徑與檔案類型。</p>
<p>事件通知也可以導向 SQS 或 SNS，適合需要非同步佇列處理或 fan-out 到多個消費者的場景。選擇依據是下游的消費模式：Lambda 適合輕量即時處理（毫秒級回應），SQS 適合需要 backpressure 和重試的批次處理，SNS 適合同一事件需要同時通知多個服務。</p>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/infra/07-infra-as-pr/" data-link-title="模組七：infra 走 PR 流程與自動化護欄" data-link-desc="infra 變更走 PR → plan → review diff → 合併 → apply，配 fmt / validate / tflint / checkov / tfsec 與 Atlantis 自動化，讓基礎設施可審查、可回溯、可交接">模組七：infra 走 PR 流程</a>：checkov / tfsec 攔截缺少 public access block 或加密的 bucket</li>
<li>→ <a href="/blog/infra/08-governance-habits/" data-link-title="模組八：治理好習慣 — 規模長大後不失控的最小節奏" data-link-desc="tagging 規範、secrets 不進 code、成本可見性、最小可行節奏，規模長大後不失控">模組八：治理好習慣</a>：bucket 的 tagging 與成本歸因</li>
<li>→ <a href="/blog/infra/02-identity-credentials/" data-link-title="模組二：身分與憑證地基 — IAM 與 OIDC" data-link-desc="IAM role / policy 設計、最小權限，以及用 OIDC 短期憑證取代長期 access key">模組二：身分與憑證地基</a>：bucket policy 與 IAM policy 的權限模型交集</li>
</ul>
]]></content:encoded></item><item><title>Cloud-native Data Policy (BigQuery + S3)</title><link>https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/cloud-data-policy/</link><pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/cloud-data-policy/</guid><description>&lt;p>Cloud-native data policy 的核心責任是把資料層的 access 控制綁在 &lt;em>storage resource 本身&lt;/em>、用該雲既有的 IAM 體系做 enforcement、不依賴額外的 data security platform。本頁同時涵蓋 &lt;em>BigQuery policy tooling&lt;/em>（Authorized View / Column-level security / Row-level security / Dynamic Data Masking）跟 &lt;em>AWS S3 policy tooling&lt;/em>（Bucket policy / Access Points / Object Lambda / Macie / Block Public Access）— 兩條 sister stack 是各自雲端代表性的 data access control 設計、合一頁是為了讓讀者看清楚 &lt;em>GCP 走 SQL-native 細粒度&lt;/em> 跟 &lt;em>AWS 走 storage-resource-bound&lt;/em> 的取捨差異、不是把它們當同類混寫。&lt;/p>
&lt;h2 id="服務定位">服務定位&lt;/h2>
&lt;p>Cloud-native data policy 是 &lt;em>resource-bound&lt;/em> access control — 控制邏輯掛在 BigQuery dataset / column / row 或 S3 bucket / object 上、用 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/google-cloud-iam/" data-link-title="Google Cloud IAM" data-link-desc="GCP cloud resource permission engine、Role Binding / Service Account / Workload Identity Federation、resource hierarchy 為核心的權限治理">Google Cloud IAM&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/aws-iam/" data-link-title="AWS IAM" data-link-desc="AWS cloud resource permission engine、Role / Policy / STS、跨帳號信任邊界與 OIDC federation 的核心">AWS IAM&lt;/a> 的 principal 體系做 evaluation。跟 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &amp;#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP&lt;/a> 比、DLP 是 &lt;em>content-based discovery + transformation&lt;/em>（掃 PII、做 de-id）、本頁工具是 &lt;em>access boundary&lt;/em>；典型組合是 &lt;em>DLP 發現 sensitive column → BigQuery policy tag 控制誰能讀 → S3 Object Lambda redact at read time&lt;/em>。跟 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/microsoft-purview/" data-link-title="Microsoft Purview" data-link-desc="Microsoft 跨 M365 / Azure / endpoint 的 data governance &amp;#43; information protection &amp;#43; DLP &amp;#43; insider risk 統合平台、label-driven">Microsoft Purview&lt;/a> 比、Purview 走 &lt;em>label-driven + 跨 platform&lt;/em>（同一個 sensitivity label 跨 SharePoint / Fabric / Azure SQL）、雲端原生 policy 走 &lt;em>resource-bound + 限該雲&lt;/em>；雲端原生更貼近 storage、跨雲統一靠商業 platform。跟通用 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/vendors/google-cloud-iam/" data-link-title="Google Cloud IAM" data-link-desc="GCP cloud resource permission engine、Role Binding / Service Account / Workload Identity Federation、resource hierarchy 為核心的權限治理">Cloud IAM&lt;/a> 比、IAM 是 &lt;em>resource-level read/write 二分&lt;/em>、本頁是 &lt;em>column / row / object-level 細粒度&lt;/em>、補 IAM 解不掉的「同一張表只能看自家行」場景。&lt;/p></description><content:encoded><![CDATA[<p>Cloud-native data policy 的核心責任是把資料層的 access 控制綁在 <em>storage resource 本身</em>、用該雲既有的 IAM 體系做 enforcement、不依賴額外的 data security platform。本頁同時涵蓋 <em>BigQuery policy tooling</em>（Authorized View / Column-level security / Row-level security / Dynamic Data Masking）跟 <em>AWS S3 policy tooling</em>（Bucket policy / Access Points / Object Lambda / Macie / Block Public Access）— 兩條 sister stack 是各自雲端代表性的 data access control 設計、合一頁是為了讓讀者看清楚 <em>GCP 走 SQL-native 細粒度</em> 跟 <em>AWS 走 storage-resource-bound</em> 的取捨差異、不是把它們當同類混寫。</p>
<h2 id="服務定位">服務定位</h2>
<p>Cloud-native data policy 是 <em>resource-bound</em> access control — 控制邏輯掛在 BigQuery dataset / column / row 或 S3 bucket / object 上、用 <a href="/blog/backend/07-security-data-protection/vendors/google-cloud-iam/" data-link-title="Google Cloud IAM" data-link-desc="GCP cloud resource permission engine、Role Binding / Service Account / Workload Identity Federation、resource hierarchy 為核心的權限治理">Google Cloud IAM</a> / <a href="/blog/backend/07-security-data-protection/vendors/aws-iam/" data-link-title="AWS IAM" data-link-desc="AWS cloud resource permission engine、Role / Policy / STS、跨帳號信任邊界與 OIDC federation 的核心">AWS IAM</a> 的 principal 體系做 evaluation。跟 <a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a> 比、DLP 是 <em>content-based discovery + transformation</em>（掃 PII、做 de-id）、本頁工具是 <em>access boundary</em>；典型組合是 <em>DLP 發現 sensitive column → BigQuery policy tag 控制誰能讀 → S3 Object Lambda redact at read time</em>。跟 <a href="/blog/backend/07-security-data-protection/vendors/microsoft-purview/" data-link-title="Microsoft Purview" data-link-desc="Microsoft 跨 M365 / Azure / endpoint 的 data governance &#43; information protection &#43; DLP &#43; insider risk 統合平台、label-driven">Microsoft Purview</a> 比、Purview 走 <em>label-driven + 跨 platform</em>（同一個 sensitivity label 跨 SharePoint / Fabric / Azure SQL）、雲端原生 policy 走 <em>resource-bound + 限該雲</em>；雲端原生更貼近 storage、跨雲統一靠商業 platform。跟通用 <a href="/blog/backend/07-security-data-protection/vendors/google-cloud-iam/" data-link-title="Google Cloud IAM" data-link-desc="GCP cloud resource permission engine、Role Binding / Service Account / Workload Identity Federation、resource hierarchy 為核心的權限治理">Cloud IAM</a> 比、IAM 是 <em>resource-level read/write 二分</em>、本頁是 <em>column / row / object-level 細粒度</em>、補 IAM 解不掉的「同一張表只能看自家行」場景。</p>
<p>關鍵張力：<em>資料細粒度</em> ↔ <em>跨雲 portability</em>。BigQuery RLS 跟 S3 Access Points 的 policy 語法都是該雲專屬、換雲要重寫；換來的是 free（無額外授權）+ 平台原生效能（不過代理）。多雲 enterprise 若要統一 policy DSL、走 Immuta / Privacera / Snowflake Horizon。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本頁、讀者能判斷：</p>
<ol>
<li>BigQuery 跟 S3 policy 各自能做到什麼層級的細粒度（column / row / object / cross-region）、不能做到什麼</li>
<li>Cloud-native policy 跟 <a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a> / <a href="/blog/backend/07-security-data-protection/vendors/microsoft-purview/" data-link-title="Microsoft Purview" data-link-desc="Microsoft 跨 M365 / Azure / endpoint 的 data governance &#43; information protection &#43; DLP &#43; insider risk 統合平台、label-driven">Microsoft Purview</a> 的責任分界、何時要組合使用</li>
<li>Multi-tenant SaaS 在共用 dataset / bucket 場景的 access boundary 設計（BigQuery RLS / S3 Access Points）</li>
<li>何時用雲端原生 policy、何時改走 Immuta / Privacera / Snowflake 跨雲 data security platform</li>
</ol>
<h2 id="最短判讀路徑">最短判讀路徑</h2>
<p>判斷 cloud-native data policy 是否健康、最少看四件事：</p>
<ul>
<li><strong>BigQuery 側 — RLS / column policy coverage</strong>：multi-tenant dataset 是否有 <code>CREATE ROW ACCESS POLICY</code>、sensitive column 是否綁 policy tag、policy tag 上的 IAM 是否走 group 而非 individual user、view-only access 是否走 <a href="https://cloud.google.com/bigquery/docs/authorized-views">Authorized View</a> 而非 dataset grant</li>
<li><strong>S3 側 — bucket policy 結構</strong>：Block Public Access 是否 account-level 開啟、ACL 是否 disabled（Object Ownership = BucketOwnerEnforced）、共用 bucket 是否走 Access Points 分租戶、跨帳號是否經 AP policy + bucket policy 雙重驗證</li>
<li><strong>Sensitive data discovery 接口</strong>：BigQuery 是否接 <a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a> inspection job、Dataplex 是否跑 data classification、S3 是否開 Macie scan、findings 是否進 EventBridge / Security Hub 而非僅 console 看</li>
<li><strong>Audit trail completeness</strong>：BigQuery audit log（dataAccess）是否進 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">Cloud Logging</a> + 進 SIEM、S3 是否開 server access logging + CloudTrail data event（GetObject / PutObject）、跟 <a href="/blog/backend/07-security-data-protection/detection-coverage-and-signal-governance/" data-link-title="7.13 偵測覆蓋率與訊號治理" data-link-desc="定義偵測覆蓋、訊號品質與誤報成本的治理問題">Detection Coverage</a> 對齊</li>
</ul>
<p>四件事任一缺失、就是 <a href="/blog/backend/07-security-data-protection/data-residency-deletion-and-evidence-chain/" data-link-title="7.11 資料駐留、刪除與證據鏈" data-link-desc="定義跨區資料駐留、刪除請求與可驗證證據鏈問題">Data Residency, Deletion and Evidence Chain</a> 邊界的待補項目。</p>
<h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="bigquery-側">BigQuery 側</h3>
<p><strong>Authorized View / Authorized Routine</strong>：view 的 SQL definition 可以讀 source dataset、grantee 只要被 grant view 自身就能查、<em>不需要 grant source dataset access</em>。經典「給 analyst 看 aggregate 數據但不給原始 PII row」模式 — analyst 看 <code>SELECT region, count(*) FROM customer</code> 沒問題、但 underlying <code>customer</code> table 從不出現在 analyst IAM。Authorized Routine 是同邏輯延伸到 stored procedure / UDF、適合 logic 比 SELECT 複雜的轉換場景。</p>
<p><strong>Column-level security（policy tag）</strong>：在 <a href="https://cloud.google.com/data-catalog">Data Catalog</a> 建 taxonomy + policy tag、把 BigQuery column schema 綁 tag、policy tag 上設 <em>fine-grained reader</em> role。沒這個 role 的 user 即使有 dataset access、<code>SELECT *</code> 時該 column 會 <em>raise error</em> 或 <em>被 omit</em>。HIPAA / PCI-DSS 對「即使 DBA 也不能 default 看到 PHI / cardholder data」的硬要求、走 policy tag 是技術性 enforcement、不是 procedural control。</p>
<p><strong>Row-level security (RLS)</strong>：<code>CREATE ROW ACCESS POLICY tenant_filter ON dataset.table GRANT TO ('group:analysts@org.com') FILTER USING (tenant_id = SESSION_USER())</code>。每個 query 自動 append filter、user 看到的 row 由 policy expression 決定。Multi-tenant SaaS（共用 dataset、每行帶 <code>tenant_id</code>）必用 — 否則 query 必須在 application layer 帶 WHERE、漏一處就是跨 tenant data leak。對應 <a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/snowflake-2024-credential-abuse/" data-link-title="7.R7.4.2 Snowflake 2024：憑證濫用與資料竊取" data-link-desc="外洩憑證與 MFA 缺口如何在資料平台形成高風險外送事件">Snowflake 2024 Credential Abuse</a> 的對照啟示。</p>
<p><strong>Dynamic Data Masking</strong>：column 上設 masking rule（hash / nullify / partial mask / regex replace）、不同 IAM 角色看不同 mask 程度 — <code>email_address</code> 在 admin 看到原值、在 analyst 看到 <code>***@example.com</code>、在 external partner 看到 NULL。補 RLS 不足之處：RLS 過濾 <em>哪些 row 看得到</em>、Masking 過濾 <em>看到的 row 內容怎麼呈現</em>；兩者組合解大多數 multi-tenant + multi-role 場景。</p>
<p><strong>Dataplex Data Classification + DLP 整合</strong>：Dataplex 走 lake-wide 治理（dataset metadata + lineage + quality）、自動觸發 <a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a> inspection、發現 sensitive column 自動建議 / 套用 policy tag。是 GCP 內部把 <em>discovery → access control</em> 自動化的標準路徑。</p>
<h3 id="s3-側">S3 側</h3>
<p><strong>Block Public Access account-level</strong>：2018 推出、2023 起新建 bucket 預設開啟。account-level setting 強制 override 所有 bucket policy / ACL — 即使有 bucket policy 寫 <code>&quot;Principal&quot;: &quot;*&quot;</code>、Block Public Access 開啟時也禁止對外暴露。Production AWS 帳號必須 account-level 開、bucket-level 額外加固。是 <a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/lastpass-2022-backup-chain/" data-link-title="7.R7.4.1 LastPass 2022：備份路徑與鏈式入侵" data-link-desc="開發環境資訊外流如何沿著備份路徑擴大成資料風險">LastPass 2022 Backup Chain</a> 類事故的 last-line defense。</p>
<p><strong>Bucket policy / IAM policy / ACL（legacy）</strong>：三層 evaluation — bucket policy（resource-based、寫在 bucket 上）、IAM policy（identity-based、寫在 principal 上）、ACL（legacy object-level、新建 bucket 應禁用）。AWS 2023 起推 <em>Object Ownership = BucketOwnerEnforced</em>、強制 ACL disabled、所有 access 經 bucket policy + IAM 決定。舊 bucket 應走 ACL → bucket policy migration。</p>
<p><strong>S3 Access Points</strong>：每個 bucket 可開多個 Access Point、各有獨立 name + policy + VPC restriction。Multi-tenant 場景（一個 bucket 服務多個 tenant）走「每個 tenant 一個 AP + AP policy 限定 prefix + 限定 VPC」、取代過去「shared bucket + prefix-based IAM」的脆弱模式。對應 <a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/mailchimp-2023-support-tool-abuse/" data-link-title="7.R7.4.4 Mailchimp 2023：支援工具路徑與客戶資料風險" data-link-desc="社交工程進入客服工具後，如何形成特定客戶資料存取風險">Mailchimp 2023 Support Tool Abuse</a> 的對照啟示 — 共用入口需 <em>per-tenant policy boundary</em>、不是 application-layer filtering。</p>
<p><strong>Multi-Region Access Points (MRAP)</strong>：跨 region replicated bucket 的單一 global endpoint、自動 route 到最近 region。資料駐留要求高的場景（GDPR / 中國資料法）反而要慎用、因為 read 來源不可預測；對 latency-sensitive 全球分發是 first-class 解法。</p>
<p><strong>Object Lambda Access Points</strong>：在 GetObject response path 插 Lambda、做 <em>read-time transformation</em>（redact PII / format conversion / image resize / decrypt + re-encrypt）。同一份 raw object、不同 caller 透過不同 Object Lambda AP 看到不同版本 — 等同 BigQuery Dynamic Data Masking 在 S3 的對應物。但 Lambda 有 cold start + 6MB response limit、不是所有場景都合適。</p>
<p><strong>Macie sensitive data discovery</strong>：S3 專屬、scan bucket 找 PII / credential / payment data、findings 進 EventBridge + AWS Security Hub。跟 <a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a> 同層但限 S3、不能掃 RDS / DynamoDB。findings 應自動 route 到 SIEM、不是只在 Macie console 等人看。對應 <a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/progress-wsftp-2023-file-service-breach/" data-link-title="7.R7.4.6 Progress WS_FTP 2023：檔案服務入口與資料外送" data-link-desc="對外檔案服務漏洞在企業環境常直接轉為資料外送風險">Progress WS_FTP 2023 File Service Breach</a> 的對照 — 對外檔案服務必有 audit + 異常量 baseline + Macie sensitive content scan。</p>
<p><strong>S3 Object Ownership / ACL disabled</strong>：2023+ 預設 ACL disabled、所有新 bucket 應 keep this default、舊 bucket 走 audit + migration（先掃 ACL grant、確認沒人靠 ACL 拿 access、再切換）。混用 ACL + bucket policy 的 bucket 是 access control 漂移最常見的源頭。</p>
<h2 id="核心取捨表">核心取捨表</h2>
<table>
  <thead>
      <tr>
          <th>取捨維度</th>
          <th>BigQuery policy tooling</th>
          <th>S3 policy tooling</th>
          <th>Immuta / Privacera</th>
          <th>Snowflake Horizon</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>細粒度層級</td>
          <td>Column / Row / cell-level（policy tag + RLS + DDM）</td>
          <td>Object-level（prefix-based）+ Object Lambda 內容轉換</td>
          <td>Column / Row / cell + 跨平台統一 DSL</td>
          <td>Column / Row + Snowflake 平台限定</td>
      </tr>
      <tr>
          <td>計費</td>
          <td>Free（included in BigQuery）</td>
          <td>Free（bucket policy）+ Macie / Object Lambda 用量計費</td>
          <td>商業授權、per-user 或 per-data-source</td>
          <td>Snowflake 平台費內含</td>
      </tr>
      <tr>
          <td>跨雲 portable</td>
          <td>GCP only</td>
          <td>AWS only</td>
          <td>跨 BigQuery / Snowflake / Databricks / S3</td>
          <td>Snowflake only</td>
      </tr>
      <tr>
          <td>Policy DSL</td>
          <td>SQL-native（CREATE ROW ACCESS POLICY、masking SQL）</td>
          <td>JSON policy + Lambda 程式碼</td>
          <td>統一 attribute-based DSL</td>
          <td>SQL-native</td>
      </tr>
      <tr>
          <td>Sensitive discovery</td>
          <td>DLP / Dataplex 自動整合</td>
          <td>Macie（限 S3）</td>
          <td>內建 + 跨平台 scan</td>
          <td>跨 schema metadata + classification</td>
      </tr>
      <tr>
          <td>Audit</td>
          <td>Cloud Audit Log dataAccess 細到 column</td>
          <td>CloudTrail data event + server access log</td>
          <td>跨平台統一 audit trail</td>
          <td>Snowflake QUERY_HISTORY</td>
      </tr>
      <tr>
          <td>適合場景</td>
          <td>GCP-first、BigQuery 為主 data warehouse</td>
          <td>AWS-first、S3 為 data lake / 檔案分發</td>
          <td>多雲 enterprise、跨平台統一 policy</td>
          <td>Snowflake-centric data platform</td>
      </tr>
      <tr>
          <td>退場成本</td>
          <td>中 — RLS / policy tag 重寫到目標平台</td>
          <td>中 — bucket policy / AP 重寫</td>
          <td>低 — DSL 抽象可遷移</td>
          <td>中 — 限 Snowflake</td>
      </tr>
  </tbody>
</table>
<p>選雲端原生 policy 的核心訴求：<em>單一雲 + 預算敏感 + 不想引入新 vendor</em>。多雲 enterprise + 統一治理需求高、走 Immuta / Privacera 才能避免兩套 policy 漂移。</p>
<h2 id="進階主題">進階主題</h2>
<p><strong>BigQuery Authorized View vs RLS 取捨</strong>：Authorized View 適合 <em>shape-based filtering</em>（grantee 只能看 aggregate / 特定 column subset）、RLS 適合 <em>value-based filtering</em>（grantee 只能看 tenant_id = self 的行）。實務常常組合 — view 限 column、view 上再加 RLS 限 row。view 的問題是維護成本（schema 改要同步改 view）、RLS 的問題是 policy expression 寫錯整批 user 看不到資料、staging tenant 跑過再 promote。</p>
<p><strong>S3 Access Points + VPC-only restriction</strong>：AP policy 可加 <code>&quot;Condition&quot;: {&quot;StringEquals&quot;: {&quot;aws:SourceVpc&quot;: &quot;vpc-xxx&quot;}}</code>、強制只能從特定 VPC access — 跨帳號場景（partner 帳號 access 自家 bucket）必加、避免 partner credential 外洩後可從任意網路位置存取。對應 <a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/lastpass-2022-backup-chain/" data-link-title="7.R7.4.1 LastPass 2022：備份路徑與鏈式入侵" data-link-desc="開發環境資訊外流如何沿著備份路徑擴大成資料風險">LastPass 2022 Backup Chain</a> 對照、backup bucket 不該跟 prod bucket 共用 IAM role + 不該允許 internet-wide access。</p>
<p><strong>Object Lambda redact PII at read time</strong>：適合 <em>raw data 已寫入、但不同 consumer 需要不同 view</em> 的場景 — 例如客服查 user record 看到 mask 過的 SSN、合規 audit 帳號看到完整 SSN。Lambda 內部呼叫 <a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a> deid template / Comprehend PII detection / 自家 regex；要注意 cold start 對 latency 的影響、不適合 high-throughput 場景。</p>
<p><strong>Macie automated discovery → SIEM</strong>：Macie findings 走 EventBridge rule → Security Hub → 推 <a href="/blog/backend/07-security-data-protection/vendors/" data-link-title="資安與資料保護 Vendor 清單" data-link-desc="規劃身份、秘密、金鑰、入口防護、供應鏈與偵測工具的服務頁撰寫順序與教學大綱">Splunk / Elastic Security / Datadog Security</a> — 不該只在 Macie console 看 findings。發現 unencrypted S3 bucket 有 cardholder data 必須觸發 incident response runbook、進 <a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">8 事故處理</a>。</p>
<p><strong>跨 region 跟 data residency</strong>：BigQuery dataset region + S3 bucket region 是 <em>資料駐留 enforcement</em> 的硬邊界、policy tooling 不能 override。GDPR / 中國資料法場景必須 <em>region pinning</em> + 禁止 Multi-Region replication、policy tag / RLS 無法解決資料離境問題。對應 <a href="/blog/backend/07-security-data-protection/data-residency-deletion-and-evidence-chain/" data-link-title="7.11 資料駐留、刪除與證據鏈" data-link-desc="定義跨區資料駐留、刪除請求與可驗證證據鏈問題">Data Residency Deletion and Evidence Chain</a> 章節原則。</p>
<h2 id="排錯與失敗快速判讀">排錯與失敗快速判讀</h2>
<ul>
<li><strong>BigQuery RLS 設了但 user 還是看到全部 row</strong>：policy <code>GRANT TO</code> 沒包該 user 的 group、或 user 有 <code>bigquery.dataOwner</code> role（owner override RLS）— check group membership + 降權到 dataViewer</li>
<li><strong>Column policy tag 沒生效</strong>：column 沒 attach tag、或 tag taxonomy 沒在該 project / region — check Data Catalog taxonomy location 跟 dataset region 對齊</li>
<li><strong>S3 bucket 意外 public</strong>：Block Public Access account-level 沒開 + bucket policy 寫 <code>&quot;Principal&quot;: &quot;*&quot;</code>、或 ACL 殘留 AllUsers grant — 立即開 BPA + audit ACL（aws s3api get-bucket-acl）</li>
<li><strong>Access Point policy 跟 bucket policy 衝突</strong>：AP 允許 但 bucket policy 拒絕、最後是拒絕（explicit deny 永遠勝）— 兩層都要明確 allow、bucket policy 加 <code>&quot;Principal&quot;: {&quot;AWS&quot;: &quot;*&quot;}</code> + condition 限定 AP ARN</li>
<li><strong>Macie scan 跑很久 / cost 暴衝</strong>：scan 整個 bucket、含 archive prefix、沒設 sampling — 用 <em>sensitive data discovery job</em> with prefix filter + sampling rate、不要 default 全 bucket scan</li>
<li><strong>Authorized View grantee 看不到資料</strong>：view definition 走的 source dataset 沒 authorize 該 view、或 view 自身改了但沒重新 authorize — <code>bq update --view_authorization</code> 重設</li>
<li><strong>Object Lambda 慢 / timeout</strong>：Lambda cold start + 6MB response limit、大檔案不該走 Object Lambda — 改在寫入時 transform、或用 pre-signed URL 繞過 Object Lambda</li>
</ul>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>跨雲統一 data policy DSL</td>
          <td>Immuta / Privacera</td>
      </tr>
      <tr>
          <td>Content-based discovery + de-id</td>
          <td><a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a> / <a href="/blog/backend/07-security-data-protection/vendors/microsoft-purview/" data-link-title="Microsoft Purview" data-link-desc="Microsoft 跨 M365 / Azure / endpoint 的 data governance &#43; information protection &#43; DLP &#43; insider risk 統合平台、label-driven">Microsoft Purview</a></td>
      </tr>
      <tr>
          <td>Label-driven + Microsoft 365 跨 platform</td>
          <td><a href="/blog/backend/07-security-data-protection/vendors/microsoft-purview/" data-link-title="Microsoft Purview" data-link-desc="Microsoft 跨 M365 / Azure / endpoint 的 data governance &#43; information protection &#43; DLP &#43; insider risk 統合平台、label-driven">Microsoft Purview</a></td>
      </tr>
      <tr>
          <td>Application-layer access control</td>
          <td>應用層 RBAC / ABAC（Casbin / OPA / Cerbos）</td>
      </tr>
      <tr>
          <td>Snowflake-centric data platform</td>
          <td>Snowflake Horizon（row access policy / masking policy 平台內建）</td>
      </tr>
      <tr>
          <td>通用 cloud resource permission</td>
          <td><a href="/blog/backend/07-security-data-protection/vendors/aws-iam/" data-link-title="AWS IAM" data-link-desc="AWS cloud resource permission engine、Role / Policy / STS、跨帳號信任邊界與 OIDC federation 的核心">AWS IAM</a> / <a href="/blog/backend/07-security-data-protection/vendors/google-cloud-iam/" data-link-title="Google Cloud IAM" data-link-desc="GCP cloud resource permission engine、Role Binding / Service Account / Workload Identity Federation、resource hierarchy 為核心的權限治理">Google Cloud IAM</a></td>
      </tr>
      <tr>
          <td>SIEM / detection</td>
          <td><a href="/blog/backend/07-security-data-protection/vendors/" data-link-title="資安與資料保護 Vendor 清單" data-link-desc="規劃身份、秘密、金鑰、入口防護、供應鏈與偵測工具的服務頁撰寫順序與教學大綱">Splunk / Elastic Security / Datadog Security</a></td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>BigQuery / S3 自身的完整 admin guide（pricing / region / quota）</li>
<li>Encryption-at-rest 細節（KMS 整合走 <a href="/blog/backend/07-security-data-protection/vendors/aws-kms/" data-link-title="AWS KMS" data-link-desc="AWS 原生 key management service、envelope encryption / digital signing / Multi-Region Key、Key Policy &#43; Grant 雙軌授權">AWS KMS</a> / <a href="/blog/backend/07-security-data-protection/vendors/google-cloud-kms/" data-link-title="Google Cloud KMS" data-link-desc="GCP 原生 key management service、KeyRing / CryptoKey Version 設計、CMEK 整合 &#43; Cloud HSM &#43; External Key Manager">Google Cloud KMS</a> 頁）</li>
<li>Azure Data Lake / Azure SQL policy（屬 Azure stack、本頁不涵蓋）</li>
<li>應用層 RBAC framework（Casbin / Cerbos / OPA Rego）</li>
<li>資料庫層 RLS（PostgreSQL RLS / SQL Server Row-Level Security）— 跟雲端原生 storage policy 是不同層</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<p>Cloud-native data policy 在 07 案例庫沒有直接 vendor-level 事件、所有 data exfiltration case 都是 access boundary 的對照：</p>
<table>
  <thead>
      <tr>
          <th>案例</th>
          <th>跟 cloud-native data policy 的關係（對照啟示）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/snowflake-2024-credential-abuse/" data-link-title="7.R7.4.2 Snowflake 2024：憑證濫用與資料竊取" data-link-desc="外洩憑證與 MFA 缺口如何在資料平台形成高風險外送事件">Snowflake 2024 Credential Abuse</a></td>
          <td>Multi-tenant SaaS 共用 dataset / schema 必須有 BigQuery RLS / Snowflake row access policy 等技術邊界、即使 credential 外洩攻擊者也只能看授權 row、不能只靠 application-layer WHERE</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/lastpass-2022-backup-chain/" data-link-title="7.R7.4.1 LastPass 2022：備份路徑與鏈式入侵" data-link-desc="開發環境資訊外流如何沿著備份路徑擴大成資料風險">LastPass 2022 Backup Chain</a></td>
          <td>S3 backup bucket 跟 prod bucket 必須獨立 Access Point + 獨立 IAM role + VPC restriction、同帳號 prefix-based 區隔不夠、Block Public Access 是 last-line</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/progress-wsftp-2023-file-service-breach/" data-link-title="7.R7.4.6 Progress WS_FTP 2023：檔案服務入口與資料外送" data-link-desc="對外檔案服務漏洞在企業環境常直接轉為資料外送風險">Progress WS_FTP 2023 File Service Breach</a></td>
          <td>對外檔案服務必須有 S3 server access log + CloudTrail data event + Macie sensitive content scan、批量下載靠 GetObject 速率 baseline alert、不是事後檢視</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/mailchimp-2023-support-tool-abuse/" data-link-title="7.R7.4.4 Mailchimp 2023：支援工具路徑與客戶資料風險" data-link-desc="社交工程進入客服工具後，如何形成特定客戶資料存取風險">Mailchimp 2023 Support Tool Abuse</a></td>
          <td>共用 bucket 服務多 tenant 必走 S3 Access Points 拆 per-tenant policy、取代 prefix-based ACL 跟 application-layer filtering 的脆弱模式</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/07-security-data-protection/data-residency-deletion-and-evidence-chain/" data-link-title="7.11 資料駐留、刪除與證據鏈" data-link-desc="定義跨區資料駐留、刪除請求與可驗證證據鏈問題">Data Residency Deletion and Evidence Chain (section)</a></td>
          <td>Cloud-native policy 是 deletion + residency 治理的技術 enforcement 層、region pinning + 禁止 Multi-Region replication + audit log retention 對應章節原則</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游：<a href="/blog/backend/07-security-data-protection/data-residency-deletion-and-evidence-chain/" data-link-title="7.11 資料駐留、刪除與證據鏈" data-link-desc="定義跨區資料駐留、刪除請求與可驗證證據鏈問題">7.7 資料駐留刪除與證據鏈</a>、<a href="/blog/backend/07-security-data-protection/detection-coverage-and-signal-governance/" data-link-title="7.13 偵測覆蓋率與訊號治理" data-link-desc="定義偵測覆蓋、訊號品質與誤報成本的治理問題">Detection Coverage and Signal Governance</a></li>
<li>平行：<a href="/blog/backend/07-security-data-protection/vendors/google-dlp/" data-link-title="Google DLP" data-link-desc="GCP 原生 Sensitive Data Protection：infoType discovery &#43; transformation (mask / FPE / tokenize / k-anonymity)、整合 BigQuery / GCS / Cloud SQL">Google DLP</a>（discovery + de-id 互補）、<a href="/blog/backend/07-security-data-protection/vendors/microsoft-purview/" data-link-title="Microsoft Purview" data-link-desc="Microsoft 跨 M365 / Azure / endpoint 的 data governance &#43; information protection &#43; DLP &#43; insider risk 統合平台、label-driven">Microsoft Purview</a>（label-driven 對照）</li>
<li>下游：<a href="/blog/backend/07-security-data-protection/vendors/" data-link-title="資安與資料保護 Vendor 清單" data-link-desc="規劃身份、秘密、金鑰、入口防護、供應鏈與偵測工具的服務頁撰寫順序與教學大綱">Splunk / Elastic Security / Datadog Security</a>（audit log + Macie findings → SIEM）</li>
<li>跨類：<a href="/blog/backend/07-security-data-protection/vendors/aws-iam/" data-link-title="AWS IAM" data-link-desc="AWS cloud resource permission engine、Role / Policy / STS、跨帳號信任邊界與 OIDC federation 的核心">AWS IAM</a> / <a href="/blog/backend/07-security-data-protection/vendors/google-cloud-iam/" data-link-title="Google Cloud IAM" data-link-desc="GCP cloud resource permission engine、Role Binding / Service Account / Workload Identity Federation、resource hierarchy 為核心的權限治理">Google Cloud IAM</a>（principal 體系基底）、<a href="/blog/backend/07-security-data-protection/vendors/aws-kms/" data-link-title="AWS KMS" data-link-desc="AWS 原生 key management service、envelope encryption / digital signing / Multi-Region Key、Key Policy &#43; Grant 雙軌授權">AWS KMS</a> / <a href="/blog/backend/07-security-data-protection/vendors/google-cloud-kms/" data-link-title="Google Cloud KMS" data-link-desc="GCP 原生 key management service、KeyRing / CryptoKey Version 設計、CMEK 整合 &#43; Cloud HSM &#43; External Key Manager">Google Cloud KMS</a>（encryption-at-rest）</li>
<li>跨模組：<a href="/blog/backend/08-incident-response/vendors/" data-link-title="事故處理 Vendor 清單" data-link-desc="規劃 on-call、incident response、status page 與 postmortem 工具的服務頁撰寫順序與判準">8 事故處理 vendor 清單</a>（data exfiltration incident routing）、<a href="/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">1 資料庫模組</a>（database-layer RLS / column policy 對照）</li>
<li>官方：<a href="https://cloud.google.com/bigquery/docs/column-level-security">BigQuery column-level security</a>、<a href="https://cloud.google.com/bigquery/docs/row-level-security-intro">BigQuery row-level security</a>、<a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-points.html">Amazon S3 Access Points</a>、<a href="https://docs.aws.amazon.com/macie/">Amazon Macie</a></li>
</ul>
]]></content:encoded></item></channel></rss>