Skip to content
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

Java: Add/improve insecure trustmanager query #4879

Merged
merged 28 commits into from
Jun 25, 2021

Conversation

intrigus-lgtm
Copy link
Contributor

This can be rebased once #4771 is merged.

@smowton
Copy link
Contributor

smowton commented Jan 4, 2021

Waiting for #4771 before reviewing this

@kevinbackhouse
Copy link
Contributor

Hi @intrigus-lgtm. It looks like #4771 has been merged. Are you able to rebase this now?

@intrigus-lgtm
Copy link
Contributor Author

@kevinbackhouse sure! But I don't have time right now, probably later this day.

@intrigus-lgtm intrigus-lgtm dismissed a stale review via e5c748f January 15, 2021 16:34
@intrigus-lgtm intrigus-lgtm force-pushed the java/improve-trustmanager branch 2 times, most recently from e5c748f to 6c2475a Compare January 16, 2021 13:08
@intrigus-lgtm
Copy link
Contributor Author

@kevinbackhouse @smowton this has been rebased :)

Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

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

How about instead of implementing custom logic for barrier guards to pick out the various "if(acceptAllCertificates) ..." cases, we treat context.getSocketFactory as the sink and init(...) as a taint propagator? That way we'll pick out cases where the if(acceptAll) ... logic happens in a parent function, while the flow from instantiating a bad trust manager to calling context.init happens entirely within a child function. By lengthening the passage of code we consider as the source -> sink path, we should be able to use Configuraton.isBarrierGuard/isSanitizerGuard instead of implementing our own guard.controls logic.

If the <code>checkServerTrusted</code> method of a <code>TrustManager</code> never throws a <code>CertificateException</code> it trusts every certificate.
This allows an attacker to perform a Man-in-the-middle attack against the application therefore breaking any security Transport Layer Security (TLS) gives.

An attack would look like this:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
An attack would look like this:
An example attack:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has not been applied.
Instead it has been changed to "An attack might look like this:".
Hope this is ok.

3. Java calls the <code>checkServerTrusted</code> method to check whether it should trust the certificate.
4. The <code>checkServerTrusted</code> method of your <code>TrustManager</code> does not throw a <code>CertificateException</code>.
5. Java proceeds with the connection since your <code>TrustManager</code> implicitly trusted it by not throwing an exception.
6. The attacker can now read the data your program sends to <code>https://example.com</code> and/or alter its replies while the program thinks the connection is secure.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
6. The attacker can now read the data your program sends to <code>https://example.com</code> and/or alter its replies while the program thinks the connection is secure.
6. The attacker can now read the data your program sends to <code>https://example.com</code> and/or alter its replies while the user might expect that the connection is secure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, ok let's leave this the way it is for now and when I get a moment I'll see if that query can be improved.

java/ql/src/Security/CWE/CWE-295/InsecureTrustManager.ql Outdated Show resolved Hide resolved
java/ql/src/Security/CWE/CWE-295/InsecureTrustManager.ql Outdated Show resolved Hide resolved
java/ql/src/Security/CWE/CWE-295/InsecureTrustManager.ql Outdated Show resolved Hide resolved
java/ql/src/Security/CWE/CWE-295/InsecureTrustManager.ql Outdated Show resolved Hide resolved
}

/** Holds if `node` is guarded by a flag that suggests an intentionally insecure feature. */
private predicate isNodeGuardedByFlag(DataFlow::Node node) {
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 reimplementing the Configuration.isBarrierGuard concept

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So what do you suggest going forward?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll see if I can fit this into a barrier-guard tomorrow.

@intrigus-lgtm
Copy link
Contributor Author

How about instead of implementing custom logic for barrier guards to pick out the various "if(acceptAllCertificates) ..." cases, we treat context.getSocketFactory as the sink and init(...) as a taint propagator? That way we'll pick out cases where the if(acceptAll) ... logic happens in a parent function, while the flow from instantiating a bad trust manager to calling context.init happens entirely within a child function. By lengthening the passage of code we consider as the source -> sink path, we should be able to use Configuraton.isBarrierGuard/isSanitizerGuard instead of implementing our own guard.controls logic.

How strong do you feel about this?
If you want this changed, I guess this should change as well?
https://github.com/github/codeql/blob/main/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql#L109-L152

@intrigus-lgtm
Copy link
Contributor Author

@smowton I applied most of your suggestions besides to suggestions in the .qhelp file.
Let me know your thoughts as this section is very similar to the InsecureHostnameVerification.qhelp file.

Suggestions on the barrier stuff are also unresolved, as it is not yet clear how we want to proceed.

@smowton
Copy link
Contributor

smowton commented Jan 28, 2021

OK, if you don't want to pursue that you don't have to; I'll start a benchmark run with the query as-is

Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

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

There are also various unaddressed comments here. They don't all have to be applied, but please do respond to them

java/ql/src/Security/CWE/CWE-295/InsecureTrustManager.ql Outdated Show resolved Hide resolved
@intrigus-lgtm
Copy link
Contributor Author

There are also various unaddressed comments here. They don't all have to be applied, but please do respond to them

Will do, but I'm currently lacking a continuous chunk of time needed for evaluating the barrier guard stuff which could make some comments outdated.

@felicitymay felicitymay removed their request for review February 22, 2021 08:38
@felicitymay
Copy link
Contributor

felicitymay commented Feb 22, 2021

👋🏻 I've removed myself as a reviewer so that I don't get nagged about reviewing this PR every morning 😄

@github/codeql-java When this is ready for a docs team review, please ping the new @github/docs-content-codeql team and a member of the team will be in touch.

@intrigus-lgtm
Copy link
Contributor Author

intrigus-lgtm commented Mar 24, 2021

Sorry for the long wait.

I've experimented a bit with the query.

  1. A query without any filtering, run only on projects that were previously identified when run on all of lgtm.com.
    557 of 557 hits.
  2. A query with all filtering applied, that is, filtering based on source/sink enclosing callable name and filtering based on flag guards.
    473 of 551 hits.
  3. A query with partial filtering, that is, only filtering based on flag guards.
    528 of 551 hits.
  4. A query based on this idea with no filtering.
    400 of 551 hits.
  5. A query based on this idea with partial filtering, that is, only filtering based on flag guards.
    387 of 551 hits.

With filtering on flag guards I mean code like this:

if(enableHttps) { 
  sink();
}

Re:

  1. Nothing unexpected here.
  2. It would be possible to further reduce the number of hits, by improving the filtering based on source/sink names.
  3. This shows a small reduction of hits. It has the big advantage that I don't have to guess the intention of the developer.
    In the case of source/sink filtering, if a method is named disableSSLSecurity, why is it named this way?
    a) This method may be for external (=user controlled) consumption only. This should not be detected.
    b) The dev wanted to create a re-usable method (for their code only) and always calls it. This should be detected.
    But I'm pretty sure that it's not possible to differentiate between a) and b), because how would one determine whether something is for external consumption?
  4. This shows a reduction, although there are probably sinks where the sink is not sslContext.getSocketFactory() but a call to another API that only receives the sslContext.
  5. A small further reduction.

Links for the query runs have been privately shared with @smowton.

@smowton
Copy link
Contributor

smowton commented Mar 25, 2021

Which version do you want to submit for consideration for a bounty?

@intrigus-lgtm
Copy link
Contributor Author

Which version do you want to submit for consideration for a bounty?

The best one :D

But I don't know what you would consider best.
Approach 2 can likely be tuned to filter most results that are likely FPs, but the downside would be that it may also filter TPs.
3 has more FPs by design but will likely not loose TPs.
4 & 5 don't really show a reduction of FPs. Just an overall reduction of results, I think.

@smowton
Copy link
Contributor

smowton commented Mar 25, 2021

I don't have the time to assess 5 variants to decide which is best -- you should pick one. To assess which is doing a better job you could use the LGTM API to determine which projects differ between the different versions and take a closer look at those ones. Then you'll know whether version 4 or 5 is actually useful or whether your guess that they're just producing less results is correct.

intrigus and others added 23 commits June 25, 2021 16:47
This removes the sanitizer part that classified some results as FP
if the results were in methods with certain names, like
`disableVerification()`. I now think that it's a bad idea to filter
based on the method name.
The custom flow steps in `flagFlowStep` are now listed explicitly.
Simplified check whether a method throws an exception.
Previously intentionally unsafe methods such as `disableCertificate`
would be ignored by this query. But now they will also be flagged
as it is hard to guess intentions...
Adjust the tests to account for this change.
Co-authored-by: Anders Schack-Mulligen <[email protected]>
Move comments to separate lines to improve
the rendering in the finished query help.
@intrigus-lgtm
Copy link
Contributor Author

intrigus-lgtm commented Jun 25, 2021

Rebased.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Java no-change-note-required This PR does not need a change note ready-for-doc-review This PR requires and is ready for review from the GitHub docs team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants