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

Adds load json ssrf mitigation #2467

Merged
merged 9 commits into from
Feb 2, 2022

Conversation

ncordon
Copy link
Contributor

@ncordon ncordon commented Jan 18, 2022

What

Uses Neo4j internal option unsupported.dbms.cypher_ip_blocklist, which consists on a list of ip addresses in CDIR notation, i.e. ip + mask for subnets, e.g. 192.167.2.3/8, 192.234.3.123/16. When using the apoc.load.json(url) command, the url will be looked up in a DNS resolver and we will fail if it is on the list of blocked ip addresses.

Why

Users can try to load data from any url (including internal ones), which could expose files the user does not have permissions to see from outside.

build.gradle Outdated
@@ -109,7 +109,7 @@ subprojects {

ext {
// NB: due to version.json generation by parsing this file, the next line must not have any if/then/else logic
neo4jVersion = "4.4.1"
neo4jVersion = "4.4.4-SNAPSHOT"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A new 4.4 version of the database needs to be released and substituted here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@ncordon If we don't use a non snapshot version we aren't able to test the PR in GH can you please change it to 4.4.3?

Copy link
Contributor Author

@ncordon ncordon Jan 31, 2022

Choose a reason for hiding this comment

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

The code we need is in 4.4.4, and to release 4.4.4 we are going to need this to be merged on red. The CI of the database will compile the APOC code overriding the version.

So even bumping this, don't expect the CI to pass.

After the database has been released, we can bump here to 4.4.4 and release a new version of APOC.

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 was done without the need to depend on 4.4.4 in the end

@ncordon
Copy link
Contributor Author

ncordon commented Jan 26, 2022

}

public static Stream<Object> loadJson(Object urlOrBinary, Map<String,Object> headers, String payload, String path, boolean failOnError, String compressionAlgo, List<String> options) {
public static Stream<Object> loadJson(Object urlOrBinary, Map<String,Object> headers, String payload, String path, boolean failOnError, String compressionAlgo, List<String> options, ApocConfig apocConfig) {
try {
if (urlOrBinary instanceof String) {
String url = (String) urlOrBinary;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this check be done in checkReadAllowed, where the other access rules as processed? With the check here, you can still make calls that violate the blocklist using apoc.load.xml, apoc.xml.import, apoc.load.xmlSimple, apoc.load.xls, apoc.cypher.runFiles and so on.

Similarly, we should process the blocklist in checkWriteAllowed, otherwise people can violate the blocklist by sending POST requests and other output network connections?

Copy link
Member

Choose a reason for hiding this comment

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

yes we should not pass the full apoc config through, either just what we need or better like Jake said handle it in the single place where we control the loading.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Also you don't need to pass apocConfig through but use ApocConfig.getConfig().methodCall()

there are already some methods for checking, we can handle the address as part of that.
so you would rather do something like ApocConfig#isIpAllowed(ip) or ApocConfig#isHostNameAllowed(hostname)
need to make sure though to resolve the final hostname properly to ip-address (after all the http-redirects are handled).

https://github.com/neo4j-contrib/neo4j-apoc-procedures/blob/4.3/core/src/main/java/apoc/ApocConfig.java#L250-L262

Copy link
Collaborator

Choose a reason for hiding this comment

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

@ncordon please look at the comments above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jexp I have addressed the concern for redirects in a pending PR: https://github.com/neo-technology/neo4j/pull/14171. The logic for the blocking lives in the kernel of the database and here I am reusing the same logic. So once that's merged we should be safe with respect to redirects (I hope).

@conker84 I get the need for checkReadAllowed, but checkWriteAllowed works only with filenames, which this blocklist doesn't include, judging by its signature?

Do we have any situation where we can do a POST to a path in a remote server?

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 been addressed.

With respect to checkWriteAllowed, we've deemed that change is not necessary as it only affects files. POST requests (load json ones for example) go through the checkReadAllowed.

Copy link
Member

Choose a reason for hiding this comment

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

write list also works with urls e.g. for s3 URLs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

checkWriteAllowed is only used with things that require a physical file I believe:
checkWriteAllowed

@ncordon ncordon force-pushed the 4.4-load-json-ssrf-mitigation branch from bcc6103 to a00abfc Compare January 28, 2022 11:30
Copy link
Collaborator

@conker84 conker84 left a comment

Choose a reason for hiding this comment

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

this should be not necessary
edit. github seems that cutted my review, lemme do that again

core/src/main/java/apoc/ApocConfig.java Outdated Show resolved Hide resolved
}

public static Stream<Object> loadJson(Object urlOrBinary, Map<String,Object> headers, String payload, String path, boolean failOnError, String compressionAlgo, List<String> options) {
public static Stream<Object> loadJson(Object urlOrBinary, Map<String,Object> headers, String payload, String path, boolean failOnError, String compressionAlgo, List<String> options, ApocConfig apocConfig) {
try {
if (urlOrBinary instanceof String) {
String url = (String) urlOrBinary;
Copy link
Collaborator

Choose a reason for hiding this comment

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

@ncordon please look at the comments above

@ncordon ncordon force-pushed the 4.4-load-json-ssrf-mitigation branch 2 times, most recently from 61ae529 to 9ca37c7 Compare January 31, 2022 12:31
@ncordon ncordon force-pushed the 4.4-load-json-ssrf-mitigation branch from 9ca37c7 to 2670ade Compare January 31, 2022 12:32
@ncordon ncordon force-pushed the 4.4-load-json-ssrf-mitigation branch 5 times, most recently from c9cdfb9 to fba2b90 Compare February 1, 2022 12:25
@ncordon ncordon force-pushed the 4.4-load-json-ssrf-mitigation branch from fba2b90 to 13814da Compare February 1, 2022 12:27
@ncordon ncordon force-pushed the 4.4-load-json-ssrf-mitigation branch 2 times, most recently from 5ac2f68 to 2c06679 Compare February 1, 2022 15:24
@@ -60,6 +60,7 @@ dependencies {
exclude module: "snakeyaml"
}
compile group: 'org.yaml', name: 'snakeyaml', version: '1.26'
compile group: 'com.github.seancfoley', name: 'ipaddress', version: '5.3.3'
Copy link
Contributor Author

@ncordon ncordon Feb 1, 2022

Choose a reason for hiding this comment

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

This is needed now if we want to avoid transitive dependencies that come from the database in 4.4.4

Copy link
Member

Choose a reason for hiding this comment

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

but then we need to take it out when 4.4.4 is out?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@jexp My idea is to keep in 4.4.x so we can cover also older Neo4j versions, and remove it from 5.0

@ncordon ncordon force-pushed the 4.4-load-json-ssrf-mitigation branch from 432ff3d to c5a8ca8 Compare February 1, 2022 16:06
Copy link
Collaborator

@conker84 conker84 left a comment

Choose a reason for hiding this comment

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

Thank you so much @ncordon

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.

5 participants