Skip to content

Commit dec1e4d

Browse files
authored
Merge pull request github#12666 from smiddy007/improve-insufficient-pw-hash-query
JS: Improve insufficient pw hash query
2 parents ddb27e5 + 0eb61d3 commit dec1e4d

File tree

6 files changed

+94
-1
lines changed

6 files changed

+94
-1
lines changed
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 crypto-js module in `CryptoLibraries.qll` now supports progressive hashing with algo.update().

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

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import javascript
66
import semmle.javascript.Concepts::Cryptography
7+
private import semmle.javascript.security.internal.CryptoAlgorithmNames
78

89
/**
910
* A key used in a cryptographic algorithm.
@@ -274,6 +275,47 @@ private module NodeJSCrypto {
274275
* A model of the crypto-js library.
275276
*/
276277
private module CryptoJS {
278+
private class InstantiatedAlgorithm extends DataFlow::CallNode {
279+
private string algorithmName;
280+
281+
InstantiatedAlgorithm() {
282+
/*
283+
* ```
284+
* const crypto = require("crypto-js");
285+
* const cipher = crypto.algo.SHA256.create();
286+
* ```
287+
* matched as:
288+
* ```
289+
* const crypto = require("crypto-js");
290+
* const cipher = crypto.algo.<algorithmName>.create();
291+
* ```
292+
*/
293+
294+
exists(DataFlow::SourceNode mod, DataFlow::PropRead propRead |
295+
mod = DataFlow::moduleImport("crypto-js") and
296+
propRead = mod.getAPropertyRead("algo").getAPropertyRead() and
297+
this = propRead.getAMemberCall("create") and
298+
algorithmName = propRead.getPropertyName() and
299+
not isStrongPasswordHashingAlgorithm(algorithmName)
300+
)
301+
}
302+
303+
CryptographicAlgorithm getAlgorithm() { result.matchesName(algorithmName) }
304+
305+
private BlockMode getExplicitBlockMode() { result.matchesString(algorithmName) }
306+
307+
BlockMode getBlockMode() {
308+
isBlockEncryptionAlgorithm(this.getAlgorithm()) and
309+
(
310+
if exists(this.getExplicitBlockMode())
311+
then result = this.getExplicitBlockMode()
312+
else
313+
// CBC is the default if not explicitly specified
314+
result = "CBC"
315+
)
316+
}
317+
}
318+
277319
/**
278320
* Matches `CryptoJS.<algorithmName>` and `require("crypto-js/<algorithmName>")`
279321
*/
@@ -325,13 +367,44 @@ private module CryptoJS {
325367
input = result.getParameter(0)
326368
}
327369

370+
private API::CallNode getUpdatedApplication(API::Node input, InstantiatedAlgorithm instantiation) {
371+
/*
372+
* ```
373+
* var CryptoJS = require("crypto-js");
374+
* var hash = CryptoJS.algo.SHA256.create();
375+
* hash.update('message');
376+
* hash.update('password');
377+
* var hashInHex = hash.finalize();
378+
* ```
379+
* Matched as:
380+
* ```
381+
* var CryptoJS = require("crypto-js");
382+
* var hash = CryptoJS.algo.<algorithmName>.create();
383+
* hash.update(<input>);
384+
* hash.update(<input>);
385+
* var hashInHex = hash.finalize();
386+
* ```
387+
* Also matches where `CryptoJS.algo.<algorithmName>` has been
388+
* replaced by `require("crypto-js/<algorithmName>")`
389+
*/
390+
391+
result = instantiation.getAMemberCall("update") and
392+
input = result.getParameter(0)
393+
}
394+
328395
private class Apply extends CryptographicOperation::Range instanceof API::CallNode {
329396
API::Node input;
330397
CryptographicAlgorithm algorithm; // non-functional
331398

332399
Apply() {
333-
this = getEncryptionApplication(input, algorithm) or
400+
this = getEncryptionApplication(input, algorithm)
401+
or
334402
this = getDirectApplication(input, algorithm)
403+
or
404+
exists(InstantiatedAlgorithm instantiation |
405+
this = getUpdatedApplication(input, instantiation) and
406+
algorithm = instantiation.getAlgorithm()
407+
)
335408
}
336409

337410
override DataFlow::Node getAnInput() { result = input.asSink() }
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const crypto = require('crypto-js')
2+
function hashPassword(email, password) {
3+
var algo = crypto.algo.SHA512.create()
4+
algo.update(password, 'utf-8') // BAD
5+
algo.update(email.toLowerCase(), 'utf-8')
6+
var hash = algo.finalize()
7+
return hash.toString(crypto.enc.Base64)
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const crypto = require('crypto-js')
2+
function hashPassword(email, password) {
3+
var algo = crypto.algo.PBKDF2.create()
4+
algo.update(password, 'utf-8') // GOOD
5+
algo.update(email.toLowerCase(), 'utf-8')
6+
var hash = algo.finalize()
7+
return hash.toString(crypto.enc.Base64)
8+
}

0 commit comments

Comments
 (0)