mirror of
https://github.com/github/codeql.git
synced 2026-01-31 07:12:57 +01:00
Merge pull request #284 from dilanbhalla/gocrypto
Adding Crypto Query/Library
This commit is contained in:
245
ql/src/experimental/CWE-327/CryptoLibraries.qll
Normal file
245
ql/src/experimental/CWE-327/CryptoLibraries.qll
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Provides classes for modeling cryptographic libraries.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* Names of cryptographic algorithms, separated into strong and weak variants.
|
||||
*
|
||||
* The names are normalized: upper-case, no spaces, dashes or underscores.
|
||||
*
|
||||
* The names are inspired by the names used in real world crypto libraries.
|
||||
*
|
||||
* The classification into strong and weak are based on OWASP and Wikipedia (2020).
|
||||
*
|
||||
* Sources (more links in qhelp file):
|
||||
* https://en.wikipedia.org/wiki/Strong_cryptography#Cryptographically_strong_algorithms
|
||||
* https://en.wikipedia.org/wiki/Strong_cryptography#Examples
|
||||
* https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
|
||||
*/
|
||||
private module AlgorithmNames {
|
||||
predicate isStrongHashingAlgorithm(string name) {
|
||||
name = "DSA" or
|
||||
name = "ED25519" or
|
||||
name = "ES256" or
|
||||
name = "ECDSA256" or
|
||||
name = "ES384" or
|
||||
name = "ECDSA384" or
|
||||
name = "ES512" or
|
||||
name = "ECDSA512" or
|
||||
name = "SHA2" or
|
||||
name = "SHA224" or
|
||||
name = "SHA256" or
|
||||
name = "SHA384" or
|
||||
name = "SHA512" or
|
||||
name = "SHA3"
|
||||
}
|
||||
|
||||
predicate isWeakHashingAlgorithm(string name) {
|
||||
name = "HAVEL128" or
|
||||
name = "MD2" or
|
||||
name = "MD4" or
|
||||
name = "MD5" or
|
||||
name = "PANAMA" or
|
||||
name = "RIPEMD" or
|
||||
name = "RIPEMD128" or
|
||||
name = "RIPEMD256" or
|
||||
name = "RIPEMD320" or
|
||||
name = "SHA0" or
|
||||
name = "SHA1"
|
||||
}
|
||||
|
||||
predicate isStrongEncryptionAlgorithm(string name) {
|
||||
name = "AES" or
|
||||
name = "AES128" or
|
||||
name = "AES192" or
|
||||
name = "AES256" or
|
||||
name = "AES512" or
|
||||
name = "RSA" or
|
||||
name = "RABBIT" or
|
||||
name = "BLOWFISH"
|
||||
}
|
||||
|
||||
predicate isWeakEncryptionAlgorithm(string name) {
|
||||
name = "DES" or
|
||||
name = "3DES" or
|
||||
name = "TRIPLEDES" or
|
||||
name = "TDEA" or
|
||||
name = "TRIPLEDEA" or
|
||||
name = "ARC2" or
|
||||
name = "RC2" or
|
||||
name = "ARC4" or
|
||||
name = "RC4" or
|
||||
name = "ARCFOUR" or
|
||||
name = "ARC5" or
|
||||
name = "RC5"
|
||||
}
|
||||
|
||||
predicate isStrongPasswordHashingAlgorithm(string name) {
|
||||
name = "ARGON2" or
|
||||
name = "PBKDF2" or
|
||||
name = "BCRYPT" or
|
||||
name = "SCRYPT"
|
||||
}
|
||||
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { none() }
|
||||
}
|
||||
|
||||
private import AlgorithmNames
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm.
|
||||
*/
|
||||
private newtype TCryptographicAlgorithm =
|
||||
MkHashingAlgorithm(string name, boolean isWeak) {
|
||||
isStrongHashingAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakHashingAlgorithm(name) and isWeak = true
|
||||
} or
|
||||
MkEncryptionAlgorithm(string name, boolean isWeak) {
|
||||
isStrongEncryptionAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakEncryptionAlgorithm(name) and isWeak = true
|
||||
} or
|
||||
MkPasswordHashingAlgorithm(string name, boolean isWeak) {
|
||||
isStrongPasswordHashingAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakPasswordHashingAlgorithm(name) and isWeak = true
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getName() }
|
||||
|
||||
/**
|
||||
* Gets the name of this algorithm.
|
||||
*/
|
||||
abstract string getName();
|
||||
|
||||
/**
|
||||
* Holds if the name of this algorithm matches `name` modulo case,
|
||||
* white space, dashes and underscores.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate matchesName(string name) {
|
||||
exists(name.regexpReplaceAll("[-_]", "").regexpFind("(?i)\\Q" + getName() + "\\E", _, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this algorithm is weak.
|
||||
*/
|
||||
abstract predicate isWeak();
|
||||
}
|
||||
|
||||
/**
|
||||
* A hashing algorithm such as `MD5` or `SHA512`.
|
||||
*/
|
||||
class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* An encryption algorithm such as `DES` or `AES512`.
|
||||
*/
|
||||
class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* A password hashing algorithm such as `PBKDF2` or `SCRYPT`.
|
||||
*/
|
||||
class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* An application of a cryptographic algorithm.
|
||||
*/
|
||||
abstract class CryptographicOperation extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the input the algorithm is used on, e.g. the plain text input to be encrypted.
|
||||
*/
|
||||
abstract Expr getInput();
|
||||
|
||||
/**
|
||||
* Gets the applied algorithm.
|
||||
*/
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Models cryptographic operations of the `crypto/md5` package.
|
||||
*/
|
||||
class Md5 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Md5() { getTarget().hasQualifiedName("crypto/md5", ["New", "Sum"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models cryptographic operations of the `crypto/sha1` package.
|
||||
*/
|
||||
class Sha1 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Sha1() { getTarget().hasQualifiedName("crypto/sha1", ["New", "Sum"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models cryptographic operations of the `crypto/des` package.
|
||||
*/
|
||||
class Des extends CryptographicOperation, DataFlow::CallNode {
|
||||
Des() { getTarget().hasQualifiedName("crypto/des", ["NewCipher", "NewTripleDESCipher"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models cryptographic operations of the `crypto/rc4` package.
|
||||
*/
|
||||
class Rc4 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Rc4() { getTarget().hasQualifiedName("crypto/rc4", ["NewCipher"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
55
ql/src/experimental/CWE-327/WeakCryptoAlgorithm.qhelp
Normal file
55
ql/src/experimental/CWE-327/WeakCryptoAlgorithm.qhelp
Normal file
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Using weak cryptographic algorithms can leave data
|
||||
vulnerable to being decrypted or forged by an attacker.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Many cryptographic algorithms provided by cryptography
|
||||
libraries are known to be weak. Using such an
|
||||
algorithm means that encrypted or hashed data is less
|
||||
secure than it appears to be.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Ensure that you use a strong, modern cryptographic
|
||||
algorithm. Use at least AES-128 or RSA-2048 for
|
||||
encryption, and SHA-2 or SHA-3 for secure hashing.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following code uses the different packages to encrypt/hash
|
||||
some secret data. The first few examples uses DES, MD5, RC4, and SHA1,
|
||||
which are older algorithms that are now considered weak. The following
|
||||
examples use AES and SHA256, which are stronger, more modern algorithms.
|
||||
</p>
|
||||
|
||||
<sample src="examples/Crypto.go" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a
|
||||
href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html">Cryptographic Storage Cheat Sheet</a>.
|
||||
</li>
|
||||
<li>Wikipedia: <a
|
||||
href="https://en.wikipedia.org/wiki/Strong_cryptography#Cryptographically_strong_algorithms">Cryptographically Strong Algorithms</a>.
|
||||
</li>
|
||||
<li>Wikipedia: <a
|
||||
href="https://en.wikipedia.org/wiki/Strong_cryptography#Examples">Strong Cryptography Examples</a>.
|
||||
</li>
|
||||
<li>NIST, FIPS 140 Annex a: <a href="http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf"> Approved Security Functions</a>.</li>
|
||||
<li>NIST, SP 800-131A: <a href="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf"> Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths</a>.</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
18
ql/src/experimental/CWE-327/WeakCryptoAlgorithm.ql
Normal file
18
ql/src/experimental/CWE-327/WeakCryptoAlgorithm.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Use of a weak cryptographic algorithm
|
||||
* @description Using weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/weak-crypto-algorithm
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
*/
|
||||
|
||||
import go
|
||||
import WeakCryptoAlgorithmCustomizations::WeakCryptoAlgorithm
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "$@ is used in a weak cryptographic algorithm.",
|
||||
source.getNode(), "Sensitive data"
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* sensitive information in weak cryptographic algorithms,
|
||||
* as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
import go
|
||||
private import semmle.go.security.SensitiveActions
|
||||
private import CryptoLibraries
|
||||
|
||||
module WeakCryptoAlgorithm {
|
||||
/**
|
||||
* A data flow source for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sensitive source.
|
||||
*/
|
||||
class SensitiveSource extends Source {
|
||||
SensitiveSource() { this.asExpr() instanceof SensitiveExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression used by a weak cryptographic algorithm.
|
||||
*/
|
||||
class WeakCryptographicOperationSink extends Sink {
|
||||
WeakCryptographicOperationSink() {
|
||||
exists(CryptographicOperation application |
|
||||
application.getAlgorithm().isWeak() and
|
||||
this.asExpr() = application.getInput()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration depicting taint flow from sensitive information to weak cryptographic algorithms.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "WeakCryptoAlgorithm" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
}
|
||||
}
|
||||
33
ql/src/experimental/CWE-327/examples/Crypto.go
Normal file
33
ql/src/experimental/CWE-327/examples/Crypto.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
func main() {
|
||||
password := []byte("password")
|
||||
|
||||
// BAD, des is a weak crypto algorithm
|
||||
des.NewTripleDESCipher(password)
|
||||
|
||||
// BAD, md5 is a weak crypto algorithm
|
||||
md5.Sum(password)
|
||||
|
||||
// BAD, rc4 is a weak crypto algorithm
|
||||
rc4.NewCipher(password)
|
||||
|
||||
// BAD, sha1 is a weak crypto algorithm
|
||||
sha1.Sum(password)
|
||||
|
||||
// GOOD, aes is a strong crypto algorithm
|
||||
aes.NewCipher(password)
|
||||
|
||||
// GOOD, sha256 is a strong crypto algorithm
|
||||
sha256.Sum256(password)
|
||||
|
||||
}
|
||||
53
ql/test/experimental/CWE-327/Crypto.go
Normal file
53
ql/test/experimental/CWE-327/Crypto.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
func main() {
|
||||
public := []byte("hello")
|
||||
|
||||
password := []byte("123456")
|
||||
buf := password // testing dataflow by passing into different variable
|
||||
|
||||
// BAD, des is a weak crypto algorithm and password is sensitive data
|
||||
des.NewTripleDESCipher(buf)
|
||||
|
||||
// BAD, md5 is a weak crypto algorithm and password is sensitive data
|
||||
md5.Sum(buf)
|
||||
|
||||
// BAD, rc4 is a weak crypto algorithm and password is sensitive data
|
||||
rc4.NewCipher(buf)
|
||||
|
||||
// BAD, sha1 is a weak crypto algorithm and password is sensitive data
|
||||
sha1.Sum(buf)
|
||||
|
||||
// GOOD, password is sensitive data but aes is a strong crypto algorithm
|
||||
aes.NewCipher(buf)
|
||||
|
||||
// GOOD, password is sensitive data but sha256 is a strong crypto algorithm
|
||||
sha256.Sum256(buf)
|
||||
|
||||
// GOOD, des is a weak crypto algorithm but public is not sensitive data
|
||||
des.NewTripleDESCipher(public)
|
||||
|
||||
// GOOD, md5 is a weak crypto algorithm but public is not sensitive data
|
||||
md5.Sum(public)
|
||||
|
||||
// GOOD, rc4 is a weak crypto algorithm but public is not sensitive data
|
||||
rc4.NewCipher(public)
|
||||
|
||||
// GOOD, sha1 is a weak crypto algorithm but public is not sensitive data
|
||||
sha1.Sum(public)
|
||||
|
||||
// GOOD, aes is a strong crypto algorithm and public is not sensitive data
|
||||
aes.NewCipher(public)
|
||||
|
||||
// GOOD, sha256 is a strong crypto algorithm and public is not sensitive data
|
||||
sha256.Sum256(public)
|
||||
}
|
||||
16
ql/test/experimental/CWE-327/WeakCryptoAlgorithm.expected
Normal file
16
ql/test/experimental/CWE-327/WeakCryptoAlgorithm.expected
Normal file
@@ -0,0 +1,16 @@
|
||||
edges
|
||||
| Crypto.go:16:9:16:16 | password : slice type | Crypto.go:19:25:19:27 | buf |
|
||||
| Crypto.go:16:9:16:16 | password : slice type | Crypto.go:22:10:22:12 | buf |
|
||||
| Crypto.go:16:9:16:16 | password : slice type | Crypto.go:25:16:25:18 | buf |
|
||||
| Crypto.go:16:9:16:16 | password : slice type | Crypto.go:28:11:28:13 | buf |
|
||||
nodes
|
||||
| Crypto.go:16:9:16:16 | password : slice type | semmle.label | password : slice type |
|
||||
| Crypto.go:19:25:19:27 | buf | semmle.label | buf |
|
||||
| Crypto.go:22:10:22:12 | buf | semmle.label | buf |
|
||||
| Crypto.go:25:16:25:18 | buf | semmle.label | buf |
|
||||
| Crypto.go:28:11:28:13 | buf | semmle.label | buf |
|
||||
#select
|
||||
| Crypto.go:19:25:19:27 | buf | Crypto.go:16:9:16:16 | password : slice type | Crypto.go:19:25:19:27 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
| Crypto.go:22:10:22:12 | buf | Crypto.go:16:9:16:16 | password : slice type | Crypto.go:22:10:22:12 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
| Crypto.go:25:16:25:18 | buf | Crypto.go:16:9:16:16 | password : slice type | Crypto.go:25:16:25:18 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
| Crypto.go:28:11:28:13 | buf | Crypto.go:16:9:16:16 | password : slice type | Crypto.go:28:11:28:13 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
1
ql/test/experimental/CWE-327/WeakCryptoAlgorithm.qlref
Normal file
1
ql/test/experimental/CWE-327/WeakCryptoAlgorithm.qlref
Normal file
@@ -0,0 +1 @@
|
||||
experimental/CWE-327/WeakCryptoAlgorithm.ql
|
||||
Reference in New Issue
Block a user