-
Notifications
You must be signed in to change notification settings - Fork 241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sec: fix s3 and gcs host checks #512
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @dduzgun-security, great contribution. I have added added some comments and thoughts around the implementation, nothing special.
Looking forward to see this PR landing!
@@ -18,23 +21,39 @@ func (d *GCSDetector) Detect(src, _ string) (string, bool, error) { | |||
return "", false, nil | |||
} | |||
|
|||
if strings.Contains(src, "googleapis.com/") { | |||
if strings.Contains(src, ".googleapis.com/") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we be sure that src
is always only the FQDN and not also the path? So that we can be sure that src
is not something like evil.foo.bar/hi-hashi.googleapis.com/whatever/
. I will take a look into the call path, but just having that as a first thought in mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If take a look into the call graph, the mentioned case ^ might ocour:
Lines 47 to 60 in 842d6c3
func Detect(src string, pwd string, ds []Detector) (string, error) { | |
getForce, getSrc := getForcedGetter(src) | |
// Separate out the subdir if there is one, we don't pass that to detect | |
getSrc, subDir := SourceDirSubdir(getSrc) | |
u, err := url.Parse(getSrc) | |
if err == nil && u.Scheme != "" { | |
// Valid URL | |
return src, nil | |
} | |
for _, d := range ds { | |
result, ok, err := d.Detect(getSrc, pwd) |
Might be worse to parse the url and inspect only the host. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, what about various cases? 🙈
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. You create your own url here later on:
Line 58 in 042c9f5
url, err := url.Parse(fmt.Sprintf("https://www.googleapis.com/storage/%s/%s/%s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, it's still not a bad idea to url parse it here too and validate the sufix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, well ... as long as you check or for the suffix of the host name :)
} | ||
|
||
// Rule 6: Bucket name cannot contain "google" or common misspellings like "g00gle" | ||
googlePattern := `google|g00gle` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about go0gle
? Is there somewhere a spec to that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if there will be too many permutations to consider here. The documentation really only says:
Bucket names cannot contain "google" or close misspellings, such as "g00gle".
https://cloud.google.com/storage/docs/buckets#naming
We could go further with a regex like g[o0]{2}gle
, but there's also 1
s for l
, etc. It's a rabbit hole I'm not sure we want to go down. 🕳️ 🐇
Since this will be enforced by Google Cloud when creating a bucket, maybe we don't really need to worry about it for the means of detecting a bucket. If we send an invalid bucket name in a request to GCS, it should just fail.
I suspect we can simply detect it likely being a bucket without checking these common misspellings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
I don't mind if we leave it as @dduzgun-security proposed it and any further validation is done on the server site endpoint. My comment from above was mainly a thing that came through my mind while reading that check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After talking with @picatz, we won't need this part as it might add more risk or blockers on specific use cases by people. Also, this doesn't allow any injection point and Google validates the call.
} | ||
|
||
// Rule 2: Bucket name cannot start or end with a hyphen, dot, or underscore | ||
if bucket[0] == '-' || bucket[0] == '.' || bucket[len(bucket)-1] == '-' || bucket[len(bucket)-1] == '.' || bucket[len(bucket)-1] == '_' { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if bucket[0] == '-' || bucket[0] == '.' || bucket[len(bucket)-1] == '-' || bucket[len(bucket)-1] == '.' || bucket[len(bucket)-1] == '_' { | |
if bucket[0] == '-' || bucket[0] == '.' || bucket[0] == '_' || bucket[len(bucket)-1] == '-' || bucket[len(bucket)-1] == '.' || bucket[len(bucket)-1] == '_' { |
} | ||
|
||
// Rule 4: Bucket name cannot be an IP address (only digits and dots, e.g., 192.168.5.4) | ||
ipPattern := `^(\d{1,3}\.){3}\d{1,3}$` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
999.999.999
is not a valid IP address, but is it still officially invalid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding on to this maybe a more direct check should be used for example:
// IPv4 regex matches 0.0.0.0 to 255.255.255.255
simpleRegex := `^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 Maybe it would be even better to avoid a regex, and use the standard library:
https://pkg.go.dev/net/netip#ParseAddr
addr, err := netip.ParseAddr(ipString)
...
Compared to the net.IP type, Addr type takes less memory, is immutable, and is comparable (supports
==
and being amap
key).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, is it not a bit too much over-processing for a path-validation to try to convert parts of the path into a Addr
object? I don't have a perfect solution in mind 🤷
return false | ||
} | ||
|
||
// Rule 3: Object names cannot be exactly '.' or '..' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cant be anyway, due to the fact that you perform the path.Clean(..)
at the beginning 😬 however, the check is not wrong :)
@@ -233,3 +233,35 @@ func TestGCSGetter_GetFile_OAuthAccessToken(t *testing.T) { | |||
} | |||
assertContents(t, dst, "# Main\n") | |||
} | |||
|
|||
func Test_GCSGetter_ParseUrl_Malformed(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to have a positive example for this test.
Resolves potential incorrect host checks for S3 and GCS.
Ref: SECVULN-14562, SECVULN-14561, SECVULN-14564