Skip to content

Commit 448439e

Browse files
authored
Merge pull request github#15294 from atorralba/atorralba/go/insecure-randomness-index-flowstep
Go: Recognize unsafe candidate selection in `go/insecure-randomness`
2 parents 6945289 + 31c11ad commit 448439e

File tree

5 files changed

+43
-2
lines changed

5 files changed

+43
-2
lines changed

go/ql/lib/semmle/go/security/InsecureRandomness.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ module InsecureRandomness {
4444
predicate isSink(DataFlow::Node sink) { isSinkWithKind(sink, _) }
4545

4646
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
47+
48+
predicate isBarrierOut(DataFlow::Node node) { isSink(node) }
49+
50+
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
51+
// Allow flow from tainted indexes to the base expression.
52+
// Randomly selecting a character/substring/integer from a predefined set
53+
// with a weak RNG is also a security risk if the result is used in
54+
// a sensitive function.
55+
n1.asExpr() = n2.asExpr().(IndexExpr).getIndex() and
56+
(
57+
n2.getType() instanceof StringType or
58+
n2.getType() instanceof IntegerType
59+
)
60+
}
4761
}
4862

4963
/**

go/ql/lib/semmle/go/security/InsecureRandomnessCustomizations.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ module InsecureRandomness {
4040
* Gets an interface outside of the `crypto` package which is the same as an
4141
* interface in the `crypto` package.
4242
*/
43-
string nonCryptoInterface() { result = ["io.Writer", "io.Reader", "sync.Mutex", "net.Listener"] }
43+
string nonCryptoInterface() {
44+
result = ["io.Writer", "io.Reader", "sync.Map", "sync.Mutex", "net.Listener"]
45+
}
4446

4547
/**
4648
* A cryptographic algorithm.
@@ -57,6 +59,7 @@ module InsecureRandomness {
5759
not (pkg = "crypto/rand" and name = "Read") and
5860
// `crypto/cipher` APIs for reading/writing encrypted streams
5961
not (pkg = "crypto/cipher" and name = ["Read", "Write"]) and
62+
not (pkg = "crypto/tls" and name = ["Client", "Dial", "DialWithDialer"]) and
6063
// Some interfaces in the `crypto` package are the same as interfaces
6164
// elsewhere, e.g. tls.listener is the same as net.Listener
6265
not fn.hasQualifiedName(nonCryptoInterface(), _) and
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The query `go/insecure-randomness` now recognizes the selection of candidates from a predefined set using a weak RNG when the result is used in a sensitive operation. Also, false positives have been reduced by adding more sink exclusions for functions in the `crypto` package not related to cryptographic operations.

go/ql/test/query-tests/Security/CWE-338/InsecureRandomness/InsecureRandomness.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ edges
1010
| sample.go:33:2:33:6 | definition of nonce | sample.go:37:32:37:36 | nonce |
1111
| sample.go:34:12:34:40 | call to New | sample.go:35:14:35:19 | random |
1212
| sample.go:35:14:35:19 | random | sample.go:33:2:33:6 | definition of nonce |
13+
| sample.go:55:17:55:42 | call to Intn | sample.go:56:29:56:38 | randNumber |
14+
| sample.go:56:11:56:40 | type conversion | sample.go:58:32:58:43 | type conversion |
15+
| sample.go:56:18:56:39 | index expression | sample.go:56:11:56:40 | type conversion |
16+
| sample.go:56:29:56:38 | randNumber | sample.go:56:18:56:39 | index expression |
1317
nodes
1418
| InsecureRandomness.go:12:18:12:40 | call to Intn | semmle.label | call to Intn |
1519
| sample.go:15:10:15:64 | call to Sum256 | semmle.label | call to Sum256 |
@@ -29,10 +33,16 @@ nodes
2933
| sample.go:45:17:45:39 | call to Intn | semmle.label | call to Intn |
3034
| sample.go:46:17:46:39 | call to Intn | semmle.label | call to Intn |
3135
| sample.go:47:17:47:39 | call to Intn | semmle.label | call to Intn |
36+
| sample.go:55:17:55:42 | call to Intn | semmle.label | call to Intn |
37+
| sample.go:56:11:56:40 | type conversion | semmle.label | type conversion |
38+
| sample.go:56:18:56:39 | index expression | semmle.label | index expression |
39+
| sample.go:56:29:56:38 | randNumber | semmle.label | randNumber |
40+
| sample.go:58:32:58:43 | type conversion | semmle.label | type conversion |
3241
subpaths
3342
#select
3443
| InsecureRandomness.go:12:18:12:40 | call to Intn | InsecureRandomness.go:12:18:12:40 | call to Intn | InsecureRandomness.go:12:18:12:40 | call to Intn | A password-related function depends on a $@ generated with a cryptographically weak RNG. | InsecureRandomness.go:12:18:12:40 | call to Intn | random number |
3544
| sample.go:26:25:26:30 | call to Guid | sample.go:15:49:15:61 | call to Uint32 | sample.go:26:25:26:30 | call to Guid | This cryptographic algorithm depends on a $@ generated with a cryptographically weak RNG. | sample.go:15:49:15:61 | call to Uint32 | random number |
3645
| sample.go:37:25:37:29 | nonce | sample.go:34:12:34:40 | call to New | sample.go:37:25:37:29 | nonce | This cryptographic algorithm depends on a $@ generated with a cryptographically weak RNG. | sample.go:34:12:34:40 | call to New | random number |
3746
| sample.go:37:32:37:36 | nonce | sample.go:34:12:34:40 | call to New | sample.go:37:32:37:36 | nonce | This cryptographic algorithm depends on a $@ generated with a cryptographically weak RNG. | sample.go:34:12:34:40 | call to New | random number |
3847
| sample.go:43:17:43:39 | call to Intn | sample.go:43:17:43:39 | call to Intn | sample.go:43:17:43:39 | call to Intn | A password-related function depends on a $@ generated with a cryptographically weak RNG. | sample.go:43:17:43:39 | call to Intn | random number |
48+
| sample.go:58:32:58:43 | type conversion | sample.go:55:17:55:42 | call to Intn | sample.go:58:32:58:43 | type conversion | This cryptographic algorithm depends on a $@ generated with a cryptographically weak RNG. | sample.go:55:17:55:42 | call to Intn | random number |

go/ql/test/query-tests/Security/CWE-338/InsecureRandomness/sample.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,19 @@ func encrypt(data []byte, password string) []byte {
4141
func makePasswordFiveChar() string {
4242
s := make([]rune, 5)
4343
s[0] = charset[rand.Intn(len(charset))] // BAD: weak RNG used to generate salt
44-
s[1] = charset[rand.Intn(len(charset))] // Rest OK because the only the first result is caught
44+
s[1] = charset[rand.Intn(len(charset))] // Rest OK because only the first result is caught
4545
s[2] = charset[rand.Intn(len(charset))]
4646
s[3] = charset[rand.Intn(len(charset))]
4747
s[4] = charset[rand.Intn(len(charset))]
4848
return string(s)
4949
}
50+
51+
func generateRandomKey() ed25519.PrivateKey {
52+
candidates := "0123456789ABCDEF"
53+
seed := ""
54+
for i := 0; i < ed25519.SeedSize; i++ {
55+
randNumber := rand.Intn(len(candidates))
56+
seed += string(candidates[randNumber])
57+
}
58+
return ed25519.NewKeyFromSeed([]byte(seed)) // BAD: seed candidates were selected with a weak RNG
59+
}

0 commit comments

Comments
 (0)