Skip to content

Java: Improve modelling of Spring requests, flow steps and XSS sinks #3653

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 38 commits into from
Jul 8, 2020

Conversation

lcartey
Copy link
Contributor

@lcartey lcartey commented Jun 9, 2020

As part of some recent customer engagements I spent some time improving our modelling of Spring sources, flow steps and XSS sink.

The identification of trusted/untrusted data and XSS sinks is primarily taken from the Spring reference documentation for request mapping handler methods, which has a detailed breakdown of how parameters and return values are treated based on the type/annotations.

Review highlights:

  • This adds some general taint steps for flow out of Maps and Lists. This is required because the tainted parameters to a Spring request method may be either of these types, and we need to track flow out of them. One concern here is whether this can result in spurious flow through these types, although I believe this will not happen in practice because we generally don't track flow into them (i.e. we don't have List.add etc. as a taint step).
  • I've also added a taint step for String.replace, as I had a customer provided benchmark which required flow through an inadequate replace call. If we retain this step, we may need to add some sanitizers to the XSS (and other) queries for "safe" looking replaces.
  • Spring has a mechanism whereby complex datatypes can automatically be populated from the request parameters or the request body (and thus contain user-provided data). For example, if parameters are annotated with @RequestBody:
    @PostMapping
    public void createUser(@RequestBody User user) {
      ...
    }
    The User class will be populated from JSON provided in the request body, with fields populated recursively. To capture this aspect I have introduced the concept of SpringUntrustedDataType, and added taint flow steps through getter methods on these classes. I also wrote a helper (stripType) to unwrap generics (so that we can identify type parameters on lists etc. as also untrusted).

Work that still needs to be done:

  • Performance testing - I've only tried this on small to medium sized projects.
  • Verification that the additional taint steps do not create false positives in other projects.
  • Tidy up the QL

This addresses points 1-3 from: https://github.com/github/codeql-java-team/issues/9

@lcartey lcartey requested a review from a team as a code owner June 9, 2020 10:08
@aschackmull
Copy link
Contributor

  • This adds some general taint steps for flow out of Maps and Lists. This is required because the tainted parameters to a Spring request method may be either of these types, and we need to track flow out of them. One concern here is whether this can result in spurious flow through these types, although I believe this will not happen in practice because we generally don't track flow into them (i.e. we don't have List.add etc. as a taint step).

This is not entirely accurate. We do already have default taint steps for flow in and out of collections and maps, see https://github.com/github/codeql/blob/master/java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll for details about what we currently include.

@aschackmull
Copy link
Contributor

@aschackmull
Copy link
Contributor

Could you rebase onto latest master, please? That would make it a bit easier to get a working Difference job running.

lcartey added 21 commits June 16, 2020 09:50
 - Recognise @<httpverb>Mapping as well as @RequestMapping.
 - Identify tainted/not tainted parameters of RequestMapping methods.
 - Identify ModelMaps correctly
 - Add extra not tainted param types (Pageable)
 - Identify ModelAttributes
 - Only track if the body is a String type, as that is the only type at
   risk of XSS.
Look for Spring request methods which return a String value which may be
coerced into a text/html output.
Methods annotated with a produces field which indicates a safe
content-type should not be considered XSS sinks. For example:

@RequestMapping(..., produces = "application/json")
Model the datatypes that may be populated on demand from request
parameters.
 - Also improve unwrapping of lists/arrays/maps etc.
@lcartey lcartey force-pushed the java/improve-spring-support branch from 60c536d to 2978af3 Compare June 16, 2020 08:51
@aschackmull
Copy link
Contributor

Annoyingly, rebases don't provide notifications. In any case: https://jenkins.internal.semmle.com/job/Changes/job/Java-Differences/797/

@aschackmull
Copy link
Contributor

The Differences job failed for some reason. Retrying: https://jenkins.internal.semmle.com/job/Changes/job/Java-Differences/815/

@aschackmull
Copy link
Contributor

or
m.getDeclaringType().getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", "List") and
(
m.getName().regexpMatch("get|toArray|subList|spliterator|set|iterator|listIterator") or
Copy link
Contributor

Choose a reason for hiding this comment

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

@aschackmull Wouldn't it be better to replace these regexMatch calls with something like :

m.hasName([ "get", "toArray", ...])

@aschackmull
Copy link
Contributor

I've made a bunch of suggested changes here: lcartey#1

@aschackmull
Copy link
Contributor

The differences job shows a 3% time increase for jdk, so that looks acceptable.

@aschackmull
Copy link
Contributor

One of the individual commit comments (7d555a7) suggests that some of the added steps are specific to XSS. Is this the case? If so, then we should move them to the XSS query.

@aschackmull
Copy link
Contributor

3 tests are failing due to parameters marked with @RequestParam no longer is enough to be considered a taint source - the parameter must now also belong to a SpringRequestMappingMethod. @lcartey could you verify that this is intentional? Because then I guess we'll just need to add @RequestMapping annotations in those 3 tests or something along those lines.

@lcartey
Copy link
Contributor Author

lcartey commented Jul 7, 2020

One of the individual commit comments (7d555a7) suggests that some of the added steps are specific to XSS. Is this the case? If so, then we should move them to the XSS query.

I don't think they are specific to XSS, but they are most likely to be seen with XSS. I think they are fine as general taint steps.

@lcartey
Copy link
Contributor Author

lcartey commented Jul 7, 2020

3 tests are failing due to parameters marked with @RequestParam no longer is enough to be considered a taint source - the parameter must now also belong to a SpringRequestMappingMethod. @lcartey could you verify that this is intentional? Because then I guess we'll just need to add @RequestMapping annotations in those 3 tests or something along those lines.

Yes, this is intentional. @RequestParam is not sufficient - there must be an @RequestMapping annotation (or similar) on the method itself, and a @Controller (or similar) on the declaring class. We have seen false positives for XSS caused by annotating dead @RequestParams as untrusted data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants