-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Swift: Risky or Broken Cryptographic Algorithm Query #13649
base: main
Are you sure you want to change the base?
Conversation
QHelp previews: swift/ql/src/queries/Security/CWE-327/BrokenCryptoAlgorithm.qhelpUse of a broken or weak cryptographic algorithmUsing 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. This query alerts on any use of a weak cryptographic algorithm, that is not a hashing algorithm. Use of broken or weak cryptographic hash functions are handled by the RecommendationEnsure that you use a strong, modern cryptographic algorithm, such as AES-256 or RSA-2048. ExampleThe following code uses the
References
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, obviously we need to figure out a couple of realistic test cases (and perhaps some less realistic ones as well).
swift/ql/src/queries/Security/CWE-327/BrokenCryptoAlgorithm.qhelp
Outdated
Show resolved
Hide resolved
swift/ql/src/queries/Security/CWE-327/BrokenCryptoAlgorithm.qhelp
Outdated
Show resolved
Hide resolved
msgPrefix = "The cryptographic algorithm " + operation.getAlgorithm().getName() | ||
) | ||
or | ||
operation.getBlockMode().isWeak() and msgPrefix = "The block mode " + operation.getBlockMode() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There may be some overlap in results here with the swift/ecb-encryption
query - though that query currently only models a very narrow range of algorithms and does not use heuristics. We might want to sort this out later. But I suggest we make it a follow-up issue and don't let it distract from finishing the swift/weak-cryptographic-algorithm
query.
let SECRET_KEY = SymmetricKey(size: .bits256) | ||
|
||
func sendEncrypted(plaintext: Data, to channel: Channel) { | ||
channel.send(cipher.encrypt(plaintext)) // BAD: weak encryption |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is cipher
placeholder?
Also is Channel
a Swift class, third party library, or something we're just implying the software of the example has?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just a placeholder, there's nothing quite equivalent in Swift's standard library, which prefers Actors for cross-thread communication, so I'll just replace this with a return
since this query does not care about where ciphertext ends up getting used.
* Extend this class to model new APIs. If you want to refine existing API models, | ||
* extend `CryptographicOperation` instead. | ||
*/ | ||
abstract class Range extends DataFlow::Node { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we missing one or more implementations extending CryptographicOperation::Range
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's the extension point.
Btw, do you know if it's possible to specify them as Models-as-Data CSVs? I'm a bit unfamiliar with the API for those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you could define them as sources or sinks (by extending SourceModelCsv
/ SinkModelCsv
) and then use the sourceNode
/ sinkNode
predicate to select the corresponding DataFlow::Node
s. Morally I'm not sure they're quite the same thing as sources or sinks though.
Tests for this query on the CommonCrypto library here: #13870 |
I'm hoping to extend the dataflow to go from algorithm constants to the outputs of CommonCrypto encryption/decryption functions, through the CSV summaries, but I need to write test cases to make sure this actually works. Next steps: - Declare en/decryption outputs as subtypes of CommonCryptoOutputNode - Add test cases - Add also block mode constants to the source, so that they may be returned by getBlockMode().
} | ||
} | ||
|
||
private module AlgorithmUse = DataFlow::Global<AlgorithmUseConfig>; |
Check warning
Code scanning / CodeQL
Dead code
/** | ||
* A call expression that uses a `CCAlgorithm` constant through an argument, e.g. `CCryptorCreate(…)`. | ||
*/ | ||
private class AlgorithmUser extends CallExpr { |
Check warning
Code scanning / CodeQL
Dead code
";;false;CCCryptorCreate(_:_:_:_:_:_:_:);;;Argument[1];Argument[6];value", | ||
";;false;CCCryptorCreateFromData(_:_:_:_:_:_:_:_:_:_:);;;Argument[1];Argument[8];value", | ||
";;false;CCCryptorCreateWithMode(_:_:_:_:_:_:_:_:_:_:_:_:);;;Argument[1..2];Argument[10];value", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
";;false;CCCryptorCreate(_:_:_:_:_:_:_:);;;Argument[1];Argument[6];value", | |
";;false;CCCryptorCreateFromData(_:_:_:_:_:_:_:_:_:_:);;;Argument[1];Argument[8];value", | |
";;false;CCCryptorCreateWithMode(_:_:_:_:_:_:_:_:_:_:_:_:);;;Argument[1..2];Argument[10];value", | |
";;false;CCCryptorCreate(_:_:_:_:_:_:_:);;;Argument[1];Argument[6];taint", | |
";;false;CCCryptorCreateFromData(_:_:_:_:_:_:_:_:_:_:);;;Argument[1];Argument[8];taint", | |
";;false;CCCryptorCreateWithMode(_:_:_:_:_:_:_:_:_:_:_:_:);;;Argument[1..2];Argument[11];taint", |
I believe this should be (at most) taint flow, because someone whoever controls the algorithm choice substantially controls cryptorRef
- but cryptorRef
is not itself just a copy of the algorithm argument (which would be value flow).
You'll have to make AlgorithmUse
a TaintTracking::Global
flow however.
|
||
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { | ||
// Flow through cast expressions like `CCAlgorithm(kCCAlgorithmAES128)`. | ||
// TODO: turn this into a generally available flow step. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand it there's a slight risk that NumericalCastType
matches something that isn't truly a cast. But I imagine this will be very worthwhile overall. 👍
The trouble is it really should have it's own tests, so it might be better done as a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... do you mind if I grab your implementation of NumericalCastType
and do the generally available step myself? It looks like having this might fix an issue elsewhere (in the constant salt query tests).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inspired by this and some similar problems we've run into, I've created #13946 . It does not contain your code, just a number of specific models for Numeric
and similar classes (which may cover your needs, as really you're just dealing with Int
/ UInt32
). In any case we could add your model to Numeric.qll
quite easily now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to future developer working on this query: the isAdditionalFlowStep
and associated code should be removed from this PR. We've evolved this code and got these cases by default now.
} | ||
|
||
/** A data flow node for the output of a CommonCrypto encryption/decryption operation */ | ||
abstract private class CommonCryptoOutputNode extends CryptographicOperation::Range { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should just explicitly model CCCrypt
and CCCryptorUpdate
? It looks like you have everything you need to figure out which algorithm is used (at which point you should begin to get results on the tests), and I guess similar logic for the block mode.
No description provided.