<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Object Grant on SILO</title><link>https://silo.pgsty.com/tags/object-grant/</link><description>Recent content in Object Grant on SILO</description><generator>Hugo</generator><language>en</language><lastBuildDate>Tue, 04 Aug 2026 14:52:08 +0800</lastBuildDate><atom:link href="https://silo.pgsty.com/tags/object-grant/index.xml" rel="self" type="application/rss+xml"/><item><title>Object Grant, Bucket Reach: When 'bucket/*' Could Rewrite the Bucket Itself</title><link>https://silo.pgsty.com/blog/security/object-grant-bucket-reach/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><guid>https://silo.pgsty.com/blog/security/object-grant-bucket-reach/</guid><description>&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed on &lt;code&gt;pgsty/silo-pkg&lt;/code&gt; &lt;code&gt;main&lt;/code&gt; (&lt;code&gt;3c24ad1&lt;/code&gt;, extended by &lt;code&gt;1f97549&lt;/code&gt;, scoped to its final twelve actions in &lt;code&gt;d8b1fa7&lt;/code&gt;), &lt;strong&gt;released as &lt;code&gt;silo-pkg v3.11.0&lt;/code&gt;&lt;/strong&gt;; consumed by &lt;code&gt;pgsty/minio&lt;/code&gt;
&lt;strong&gt;Classification:&lt;/strong&gt; Access-control hardening — a privilege boundary, narrowly restored
&lt;strong&gt;Affected scope:&lt;/strong&gt; IAM users/roles/service accounts granted only object-scoped (&lt;code&gt;arn:aws:s3:::bucket/*&lt;/code&gt;) access, in deployments that share a cluster across tenants
&lt;strong&gt;Tracking:&lt;/strong&gt; upstream &lt;code&gt;minio/minio&lt;/code&gt; issue &lt;a href="https://github.com/minio/minio/issues/20449"&gt;#20449&lt;/a&gt; (public since 2024, still open)&lt;/p&gt;
&lt;h2 id="summary"&gt;Conclusions first&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;In IAM policy matching, a bucket-level request carries an &lt;strong&gt;empty object name&lt;/strong&gt;, and the matcher built its resource string as &lt;code&gt;&amp;quot;bucket/&amp;quot;&lt;/code&gt;. An object-only policy pattern — &lt;code&gt;&amp;quot;arn:aws:s3:::bucket/*&amp;quot;&lt;/code&gt; — then matched that string, so a grant that should cover only objects &lt;strong&gt;also authorized bucket-level actions.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The dangerous one is &lt;strong&gt;&lt;code&gt;PutBucketPolicy&lt;/code&gt;&lt;/strong&gt;. A tenant holding only &lt;code&gt;s3:*&lt;/code&gt; on &lt;code&gt;bucket/*&lt;/code&gt; could install a bucket policy with &lt;code&gt;Principal:&amp;quot;*&amp;quot;&lt;/code&gt; — making the bucket &lt;strong&gt;publicly readable or writable&lt;/strong&gt; — or grant itself bucket-level control. Same mechanism, same class: &lt;code&gt;DeleteBucket&lt;/code&gt;/&lt;code&gt;ForceDeleteBucket&lt;/code&gt; (the issue&amp;rsquo;s own reproduction), &lt;code&gt;PutReplicationConfiguration&lt;/code&gt; (exfiltration), &lt;code&gt;PutBucketLifecycle&lt;/code&gt; (mass deletion), &lt;code&gt;PutBucketVersioning&lt;/code&gt;, &lt;code&gt;PutBucketObjectLockConfiguration&lt;/code&gt;, and the rest of the bucket-configuration writes.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;full&lt;/strong&gt; correction is a two-directional behavior change: it tightens over-granting &lt;code&gt;Allow&lt;/code&gt; statements &lt;strong&gt;and&lt;/strong&gt; loosens over-blocking &lt;code&gt;Deny&lt;/code&gt; statements, and it would revoke &lt;code&gt;ListBucket&lt;/code&gt;/&lt;code&gt;GetBucketLocation&lt;/code&gt; grants that &lt;strong&gt;many real deployments write as &lt;code&gt;bucket/*&lt;/code&gt; today&lt;/strong&gt;. That is a compatibility break, not a clean patch.&lt;/li&gt;
&lt;li&gt;So we shipped a &lt;strong&gt;narrow&lt;/strong&gt; fix — first six sensitive bucket-configuration writes, then, in a second pass, &lt;strong&gt;twelve&lt;/strong&gt;: the bucket-level writes that hand the caller something its object access does not already give it, plus four that no handler implements. Only on &lt;code&gt;Allow&lt;/code&gt; statements, so no &lt;code&gt;Deny&lt;/code&gt; and no &lt;code&gt;NotResource&lt;/code&gt; exclusion is ever weakened, with an environment-variable escape hatch. The compatibility-sensitive read/list family, &lt;code&gt;CreateBucket&lt;/code&gt;, and three bucket writes with plausible tenant use are &lt;strong&gt;left unchanged, by decision&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Twice we claimed the change could only remove permissions, and twice an untested case said otherwise — the second time found by an independent review of a shipped release. The protected path now requires the resource to match &lt;strong&gt;both&lt;/strong&gt; the bare and the historical form, which makes the property hold by construction rather than by argument.&lt;/li&gt;
&lt;li&gt;The fix is &lt;strong&gt;red/green proven&lt;/strong&gt; at the matcher layer and end to end through the real handlers; the object-scoped hot path is untouched.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-defect"&gt;The slash, and the empty object name&lt;/h2&gt;
&lt;p&gt;Every bucket-level S3 operation authorizes with an empty object name — &lt;code&gt;checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, &amp;quot;&amp;quot;)&lt;/code&gt;. The IAM matcher turned that into a resource string, and for the empty-object case it appended a trailing slash:&lt;/p&gt;</description></item></channel></rss>