Skip to content

Ruby: Add rb/weak-cryptographic-algorithm query #8421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 5, 2022

Conversation

alexrford
Copy link
Contributor

Ports py/weak-cryptographic-algorithm to Ruby. Currently supports OpenSSL ciphers.

This is generally similar, using the Cryptography::CryptographicOperation concept. There is a slight tweak in moving the isWeak() predicate into CryptographicOperation rather than only having it on CryptographicAlgorithm, which allows us to account for operations with ciphers that use a weak block mode (in practice, this means ECB). The CryptographicAlgorithm only includes the block encryption algorithm itself, not the block mode in the context where the algorithm is used in a stream cipher.

The side-effect of this is that the alert messages can currently be misleading, e.g. suggesting that the block encryption algorithm is weak when it is actually the block mode.

The best solutions to this that I can see are:

  1. Keeping CryptographicAlgorithm and the block mode separate, but introducing a proper BlockMode class with e.g. an isWeak() predicate of its own. CryptographicOperation could then have a BlockMode getBlockMode() predicate rather than its own isWeak() predicate, and the query could check both the encryption algorithm and the block mode.
  2. Include the block mode in the CryptographicAlgorithm, and add 2 predicates - isWeakEncryptionAlgorithm and isWeakBlockMode, to that class. Then CryptographicOperation#isWeak() could be removed.

@github-actions
Copy link
Contributor

QHelp previews:

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

Use of a broken or weak cryptographic algorithm

Using broken or 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, or flawed. 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, such as AES-128 or RSA-2048.

Example

The following code uses the OpenSSL library to encrypt some secret data. When you create a cipher using OpenSSL you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second example uses AES, which is a stronger modern algorithm.

require 'openssl'

class Encryptor
  attr_accessor :secret_key

  def encrypt_message_weak(message)
    cipher = OpenSSL::Cipher.new('des') # BAD: weak encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end

  def encrypt_message_strong(message)
    cipher = OpenSSL::Cipher::AES128.new # GOOD: strong encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end
end

References

@alexrford alexrford marked this pull request as ready for review March 14, 2022 08:39
@alexrford alexrford requested a review from a team as a code owner March 14, 2022 08:39
@github-actions
Copy link
Contributor

QHelp previews:

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

Use of a broken or weak cryptographic algorithm

Using broken or 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, or flawed. 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, such as AES-128 or RSA-2048.

Example

The following code uses the OpenSSL library to encrypt some secret data. When you create a cipher using OpenSSL you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second example uses AES, which is a stronger modern algorithm.

require 'openssl'

class Encryptor
  attr_accessor :secret_key

  def encrypt_message_weak(message)
    cipher = OpenSSL::Cipher.new('des') # BAD: weak encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end

  def encrypt_message_strong(message)
    cipher = OpenSSL::Cipher::AES128.new # GOOD: strong encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end
end

References

Copy link
Contributor

@aibaars aibaars left a comment

Choose a reason for hiding this comment

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

This looks good to me.

@@ -762,3 +762,58 @@ module Logging {
abstract DataFlow::Node getAnInput();
}
}

/**
* Provides models for cryptographic things.
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps things -> concepts.

Comment on lines 439 to 440
|
cipher.matchesName(cipherName)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can be moved to line 381 .

exists(string cipherName |  cipher.matchesName(cipherName) |
    ...
    or
    ...
)

// CBC is used by default
cipherMode.isBlockMode("CBC")
or
// `OpenSSL::Cipher::AES` instantiations
Copy link
Contributor

Choose a reason for hiding this comment

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

This is quite a large block of text and I wonder if it would improve readability if split into smaller predicates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've split this into 4 separate predicates that each cover a subset of cases, I think it's slightly clearer.

@github-actions
Copy link
Contributor

QHelp previews:

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

Use of a broken or weak cryptographic algorithm

Using broken or 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, or flawed. 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, such as AES-128 or RSA-2048.

Example

The following code uses the OpenSSL library to encrypt some secret data. When you create a cipher using OpenSSL you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second example uses AES, which is a stronger modern algorithm.

require 'openssl'

class Encryptor
  attr_accessor :secret_key

  def encrypt_message_weak(message)
    cipher = OpenSSL::Cipher.new('des') # BAD: weak encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end

  def encrypt_message_strong(message)
    cipher = OpenSSL::Cipher::AES128.new # GOOD: strong encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end
end

References

@alexrford alexrford requested a review from aibaars March 20, 2022 20:22
Copy link
Contributor

@nickrolfe nickrolfe left a comment

Choose a reason for hiding this comment

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

Sorry, I know you already had a review and were just looking for re-approval, but I have a couple of comments.

@@ -0,0 +1,18 @@
| broken_crypto.rb:4:8:4:34 | call to new | The cryptographic algorithm DES is broken or weak, and should not be used. |
| broken_crypto.rb:8:1:8:18 | call to update | The cryptographic algorithm DES is broken or weak, and should not be used. |
| broken_crypto.rb:12:8:12:43 | call to new | The cryptographic algorithm AES is broken or weak, and should not be used. |
Copy link
Contributor

Choose a reason for hiding this comment

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

It's a bit of a shame that this message doesn't mention the specific cipher mode that makes it weak. Would that be difficult to add?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, the inaccurate message here is not great. At the level of CryptographicOperation, we have no concept of the block mode, so the weakness/strength of the operation is just a kind of black box. The solutions that I've considered are:

  1. Keeping CryptographicAlgorithm and the block mode separate, but introducing a proper BlockMode class with e.g. an isWeak() predicate of its own. CryptographicOperation could then have a BlockMode getBlockMode() predicate rather than its own isWeak() predicate, and the query could check both the encryption algorithm and the block mode.
  2. Include the block mode in the CryptographicAlgorithm, and add 2 predicates - isWeakEncryptionAlgorithm and isWeakBlockMode, to that class. Then CryptographicOperation#isWeak() could be removed.

I think I prefer 1 overall as it seems like the intention of the CryptographicAlgorithm class (shared with JS and Python) is to represent the algorithm modulo any block mode. Do you have any preferences between these?

Copy link
Contributor

Choose a reason for hiding this comment

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

1 sounds like a nice solution, if it's not too difficult to implement.

@github-actions
Copy link
Contributor

QHelp previews:

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

Use of a broken or weak cryptographic algorithm

Using broken or 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, or flawed. 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, such as AES-128 or RSA-2048.

Example

The following code uses the OpenSSL library to encrypt some secret data. When you create a cipher using OpenSSL you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second example uses AES, which is a stronger modern algorithm.

require 'openssl'

class Encryptor
  attr_accessor :secret_key

  def encrypt_message_weak(message)
    cipher = OpenSSL::Cipher.new('des') # BAD: weak encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end

  def encrypt_message_strong(message)
    cipher = OpenSSL::Cipher::AES128.new # GOOD: strong encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end
end

References

@github-actions
Copy link
Contributor

QHelp previews:

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

Use of a broken or weak cryptographic algorithm

Using broken or 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, or flawed. 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, such as AES-128 or RSA-2048.

Example

The following code uses the OpenSSL library to encrypt some secret data. When you create a cipher using OpenSSL you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second example uses AES, which is a stronger modern algorithm.

require 'openssl'

class Encryptor
  attr_accessor :secret_key

  def encrypt_message_weak(message)
    cipher = OpenSSL::Cipher.new('des') # BAD: weak encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end

  def encrypt_message_strong(message)
    cipher = OpenSSL::Cipher::AES128.new # GOOD: strong encryption
    cipher.encrypt
    cipher.key = secret_key
    cipher.update(message)
    cipher.final
  end
end

References

Copy link
Contributor

@nickrolfe nickrolfe left a comment

Choose a reason for hiding this comment

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

LGTM. I'll leave it up to you whether you address my comment about the block mode in the alert message.

@alexrford
Copy link
Contributor Author

I'll merge this as is and work on the block mode changes as a follow-up. CryptographicOperation is partly shared with Python - it would be nice to align these so the query implementation can be identical.

@alexrford alexrford merged commit ccd7bb5 into github:main Apr 5, 2022
@alexrford alexrford deleted the ruby/weak-cryptographic-algorithm branch April 5, 2022 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants