-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Java: Static initialization vector #6357
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
Conversation
- Added StaticInitializationVector.ql - Added StaticInitializationVector.qhelp - Added 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.
Hopefully the following review comments are useful.
The maintainers can probably provide a more in-depth review.
java/ql/src/experimental/Security/CWE/CWE-1204/StaticInitializationVector.qhelp
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-1204/StaticInitializationVector.ql
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
*/ | ||
private class StaticByteArrayCreation extends ArrayCreationExpr { | ||
StaticByteArrayCreation() { | ||
this.getType().(Array).getElementType().(PrimitiveType).getName() = "byte" and |
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.
Might be better to use getComponentType()
instead of getElementType()
, because the above would also hold for byte[][][]
while with getComponentType()
it would only hold for byte[]
.
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 expression doesn't check the dimension. Both methods work fine here, I don't really see why getComponentType()
might be better here. The docs for getElementType()
says:
Gets the type of the elements used to construct this array type.
For example, the element type of Object[][] is Object.
That's exactly what is necessary here.
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.
But will a multi-dimensional byte
array (e.g. byte[][][]
), or one of its sub arrays, ever be used as initialization vector (and would the other parts of this query even handle that correctly)?
Because StaticByteArrayCreation
is currently matching that, e.g. new byte[5][]
.
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 Marcono is right, you really do only want byte[]
here
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 didn't consider multi-dimensional arrays. I think it may make sense to support them. For example, an application can use a set of static IVs like this:
byte[][] ivs = new byte[][] {
new byte[] { 1, 2, 3 ... },
new byte[] { 4, 5, 6, ...}
...
}
...
GCMParameterSpec ivSpec = new GCMParameterSpec(128, ivs[i]);
I added test cases encryptWithOneOfStaticIvs01
, encryptWithOneOfStaticIvs02
and encryptWithOneOfStaticZeroIvs
that use multi-dimensional arrays, and updated the initializedWithConstants()
predicate.
array.getInit().getAnInit().getAChildExpr() instanceof IntegerLiteral
covers the following case:
byte[][] ivs = new byte[][] {
new byte[8],
new byte[16]
};
This expression looks weird to me, but I could not find a better way to cover the case above. While debugging this case, I saw the child elements of the ArrayInit
s are instances of IntegerLiteral
and TypeAccess
. Please let me know if there is a better way.
StaticByteArrayCreation
still uses getElementType()
because it wants to check only the type but not dimensions. If we decide to cover only one-dimension arrays, then we can use getElementType()
. I would prefer to support multi-dimensional arrays though.
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.
While debugging this case, I saw the child elements of the ArrayInits are instances of IntegerLiteral and TypeAccess.
The VSCode extension AST viewer shows that for me as well, and manually quering ArrayCreationExpr
and ArrayInit
seems to confirm that.
To me that looks like a bug in the extractor, that ArrayCreationExpr
inside an ArrayInit
is modelled as (malformed) ArrayInit
. Might be worth creating an issue for this?
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.
To me that looks like a bug in the extractor, that ArrayCreationExpr inside an ArrayInit is modelled as (malformed) ArrayInit. Might be worth creating an issue for this?
Yes, I also expected to see ArrayCreationExpr
inside. @smowton Does it look like a bug? Or, are we missing something?
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.
Looks like a bug
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.
Created #6552 (comment)
) | ||
or | ||
exists(StaticMethodAccess ma | | ||
ma.getMethod().hasQualifiedName("java.util", "Arrays", "copyOf") and |
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 is not really updating an existing array, instead it creates a copy of it. Might be good to add a comment here explaining the rationale since otherwise the class name ArrayUpdate
is rather misleading.
If you want to cover copying an existing array then this might also have to consider Arrays.copyOfRange
and calls to clone()
of an array type.
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.
Also interested in the answer to this
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 is not really updating an existing array, instead it creates a copy of it. Might be good to add a comment here explaining the rationale since otherwise the class name
ArrayUpdate
is rather misleading.
The class ArrayUpdate
covers two cases:
- when content of the array is updated, for example, by
System.arraycopy()
- when the array reference is updated, for example, by
Arrays.copyOf()
I can update the qldoc for the class with the above. I can also split the class to two -- one class for each case.
I agree that ArrayUpdate
may look misleading. Unfortunately, I didn't find a better name. Do you have a better name?
If you want to cover copying an existing array then this might also have to consider
Arrays.copyOfRange
and calls toclone()
of an array type.
Sure, I can cover these methods as well.
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 don't think you need to model any of these functions: they naturally interrupt dataflow from a static array to its usage by overwriting the variable or field the reference is stored in.
If a dataflow path is found anyhow then most likely there is a control-flow route around the assignment and it's genuinely possible for the static array to be used as an IV.
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 don't think you need to model any of these functions: they naturally interrupt dataflow from a static array to its usage by overwriting the variable or field the reference is stored in.
If I remember correctly, while testing the query, these functions didn't interrupt dataflow. That sometimes resulted in some false-positives. But I might be missing something. I did a quick test and looks like you're right. I'll remove these functions.
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Show resolved
Hide resolved
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
*/ | ||
private class StaticByteArrayCreation extends ArrayCreationExpr { | ||
StaticByteArrayCreation() { | ||
this.getType().(Array).getElementType().(PrimitiveType).getName() = "byte" and |
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 Marcono is right, you really do only want byte[]
here
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
) | ||
or | ||
exists(StaticMethodAccess ma | | ||
ma.getMethod().hasQualifiedName("java.util", "Arrays", "copyOf") and |
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.
Also interested in the answer to this
} | ||
|
||
override predicate isSanitizer(DataFlow::Node node) { | ||
exists(ArrayUpdate update | update.getArray() = node.asExpr()) |
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.
Considering the existing side-condition on your source that it never flows to an array update, why this sanitizer? Can you construct any case where both sanitizer and side-condition are required? If they're redundant, it's best to drop the side-condition and just use the sanitizer.
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.
The side-condition helps to get rid of some false-positives. For example, see encryptWithRandomIvByteByByte()
test. Unfortunately, the sanitizer doesn't help here. But you're right, the sanitizer is redundant. I'll remove it.
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
@Marcono1234 @smowton Thanks for the review. I've addressed your comments. I'm going to file a bug for this soon. |
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll
Outdated
Show resolved
Hide resolved
Co-authored-by: Chris Smowton <[email protected]>
Co-authored-by: Chris Smowton <[email protected]>
Co-authored-by: Chris Smowton <[email protected]>
be4773e
to
1dd4bf0
Compare
When a cipher is used in certain modes, it needs an initialization vector (IV). IVs are used to randomize the encryption, therefore they should be unique and ideally unpredictable. Otherwise, the same plaintexts result in same ciphertexts under a given secret key. If a static IV is used for encryption, this lets an attacker learn if the same data pieces are transfered or stored, or this can help the attacker run a dictionary attack.
I'd like to add a query detecting static IVs:
Cipher.init()
calls.Random
instead ofSecureRandom
.At first, the query used to be much simpler. Then, I tested it on ~1000 open source projects which uncovered several FPs. Fixing those FPs made the query a bit more complex.
The query finds the following vulnerabilities: