Skip to content

Conversation

@owen-mc
Copy link
Contributor

@owen-mc owen-mc commented Oct 21, 2025

Added a new query, go/weak-crypto-algorithm, to detect the use of a broken or weak cryptographic algorithm. A very simple version of this query was originally contributed as an experimental query by @dilanbhalla.

@owen-mc owen-mc requested a review from a team as a code owner October 21, 2025 15:17
Copilot AI review requested due to automatic review settings October 21, 2025 15:17
@owen-mc owen-mc requested a review from a team as a code owner October 21, 2025 15:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR promotes the go/weak-crypto-algorithm query from experimental to standard by implementing comprehensive modeling of cryptographic operations in Go. The query now detects when sensitive data is used with broken or weak cryptographic algorithms (DES, 3DES, RC4, MD5, SHA1) across various crypto libraries and block cipher modes.

Key changes:

  • Completely rewrote cryptographic operation modeling using the shared Concepts library
  • Added support for block cipher modes (CBC, CTR, CFB, OFB, GCM) and stream operations
  • Extended coverage to include hash operations with proper initialization tracking
  • Added comprehensive test cases covering multiple encryption and hashing scenarios

Reviewed Changes

Copilot reviewed 20 out of 23 changed files in this pull request and generated no comments.

Show a summary per file
File Description
go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql New production query file that replaces the experimental query
go/ql/lib/semmle/go/security/BrokenCryptoAlgorithmQuery.qll New taint-tracking configuration for the query
go/ql/lib/semmle/go/security/BrokenCryptoAlgorithmCustomizations.qll New customizations defining sources, sinks, and sanitizers
go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll Comprehensive modeling of Go crypto libraries with algorithm and block mode tracking
go/ql/lib/semmle/go/Concepts.qll Added cryptography concepts including algorithm types and operation abstractions
go/ql/lib/qlpack.yml Added dependency on codeql/concepts shared library
go/ql/lib/go.qll Imported CryptoLibraries framework
shared/concepts/codeql/concepts/internal/CryptoAlgorithmNames.qll Added SHA512224 and SHA512256 to strong hashing algorithms
go/ql/test/query-tests/Security/CWE-327/Crypto.go Comprehensive test cases for various cryptographic operations
go/ql/src/experimental/CWE-327/* Removed old experimental query files
go/ql/test/query-tests/Security/CWE-327/CryptoAlgorithm.ql New test helper for validating CryptographicOperation modeling
go/ql/src/change-notes/2025-10-21-go-weak-crypto-algorithm.md Change note documenting the new query
@github-actions
Copy link
Contributor

github-actions bot commented Oct 21, 2025

QHelp previews:

go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp

Use of a broken or weak cryptographic algorithm

Using weak cryptographic algorithms can leave data vulnerable to being decrypted or forged by an attacker.

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.

Recommendation

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.

Example

The following code uses the different packages to encrypt some secret data. The first example uses DES, which is an older algorithm that is now considered weak. The following example uses AES, which is a stronger, more modern algorithm.

package main import ( "crypto/aes" "crypto/des" ) func EncryptMessageWeak(key []byte, message []byte) (dst []byte) { // BAD, DES is a weak crypto algorithm block, _ := des.NewCipher(key) block.Encrypt(dst, message) return } func EncryptMessageStrong(key []byte, message []byte) (dst []byte) { // GOOD, AES is a weak crypto algorithm block, _ := aes.NewCipher(key) block.Encrypt(dst, message) return }

References

go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp

Use of a broken or weak cryptographic hashing algorithm on sensitive data

Using a broken or weak cryptographic hash function can leave data vulnerable, and should not be used in security related code.

A strong cryptographic hash function should be resistant to:

  • pre-image attacks: if you know a hash value h(x), you should not be able to easily find the input x.
  • collision attacks: if you know a hash value h(x), you should not be able to easily find a different input y with the same hash value h(x) = h(y).
    In cases with a limited input space, such as for passwords, the hash function also needs to be computationally expensive to be resistant to brute-force attacks. Passwords should also have an unique salt applied before hashing, but that is not considered by this query.

As an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.

Since it's OK to use a weak cryptographic hash function in a non-security context, this query only alerts when these are used to hash sensitive data (such as passwords, certificates, usernames).

Use of broken or weak cryptographic algorithms that are not hashing algorithms, is handled by the rb/weak-cryptographic-algorithm query.

Recommendation

Ensure that you use a strong, modern cryptographic hash function:

  • such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.
  • such as SHA-2, or SHA-3 in other cases.

Example

The following example shows two functions for checking whether the hash of a secret matches a known value. The first function uses SHA-1 that is known to be vulnerable to collision attacks. The second function uses SHA-256 that is a strong cryptographic hashing function.

package main import ( "crypto/sha1" "crypto/sha256" "slices" ) func SecretMatchesKnownHashBad(secret []byte, known_hash []byte) bool { // BAD, SHA1 is a weak crypto algorithm and secret is sensitive data h := sha1.New() return slices.Equal(h.Sum(secret), known_hash) } func SecretMatchesKnownHashGood(secret []byte, known_hash []byte) bool { // GOOD, SHA256 is a strong hashing algorithm h := sha256.New() return slices.Equal(h.Sum(secret), known_hash) }

Example

The following example shows two functions for hashing passwords. The first example uses SHA-256 to hash passwords. Although SHA-256 is a strong cryptographic hash function, it is not suitable for password hashing since it is not computationally expensive. The second function uses PBKDF2, which is a strong password hashing algorithm.

package main import ( "crypto/pbkdf2" "crypto/rand" "crypto/sha256" "crypto/sha512" ) func GetPasswordHashBad(password string) [32]byte { // BAD, SHA256 is a strong hashing algorithm but it is not computationally expensive return sha256.Sum256([]byte(password)) } func GetPasswordHashGood(password string) []byte { // GOOD, PBKDF2 is a strong hashing algorithm and it is computationally expensive salt := make([]byte, 16) rand.Read(salt) key, _ := pbkdf2.Key(sha512.New, password, salt, 4096, 32) return key }

References

ruby/ql/src/queries/security/cwe-327/WeakSensitiveDataHashing.qhelp

Use of a broken or weak cryptographic hashing algorithm on sensitive data

Using a broken or weak cryptographic hash function can leave data vulnerable, and should not be used in security related code.

A strong cryptographic hash function should be resistant to:

  • pre-image attacks: if you know a hash value h(x), you should not be able to easily find the input x.
  • collision attacks: if you know a hash value h(x), you should not be able to easily find a different input y with the same hash value h(x) = h(y).
    In cases with a limited input space, such as for passwords, the hash function also needs to be computationally expensive to be resistant to brute-force attacks. Passwords should also have an unique salt applied before hashing, but that is not considered by this query.

As an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.

Since it's OK to use a weak cryptographic hash function in a non-security context, this query only alerts when these are used to hash sensitive data (such as passwords, certificates, usernames).

Use of broken or weak cryptographic algorithms that are not hashing algorithms, is handled by the rb/weak-cryptographic-algorithm query.

Recommendation

Ensure that you use a strong, modern cryptographic hash function:

  • such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.
  • such as SHA-2, or SHA-3 in other cases.

Example

The following example shows two functions for checking whether the hash of a certificate matches a known value -- to prevent tampering. The first function uses SHA-1 that is known to be vulnerable to collision attacks. The second function uses SHA-256 that is a strong cryptographic hashing function.

require 'openssl' def certificate_matches_known_hash_bad(certificate, known_hash) hash = OpenSSL::Digest.new('SHA1').digest certificate hash == known_hash end def certificate_matches_known_hash_good(certificate, known_hash) hash = OpenSSL::Digest.new('SHA256').digest certificate hash == known_hash end

Example

The following example shows two functions for hashing passwords. The first function uses SHA-256 to hash passwords. Although SHA-256 is a strong cryptographic hash function, it is not suitable for password hashing since it is not computationally expensive.

require 'openssl' def get_password_hash(password, salt) OpenSSL::Digest.new('SHA256').digest(password + salt) # BAD end

The second function uses Argon2 (through the argon2 gem), which is a strong password hashing algorithm (and includes a per-password salt by default).

require 'argon2' def get_initial_hash(password) Argon2::Password.create(password) end def check_password(password, known_hash) Argon2::Password.verify_password(password, known_hash) end

References

@owen-mc owen-mc requested a review from a team as a code owner October 21, 2025 21:01
@github-actions github-actions bot added the Ruby label Oct 21, 2025
Location getASelectedSinkLocation(DataFlow::Node sink) {
result = sink.(Sink).getLocation()
or
result = sink.(Sink).getInitialization().getLocation()
Copy link
Contributor

@d10c d10c Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query at go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql only selects source and sink, not getInitialization(). Try removing this location override (getASelectedSinkLocation) altogether.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This has highlighted some differences from the existing queries for other languages which I wasn't aware of. I'll need to update the PR quite a lot, so I'm putting it back in draft now.

@owen-mc owen-mc marked this pull request as draft October 22, 2025 10:33
@owen-mc owen-mc force-pushed the go/promote-weak-crypto-algorithm branch from d231a17 to 9e04cf3 Compare October 31, 2025 16:03
Comment on lines +17 to +33
from Cryptography::CryptographicOperation operation, string msgPrefix, DataFlow::Node init
where
init = operation.getInitialization() and
// `init` may be a `BlockModeInit`, a `EncryptionAlgorithmInit`, or `operation` itself.
(
not init instanceof BlockModeInit and
exists(Cryptography::CryptographicAlgorithm algorithm |
algorithm = operation.getAlgorithm() and
algorithm.isWeak() and
msgPrefix = "The cryptographic algorithm " + algorithm.getName() and
not algorithm instanceof Cryptography::HashingAlgorithm
)
or
not init instanceof EncryptionAlgorithmInit and
operation.getBlockMode().isWeak() and
msgPrefix = "The block mode " + operation.getBlockMode()
)
select operation, "$@ is broken or weak, and should not be used.", init, msgPrefix

Check warning

Code scanning / CodeQL

Consistent alert message Warning

The go/weak-cryptographic-algorithm query does not have the same alert message as cpp.
@owen-mc owen-mc force-pushed the go/promote-weak-crypto-algorithm branch from f0e340c to d2cd96e Compare November 3, 2025 17:23
@owen-mc
Copy link
Contributor Author

owen-mc commented Nov 4, 2025

Results from running MRVA on 1000 go repos:

  • go/weak-cryptographic-algorithm: 14 repos had results. 54 results in total. Pretty much all were TPs - arguably one was doing decryption rather than encryption, which we try not to alert on, but with a stream cipher they use the same API so we can't detect that in CodeQL.
  • go/weak-sensitive-data-hashing: 73 repos had results. 437 results in total. I sampled a few and they all seemed to be TPs.
@owen-mc owen-mc marked this pull request as ready for review November 4, 2025 22:25
@owen-mc
Copy link
Contributor Author

owen-mc commented Nov 4, 2025

I ran a one-branch DCA experiment for these two queries on all the repos from MRVA that had results for them. It didn't show anything interesting, like bad join orders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

2 participants