Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1124816

Browse files
committed
fixing FPs in js/biased-cryptographic-random
1 parent 5c31b94 commit 1124816

3 files changed

Lines changed: 105 additions & 23 deletions

File tree

javascript/ql/src/Security/CWE-327/BadRandomness.ql

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,49 @@
1111
*/
1212

1313
import javascript
14-
private import semmle.javascript.dataflow.InferredTypes
1514
private import semmle.javascript.dataflow.internal.StepSummary
1615
private import semmle.javascript.security.dataflow.InsecureRandomnessCustomizations
1716

17+
/**
18+
* Gets a number that is a power of 2.
19+
*/
20+
private int powerOfTwo() {
21+
result = 1
22+
or
23+
result = 2 * powerOfTwo() and
24+
not result < 0
25+
}
26+
27+
/**
28+
* Gets a node that has value 2^n for some n.
29+
*/
30+
private DataFlow::Node isPowerOfTwo() {
31+
exists(DataFlow::Node prev |
32+
prev.getIntValue() = powerOfTwo()
33+
or
34+
// Getting around the 32 bit ints in QL. These are some hex values of the form 0x10000000
35+
prev.asExpr().(NumberLiteral).getValue() =
36+
["281474976710656", "17592186044416", "1099511627776", "68719476736", "4294967296"]
37+
|
38+
result = prev.getASuccessor*()
39+
)
40+
}
41+
42+
/**
43+
* Gets a node that has value (2^n)-1 for some n.
44+
*/
45+
private DataFlow::Node isPowerOfTwoMinusOne() {
46+
exists(DataFlow::Node prev |
47+
prev.getIntValue() = powerOfTwo() - 1
48+
or
49+
// Getting around the 32 bit ints in QL. These are some hex values of the form 0xfffffff
50+
prev.asExpr().(NumberLiteral).getValue() =
51+
["281474976710655", "17592186044415", "1099511627775", "68719476735", "4294967295"]
52+
|
53+
result = prev.getASuccessor*()
54+
)
55+
}
56+
1857
/**
1958
* Gets a Buffer/TypedArray containing cryptographically secure random numbers.
2059
*/
@@ -73,11 +112,23 @@ private DataFlow::Node goodRandom(DataFlow::TypeTracker t, DataFlow::SourceNode
73112
result = CollectionsTypeTracking::collectionStep(goodRandom(t2, source), t, t2)
74113
)
75114
or
76-
InsecureRandomness::isAdditionalTaintStep(goodRandom(t.continue(), source), result)
115+
InsecureRandomness::isAdditionalTaintStep(goodRandom(t.continue(), source), result) and
116+
// bit shifts and multiplication by powers of two are generally used for constructing larger numbers from smaller numbers.
117+
not exists(BinaryExpr binop | binop = result.asExpr() |
118+
binop.getOperator().regexpMatch(".*(<|>).*")
119+
or
120+
binop.getOperator() = "*" and
121+
(
122+
isPowerOfTwo().asExpr() = binop.getAnOperand() or
123+
binop.getAnOperand().(NumberLiteral).getValue().regexpMatch("0x0*10*")
124+
)
125+
or
126+
binop.getOperator() = "+" and exists(binop.getAnOperand().getStringValue()) // string concat does not produce a number
127+
)
77128
}
78129

79130
/**
80-
* Gets a reference to a cryptographically random number produced by `source`.
131+
* Gets a reference to a cryptographically secure random number produced by `source`.
81132
*/
82133
DataFlow::Node goodRandom(DataFlow::SourceNode source) {
83134
result = goodRandom(DataFlow::TypeTracker::end(), source)
@@ -99,10 +150,13 @@ DataFlow::Node badCrypto(string description, DataFlow::SourceNode source) {
99150
)
100151
)
101152
or
102-
// division - always bad
153+
// division - bad if result is rounded.
103154
exists(DivExpr div | result.asExpr() = div |
104155
goodRandom(source).asExpr() = div.getLeftOperand() and
105-
description = "division"
156+
description = "division and rounding the result" and
157+
not div.getRightOperand() = isPowerOfTwoMinusOne().asExpr() and // division by (2^n)-1 most of the time produces a uniformly random number between 0 and 1.
158+
div.getParentExpr+() =
159+
DataFlow::globalVarRef("Math").getAMemberCall(["round", "floor", "ceil"]).asExpr()
106160
)
107161
or
108162
// modulo - only bad if not by a power of 2 - and the result is not checked for bias
@@ -111,7 +165,7 @@ DataFlow::Node badCrypto(string description, DataFlow::SourceNode source) {
111165
goodRandom(source) = random and
112166
random.asExpr() = mod.getLeftOperand() and
113167
// division by a power of 2 is OK. E.g. if `x` is uniformly random is in the range [0..255] then `x % 32` is uniformly random in the range [0..31].
114-
not mod.getRightOperand().getIntValue() = [2, 4, 8, 16, 32, 64, 128] and
168+
not mod.getRightOperand() = isPowerOfTwo().asExpr() and
115169
// not exists a comparison that checks if the result is potentially biased.
116170
not exists(BinaryExpr comparison | comparison.getOperator() = [">", "<", "<=", ">="] |
117171
AccessPath::getAnAliasedSourceNode(random.getALocalSource())
@@ -138,4 +192,4 @@ DataFlow::Node badCrypto(string description, DataFlow::SourceNode source) {
138192
from DataFlow::Node node, string description, DataFlow::SourceNode source
139193
where node = badCrypto(description, source)
140194
select node, "Using " + description + " on a $@ produces biased results.", source,
141-
"cryptographically random number"
195+
"cryptographically secure random number"
Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
| bad-random.js:3:11:3:61 | crypto. ... s(1)[0] | Using addition on a $@ produces biased results. | bad-random.js:3:11:3:31 | crypto. ... ytes(1) | cryptographically random number |
2-
| bad-random.js:3:11:3:61 | crypto. ... s(1)[0] | Using addition on a $@ produces biased results. | bad-random.js:3:38:3:58 | crypto. ... ytes(1) | cryptographically random number |
3-
| bad-random.js:4:11:4:61 | crypto. ... s(1)[0] | Using multiplication on a $@ produces biased results. | bad-random.js:4:11:4:31 | crypto. ... ytes(1) | cryptographically random number |
4-
| bad-random.js:4:11:4:61 | crypto. ... s(1)[0] | Using multiplication on a $@ produces biased results. | bad-random.js:4:38:4:58 | crypto. ... ytes(1) | cryptographically random number |
5-
| bad-random.js:9:28:9:43 | buffer[i] / 25.6 | Using division on a $@ produces biased results. | bad-random.js:6:16:6:40 | crypto. ... (bytes) | cryptographically random number |
6-
| bad-random.js:11:17:11:31 | buffer[i] % 100 | Using modulo on a $@ produces biased results. | bad-random.js:6:16:6:40 | crypto. ... (bytes) | cryptographically random number |
7-
| bad-random.js:14:11:14:63 | Number( ... (0, 3)) | Using string concatenation on a $@ produces biased results. | bad-random.js:14:25:14:45 | crypto. ... ytes(3) | cryptographically random number |
8-
| bad-random.js:73:32:73:42 | byte / 25.6 | Using division on a $@ produces biased results. | bad-random.js:70:20:70:44 | crypto. ... (bytes) | cryptographically random number |
9-
| bad-random.js:75:21:75:30 | byte % 100 | Using modulo on a $@ produces biased results. | bad-random.js:70:20:70:44 | crypto. ... (bytes) | cryptographically random number |
10-
| bad-random.js:81:11:81:51 | secureR ... (10)[0] | Using addition on a $@ produces biased results. | bad-random.js:81:11:81:26 | secureRandom(10) | cryptographically random number |
11-
| bad-random.js:81:11:81:51 | secureR ... (10)[0] | Using addition on a $@ produces biased results. | bad-random.js:81:33:81:48 | secureRandom(10) | cryptographically random number |
12-
| bad-random.js:85:11:85:35 | goodRan ... Random2 | Using addition on a $@ produces biased results. | bad-random.js:83:23:83:38 | secureRandom(10) | cryptographically random number |
13-
| bad-random.js:85:11:85:35 | goodRan ... Random2 | Using addition on a $@ produces biased results. | bad-random.js:84:23:84:38 | secureRandom(10) | cryptographically random number |
14-
| bad-random.js:87:16:87:24 | bad + bad | Using addition on a $@ produces biased results. | bad-random.js:83:23:83:38 | secureRandom(10) | cryptographically random number |
15-
| bad-random.js:87:16:87:24 | bad + bad | Using addition on a $@ produces biased results. | bad-random.js:84:23:84:38 | secureRandom(10) | cryptographically random number |
1+
| bad-random.js:3:11:3:61 | crypto. ... s(1)[0] | Using addition on a $@ produces biased results. | bad-random.js:3:11:3:31 | crypto. ... ytes(1) | cryptographically secure random number |
2+
| bad-random.js:3:11:3:61 | crypto. ... s(1)[0] | Using addition on a $@ produces biased results. | bad-random.js:3:38:3:58 | crypto. ... ytes(1) | cryptographically secure random number |
3+
| bad-random.js:4:11:4:61 | crypto. ... s(1)[0] | Using multiplication on a $@ produces biased results. | bad-random.js:4:11:4:31 | crypto. ... ytes(1) | cryptographically secure random number |
4+
| bad-random.js:4:11:4:61 | crypto. ... s(1)[0] | Using multiplication on a $@ produces biased results. | bad-random.js:4:38:4:58 | crypto. ... ytes(1) | cryptographically secure random number |
5+
| bad-random.js:9:28:9:43 | buffer[i] / 25.6 | Using division and rounding the result on a $@ produces biased results. | bad-random.js:6:16:6:40 | crypto. ... (bytes) | cryptographically secure random number |
6+
| bad-random.js:11:17:11:31 | buffer[i] % 100 | Using modulo on a $@ produces biased results. | bad-random.js:6:16:6:40 | crypto. ... (bytes) | cryptographically secure random number |
7+
| bad-random.js:14:11:14:63 | Number( ... (0, 3)) | Using string concatenation on a $@ produces biased results. | bad-random.js:14:25:14:45 | crypto. ... ytes(3) | cryptographically secure random number |
8+
| bad-random.js:73:32:73:42 | byte / 25.6 | Using division and rounding the result on a $@ produces biased results. | bad-random.js:70:20:70:44 | crypto. ... (bytes) | cryptographically secure random number |
9+
| bad-random.js:75:21:75:30 | byte % 100 | Using modulo on a $@ produces biased results. | bad-random.js:70:20:70:44 | crypto. ... (bytes) | cryptographically secure random number |
10+
| bad-random.js:81:11:81:51 | secureR ... (10)[0] | Using addition on a $@ produces biased results. | bad-random.js:81:11:81:26 | secureRandom(10) | cryptographically secure random number |
11+
| bad-random.js:81:11:81:51 | secureR ... (10)[0] | Using addition on a $@ produces biased results. | bad-random.js:81:33:81:48 | secureRandom(10) | cryptographically secure random number |
12+
| bad-random.js:85:11:85:35 | goodRan ... Random2 | Using addition on a $@ produces biased results. | bad-random.js:83:23:83:38 | secureRandom(10) | cryptographically secure random number |
13+
| bad-random.js:85:11:85:35 | goodRan ... Random2 | Using addition on a $@ produces biased results. | bad-random.js:84:23:84:38 | secureRandom(10) | cryptographically secure random number |
14+
| bad-random.js:87:16:87:24 | bad + bad | Using addition on a $@ produces biased results. | bad-random.js:83:23:83:38 | secureRandom(10) | cryptographically secure random number |
15+
| bad-random.js:87:16:87:24 | bad + bad | Using addition on a $@ produces biased results. | bad-random.js:84:23:84:38 | secureRandom(10) | cryptographically secure random number |
16+
| bad-random.js:90:29:90:54 | secureR ... / 25.6 | Using division and rounding the result on a $@ produces biased results. | bad-random.js:90:29:90:44 | secureRandom(10) | cryptographically secure random number |
17+
| bad-random.js:96:29:96:58 | crypto. ... ] / 100 | Using division and rounding the result on a $@ produces biased results. | bad-random.js:96:29:96:49 | crypto. ... ytes(1) | cryptographically secure random number |

javascript/ql/test/query-tests/Security/CWE-327/bad-random.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,30 @@ var goodRandom1 = 5 + secureRandom(10)[0];
8484
var goodRandom2 = 5 + secureRandom(10)[0];
8585
var bad = goodRandom1 + goodRandom2; // NOT OK
8686

87-
var dontFlag = bad + bad; // OK - the operands have already been flagged - but flagged anyway due to us not detecting that [INCONSISTENCY].
87+
var dontFlag = bad + bad; // OK - the operands have already been flagged - but flagged anyway due to us not detecting that [INCONSISTENCY].
88+
89+
var good = secureRandom(10)[0] / 0xff; // OK - result is not rounded.
90+
var good = Math.ceil(0.5 - (secureRandom(10)[0] / 25.6)); // NOT OK - division generally introduces bias.
91+
92+
var good = (crypto.randomBytes(1)[0] << 8) + crypto.randomBytes(3)[0]; // OK - bit shifts are usually used to construct larger/smaller numbers,
93+
94+
var good = Math.floor(max * (crypto.randomBytes(1)[0] / 0xff)); // OK - division by 0xff (255) gives a uniformly random number between 0 and 1.
95+
96+
var bad = Math.floor(max * (crypto.randomBytes(1)[0] / 100)); // NOT OK - division by 100 gives bias.
97+
98+
var crb = crypto.randomBytes(4);
99+
var cryptoRand = 0x01000000 * crb[0] + 0x00010000 * crb[1] + 0x00000100 * crb[2] + 0x00000001 * crb[3]; // OK - producing a larger number from smaller numbers.
100+
101+
var good = (secureRandom(10)[0] + "foo") + (secureRandom(10)[0] + "bar"); // OK - string concat
102+
103+
var eight = 8;
104+
var good = crypto.randomBytes(4)[0] % eight; // OK - modulo by power of 2.
105+
106+
var twoHundredAndFiftyFive = 0xff;
107+
var good = Math.floor(max * (crypto.randomBytes(1)[0] / twoHundredAndFiftyFive)); // OK - division by 0xff (255) gives a uniformly random number between 0 and 1.
108+
109+
var a = crypto.randomBytes(10);
110+
var good = ((a[i] & 31) * 0x1000000000000) + (a[i + 1] * 0x10000000000) + (a[i + 2] * 0x100000000) + (a[i + 3] * 0x1000000) + (a[i + 4] << 16) + (a[i + 5] << 8) + a[i + 6]; // OK - generating a large number from smaller bytes.
111+
var good = (a[i] * 0x100000000) + a[i + 6]; // OK - generating a large number from smaller bytes.
112+
var good = (a[i + 2] * 0x10000000) + a[i + 6]; // OK - generating a large number from smaller bytes.
113+
var foo = 0xffffffffffff + 0xfffffffffff + 0xffffffffff + 0xfffffffff + 0xffffffff + 0xfffffff + 0xffffff

0 commit comments

Comments
 (0)