-
-
Notifications
You must be signed in to change notification settings - Fork 616
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
G407: Incorrect detection of fixed iv #1211
Comments
@expp121 please could you have a look at this? Thanks |
Yeah, this analyzer doesn't really make any sense. It requires that all nonces are generated with Edit: I just realized this was already captured in #1209. |
Although maybe in this instance the warning is right? The nonce/iv probably should come from a random source? In this particular case, as it happens, there's a good bit of randomness in there too, since |
Since bcrypt is generating a symmetric key, gosec can't tell if the nonce should be random or loaded from, say, a database. It should only be random at generation time, and even that could conceivably happen earlier than the first use. For example, even the database engine might generate the random salt and then the Go code will always just be loading it from an external source. So it's not really possible to tell if the nonce was securely generated unless the variable is never used after initialization and is initialized from the executable's bss section or such... |
I was focused on But for cases where it's clearly used for encryption (e.g. |
That seems entirely reasonable to me. Maybe we can just improve the error message. If people are loading from storage, then the wording of hard-coded may trip them up and they'll think that it's a false positive. |
But it is a false positive, as it also reacts to functions that are required to get IVs from some storage. |
I completely agreed with you @imirkin, using |
In a nutshell: if you pass a different variable (different name/different memory location) with the SAME value as the one used in Please correct me if I didn't get what you were saying! |
Nobody calls Seal + Open in the same function, other than weird examples... and all the cases being flagged here are ones where only one operation is ever done in the function. I think it's reasonable to ensure that both encryption and decryption are not done using a fixed nonce, and it's reasonable to ensure that encryption (i.e. Seal) is done using a random nonce. Sounds like this pass is overly eager in determining that SSA values are identical though. |
|
For all uses, I think it's reasonable to check if the SSA value is not an "immediate" or "const" (not sure which nomenclature is used in Go's SSA). i.e. For |
@imirkin thank you for the recommendation, will see what can I do! Edit: If possible, can you provide me with more examples in which you want something to be flagged and something that you don't want to be flagged. It would be nice if you could do that! |
Some crytpo algorithms store the nonce with the cipher-text. Maybe we should split the encryption from decryption part first to reduce the noise, and run the check only on the encryption methods where is more critical to have a fresh random nonce. I think to have a precise judgement of the origin of the data, we need taint analysis to really understand from where the random data is originating (e.g. secure random function, file etc). I think we should only flag when we are sure that the nonse is coming from an hardocded source, and we can find that source in the program. |
Not flagged (note that I'm skimping on error checking, so perhaps it'll be flagged for that...): func Decrypt(data []byte, key [32]byte) ([]byte, error) {
block, _ := aes.NewCipher(key[:32])
gcm, _ := cipher.NewGCM(block)
return gcm.Open(nil, data[:gcm.NonceSize()], data[gcm.NonceSize():], nil)
}
func Encrypt(data []byte, key [32]byte) []byte {
block, _ := aes.NewCipher(key[:32])
gcm, _ := cipher.NewGCM(block)
nonce := make([]byte, gcm.NonceSize())
io.ReadFull(rand.Reader, nonce)
return gcm.Seal(nonce, nonce, data, nil)
} and similar variants for the other encrypt/decrypt functions. Both of these should be flagged: func Decrypt(data []byte, key [32]byte) ([]byte, error) {
block, _ := aes.NewCipher(key[:32])
gcm, _ := cipher.NewGCM(block)
nonce := make([]byte, gcm.NonceSize()) // 0-initialized
return gcm.Open(nil, nonce, data[gcm.NonceSize():], nil)
}
func Encrypt(data []byte, key [32]byte) []byte {
block, _ := aes.NewCipher(key[:32])
gcm, _ := cipher.NewGCM(block)
nonce := make([]byte, gcm.NonceSize()) // 0-initialized
return gcm.Seal(nonce, nonce, data, nil)
} and other variants of making nonce with hard-coded data. And ideally this would be flagged too: func Encrypt(data []byte, nonce []byte, key [32]byte) []byte {
block, _ := aes.NewCipher(key[:32])
gcm, _ := cipher.NewGCM(block)
return gcm.Seal(data[:0], nonce, data, nil)
} since it's trying to encrypt with a non-random nonce. |
@ccojocar Perhaps makes sense to split this into two separate warnings -- one about using a provably non-random IV (i.e. directly initialized with fixed values), and another one about using a non-provably random IV for encryption? |
## What kind of change does this PR introduce? Fix to gosec warnings so builds can complete. ## What is the current behavior? The gosec checks are halting builds. ## What is the new behavior? The gosec checks are passing. ## Additional context I didn't see any warnings that led to real vulnerabilities / security issues. That said long term it may be worth adding some defensive bounds checks for a couple of the integer overflow warnings, just to future proof us age the code ages. Given that we allow supabase users to write to the database, not sure we can guarantee a user doesn't provide a bring-your-own-hash singup flow or something like that. Unbound allocations are a prime target for DoS attacks. For the nonce issues, neither is was real issue. Open is not "fixable, see gosec issue [#1211](securego/gosec#1211). For Seal I tried: ``` nonce := make([]byte, cipher.NonceSize()) if _, err := rand.Read(nonce); err != nil { panic(err) } es := EncryptedString{ KeyID: keyID, Algorithm: "aes-gcm-hkdf", Nonce: nonce, } es.Data = cipher.Seal(nil, es.Nonce, data, nil) ``` But it then considers es.Nonce to be stored / hardcoded. The only fix I could get to work was: ```Go nonce := make([]byte, cipher.NonceSize()) if _, err := rand.Read(nonce); err != nil { panic(err) } es := EncryptedString{ KeyID: keyID, Algorithm: "aes-gcm-hkdf", Nonce: nonce, Data: cipher.Seal(nil, nonce, data, nil), } ``` It seems the gosec tool requires using `rand.Read`. I changed the `cipher.NonceSize()` back to `12` (just in case it a numerical constant for a reason) and it started failing again. I think it also checks that cipher.NonceSize() is used as well, just doesn't report that. I ultimately decided to ignore this so there was no changes to crypto functions given the existing code is correct. Co-authored-by: Chris Stockton <[email protected]>
The I had |
…e#1770) ## What kind of change does this PR introduce? Fix to gosec warnings so builds can complete. ## What is the current behavior? The gosec checks are halting builds. ## What is the new behavior? The gosec checks are passing. ## Additional context I didn't see any warnings that led to real vulnerabilities / security issues. That said long term it may be worth adding some defensive bounds checks for a couple of the integer overflow warnings, just to future proof us age the code ages. Given that we allow supabase users to write to the database, not sure we can guarantee a user doesn't provide a bring-your-own-hash singup flow or something like that. Unbound allocations are a prime target for DoS attacks. For the nonce issues, neither is was real issue. Open is not "fixable, see gosec issue [supabase#1211](securego/gosec#1211). For Seal I tried: ``` nonce := make([]byte, cipher.NonceSize()) if _, err := rand.Read(nonce); err != nil { panic(err) } es := EncryptedString{ KeyID: keyID, Algorithm: "aes-gcm-hkdf", Nonce: nonce, } es.Data = cipher.Seal(nil, es.Nonce, data, nil) ``` But it then considers es.Nonce to be stored / hardcoded. The only fix I could get to work was: ```Go nonce := make([]byte, cipher.NonceSize()) if _, err := rand.Read(nonce); err != nil { panic(err) } es := EncryptedString{ KeyID: keyID, Algorithm: "aes-gcm-hkdf", Nonce: nonce, Data: cipher.Seal(nil, nonce, data, nil), } ``` It seems the gosec tool requires using `rand.Read`. I changed the `cipher.NonceSize()` back to `12` (just in case it a numerical constant for a reason) and it started failing again. I think it also checks that cipher.NonceSize() is used as well, just doesn't report that. I ultimately decided to ignore this so there was no changes to crypto functions given the existing code is correct. Co-authored-by: Chris Stockton <[email protected]>
@expp121 Did you have a chance to look at this an try to improve it? |
I removed all the decryption functions/methods from the check. It only makes sense to flag an issue when the rule finds an hardcoded value in the code. I think if the value is provided as an input via an option or read from an external source, it is not worth to flag it, since I think will generate too much noise. We need to make sure that the rule is narrowed down only to the real hardcoded values. |
The code to generate an OpenSSH-compatible encrypted private key might go something like:
Where BcryptPbkdfKey is effectively golang.org/x/crypto/ssh/internal/bcrypt_pbkdf. This code generates the following warning:
G407 (CWE-1204): Use of hardcoded IV/nonce for encryption by passing hardcoded slice/array (Confidence: HIGH, Severity: HIGH)
Probably because of the assignment from
k
?The text was updated successfully, but these errors were encountered: