Skip to content

Commit 1df8ec2

Browse files
committed
add insufficient key size model for node-forge
1 parent 62039b8 commit 1df8ec2

File tree

4 files changed

+68
-7
lines changed

4 files changed

+68
-7
lines changed

javascript/ql/lib/semmle/javascript/frameworks/CryptoLibraries.qll

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,39 @@ private module Forge {
544544
private class Key extends CryptographicKey {
545545
Key() { this = any(KeyCipher cipher).getKey() }
546546
}
547+
548+
private class CreateKey extends CryptographicKeyCreation, DataFlow::CallNode {
549+
CryptographicAlgorithm algorithm;
550+
551+
CreateKey() {
552+
// var cipher = forge.rc2.createEncryptionCipher(key, 128);
553+
this =
554+
getAnImportNode()
555+
.getAPropertyRead(any(string s | algorithm.matchesName(s)))
556+
.getAMemberCall("createEncryptionCipher")
557+
or
558+
// var key = forge.random.getBytesSync(16);
559+
// var cipher = forge.cipher.createCipher('AES-CBC', key);
560+
this =
561+
getAnImportNode()
562+
.getAPropertyRead("cipher")
563+
.getAMemberCall(["createCipher", "createDecipher"]) and
564+
algorithm.matchesName(this.getArgument(0).getStringValue())
565+
}
566+
567+
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
568+
569+
override int getSize() {
570+
result = this.getArgument(1).getIntValue()
571+
or
572+
exists(DataFlow::CallNode call | call.getCalleeName() = ["getBytes", "getBytesSync"] |
573+
getArgument(1).getALocalSource() = call and
574+
result = call.getArgument(0).getIntValue() * 8 // bytes to bits
575+
)
576+
}
577+
578+
override predicate isSymmetricKey() { any() }
579+
}
547580
}
548581

549582
/**

javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,22 @@ private module AlgorithmNames {
3131
}
3232

3333
predicate isStrongEncryptionAlgorithm(string name) {
34-
name = ["AES", "AES128", "AES192", "AES256", "AES512", "RSA", "RABBIT", "BLOWFISH"]
34+
name = [appendMode("AES"), "AES128", "AES192", "AES256", "AES512", "RSA", "RABBIT", "BLOWFISH"]
35+
}
36+
37+
/**
38+
* Gets the name with a mode of operation added as a suffix.
39+
*/
40+
bindingset[name]
41+
private string appendMode(string name) {
42+
result = name + ["", "CBC", "ECB", "CFB", "OFB", "CTR", "GCM"]
3543
}
3644

3745
predicate isWeakEncryptionAlgorithm(string name) {
3846
name =
3947
[
40-
"DES", "3DES", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR",
41-
"ARC5", "RC5"
48+
appendMode("DES"), appendMode("3DES"), "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2",
49+
"ARC4", "RC4", "ARCFOUR", "ARC5", "RC5"
4250
]
4351
}
4452

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
| tst.js:3:14:3:71 | crypto. ... 1024 }) | Creation of an asymmetric RSA key uses 1024 bits, which is below 2048 and considered breakable. |
22
| tst.js:7:14:7:59 | crypto. ... : 64 }) | Creation of an symmetric key uses 64 bits, which is below 128 and considered breakable. |
3-
| tst.js:14:14:14:56 | CryptoJ ... e: 2 }) | Creation of an symmetric PBKDF2 key uses 64 bits, which is below 128 and considered breakable. |
4-
| tst.js:15:14:15:60 | CryptoJ ... e: 2 }) | Creation of an symmetric PBKDF2 key uses 64 bits, which is below 128 and considered breakable. |
5-
| tst.js:16:14:16:60 | CryptoJ ... e: 2 }) | Creation of an symmetric EVPKDF key uses 64 bits, which is below 128 and considered breakable. |
3+
| tst.js:13:14:13:56 | CryptoJ ... e: 2 }) | Creation of an symmetric PBKDF2 key uses 64 bits, which is below 128 and considered breakable. |
4+
| tst.js:14:14:14:60 | CryptoJ ... e: 2 }) | Creation of an symmetric PBKDF2 key uses 64 bits, which is below 128 and considered breakable. |
5+
| tst.js:15:14:15:60 | CryptoJ ... e: 2 }) | Creation of an symmetric EVPKDF key uses 64 bits, which is below 128 and considered breakable. |
6+
| tst.js:19:12:19:57 | forge.r ... rd, 64) | Creation of an symmetric RC2 key uses 64 bits, which is below 128 and considered breakable. |
7+
| tst.js:26:12:26:53 | forge.c ... , key2) | Creation of an symmetric AESCBC key uses 64 bits, which is below 128 and considered breakable. |
8+
| tst.js:30:12:30:56 | forge.c ... , key3) | Creation of an symmetric 3DESCBC key uses 64 bits, which is below 128 and considered breakable. |

javascript/ql/test/query-tests/Security/CWE-326/tst.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,21 @@ var CryptoJS = require("crypto-js");
1313
const bad3 = CryptoJS.algo.PBKDF2.create({ keySize: 2 }); // NOT OK
1414
const bad4 = CryptoJS.PBKDF2(password, salt, { keySize: 2 }); // NOT OK
1515
const bad5 = CryptoJS.EvpKDF(password, salt, { keySize: 2 }); // NOT OK
16-
const bad4 = CryptoJS.PBKDF2(password, salt, { keySize: 8 }); // OK
16+
const bad6 = CryptoJS.PBKDF2(password, salt, { keySize: 8 }); // OK
17+
18+
const forge = require("node-forge");
19+
var bad7 = forge.rc2.createEncryptionCipher(password, 64); // NOT OK
20+
var good3 = forge.rc2.createEncryptionCipher(password, 128); // OK
21+
22+
var key1 = forge.random.getBytesSync(16);
23+
var good4 = forge.cipher.createCipher('AES-CBC', key1); // OK
24+
25+
var key2 = forge.random.getBytesSync(8);
26+
var bad8 = forge.cipher.createCipher('AES-CBC', key2); // NOT OK
27+
28+
var myBuffer = forge.util.createBuffer(manyBytes);
29+
var key3 = myBuffer.getBytes(8);
30+
var bad9 = forge.cipher.createDecipher('3DES-CBC', key3); // NOT OK
31+
32+
var key4 = myBuffer.getBytes(16);
33+
var good5 = forge.cipher.createDecipher('AES-CBC', key4); // OK

0 commit comments

Comments
 (0)