Skip to content

ENCRYPTED_NON_LOCAL #103

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 3 commits into from
Jul 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/v1/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,11 @@ let USER_AGENT = "neo4j-javascript/" + VERSION;
* options are as follows:
*
* {
* // Enable TLS encryption. This is on by default in modern NodeJS installs,
* // Encryption level: one of ENCRYPTION_ON, ENCRYPTION_OFF or ENCRYPTION_NON_LOCAL.
* // ENCRYPTION_NON_LOCAL is on by default in modern NodeJS installs,
* // but off by default in the Web Bundle and old (<=1.0.0) NodeJS installs
* // due to technical limitations on those platforms.
* encrypted: true|false,
* encrypted: ENCRYPTION_ON|ENCRYPTION_OFF|ENCRYPTION_NON_LOCAL
*
* // Trust strategy to use if encryption is enabled. There is no mode to disable
* // trust other than disabling encryption altogether. The reason for
Expand Down
3 changes: 3 additions & 0 deletions src/v1/internal/ch-dummy.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class DummyChannel {
constructor(opts) {
this.written = [];
}
isEncrypted() {
return false;
}
write( buf ) {
this.written.push(buf);
observer.updateInstance(this);
Expand Down
21 changes: 15 additions & 6 deletions src/v1/internal/ch-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import fs from 'fs';
import path from 'path';
import {EOL} from 'os';
import {NodeBuffer} from './buf';
import {isLocalHost, ENCRYPTION_NON_LOCAL, ENCRYPTION_OFF} from './util';
import {newError} from './../error';

let _CONNECTION_IDGEN = 0;
Expand Down Expand Up @@ -71,7 +72,7 @@ const TrustStrategy = {
"to verify trust for encrypted connections, but have not configured any " +
"trustedCertificates. You must specify the path to at least one trusted " +
"X.509 certificate for this to work. Two other alternatives is to use " +
"TRUST_ON_FIRST_USE or to disable encryption by setting encrypted=false " +
"TRUST_ON_FIRST_USE or to disable encryption by setting encrypted=\"" + ENCRYPTION_OFF + "\"" +
"in your driver configuration."));
return;
}
Expand All @@ -89,7 +90,8 @@ const TrustStrategy = {
" the signing certificate, or the server certificate, to the list of certificates trusted by this driver" +
" using `neo4j.v1.driver(.., { trustedCertificates:['path/to/certificate.crt']}). This " +
" is a security measure to protect against man-in-the-middle attacks. If you are just trying " +
" Neo4j out and are not concerned about encryption, simply disable it using `encrypted=false` in the driver" +
" Neo4j out and are not concerned about encryption, simply disable it using `encrypted=\"" + ENCRYPTION_OFF +
"\"` in the driver" +
" options."));
} else {
onSuccess();
Expand All @@ -115,7 +117,7 @@ const TrustStrategy = {
onFailure(newError("You are using a version of NodeJS that does not " +
"support trust-on-first use encryption. You can either upgrade NodeJS to " +
"a newer version, use `trust:TRUST_SIGNED_CERTIFICATES` in your driver " +
"config instead, or disable encryption using `encrypted:false`."));
"config instead, or disable encryption using `encrypted:\"" + ENCRYPTION_OFF+ "\"`."));
return;
}

Expand All @@ -140,7 +142,7 @@ const TrustStrategy = {
"update the file with the new certificate. You can configure which file the driver " +
"should use to store this information by setting `knownHosts` to another path in " +
"your driver configuration - and you can disable encryption there as well using " +
"`encrypted:false`."))
"`encrypted:\"" + ENCRYPTION_OFF + "\"`."))
}
});
});
Expand All @@ -150,7 +152,9 @@ const TrustStrategy = {
};

function connect( opts, onSuccess, onFailure=(()=>null) ) {
if( opts.encrypted === false ) {
//still allow boolean for backwards compatibility
if (opts.encrypted === false || opts.encrypted === ENCRYPTION_OFF ||
(opts.encrypted === ENCRYPTION_NON_LOCAL && isLocalHost(opts.host))) {
var conn = net.connect(opts.port, opts.host, onSuccess);
conn.on('error', onFailure);
return conn;
Expand All @@ -160,7 +164,7 @@ function connect( opts, onSuccess, onFailure=(()=>null) ) {
onFailure(newError("Unknown trust strategy: " + opts.trust + ". Please use either " +
"trust:'TRUST_SIGNED_CERTIFICATES' or trust:'TRUST_ON_FIRST_USE' in your driver " +
"configuration. Alternatively, you can disable encryption by setting " +
"`encrypted:false`. There is no mechanism to use encryption without trust verification, " +
"`encrypted:\"" + ENCRYPTION_OFF + "\"`. There is no mechanism to use encryption without trust verification, " +
"because this incurs the overhead of encryption without improving security. If " +
"the driver does not verify that the peer it is connected to is really Neo4j, it " +
"is very easy for an attacker to bypass the encryption by pretending to be Neo4j."));
Expand Down Expand Up @@ -190,6 +194,7 @@ class NodeChannel {
this._error = null;
this._handleConnectionError = this._handleConnectionError.bind(this);

this._encrypted = opts.encrypted;
this._conn = connect(opts, () => {
if(!self._open) {
return;
Expand Down Expand Up @@ -219,6 +224,10 @@ class NodeChannel {
}
}

isEncrypted() {
return this._encrypted;
}

/**
* Write the passed in buffer to connection
* @param {NodeBuffer} buffer - Buffer to write
Expand Down
13 changes: 11 additions & 2 deletions src/v1/internal/ch-websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import {debug} from "./log";
import {HeapBuffer} from "./buf";
import {newError} from './../error';
import {isLocalHost, ENCRYPTION_NON_LOCAL, ENCRYPTION_ON, ENCRYPTION_OFF} from './util';

/**
* Create a new WebSocketChannel to be used in web browsers.
Expand All @@ -40,15 +41,19 @@ class WebSocketChannel {
this._error = null;
this._handleConnectionError = this._handleConnectionError.bind(this);

this._encrypted = opts.encrypted;

let scheme = "ws";
if( opts.encrypted ) {
//Allow boolean for backwards compatibility
if( opts.encrypted === true || opts.encrypted === ENCRYPTION_ON ||
(opts.encrypted === ENCRYPTION_NON_LOCAL && !isLocalHost(opts.host))) {
if( (!opts.trust) || opts.trust === "TRUST_SIGNED_CERTIFICATES" ) {
scheme = "wss";
} else {
this._error = newError("The browser version of this driver only supports one trust " +
"strategy, 'TRUST_SIGNED_CERTIFICATES'. "+opts.trust+" is not supported. Please " +
"either use TRUST_SIGNED_CERTIFICATES or disable encryption by setting " +
"`encrypted:false` in the driver configuration.");
"`encrypted:\"" + ENCRYPTION_OFF + "\"` in the driver configuration.");
return;
}
}
Expand Down Expand Up @@ -98,6 +103,10 @@ class WebSocketChannel {
}
}
}

isEncrypted() {
return this._encrypted;
}

/**
* Write the passed in buffer to connection
Expand Down
11 changes: 8 additions & 3 deletions src/v1/internal/connector.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {alloc, CombinedBuffer} from "./buf";
import GraphType from '../graph-types';
import {int, isInt} from '../integer';
import {newError} from './../error';
import {ENCRYPTION_NON_LOCAL, ENCRYPTION_OFF, shouldEncrypt} from './util';

let Channel;
if( WebSocketChannel.available ) {
Expand Down Expand Up @@ -401,6 +402,10 @@ class Connection {
return !this._isBroken && this._ch._open;
}

isEncrypted() {
return this._ch.isEncrypted();
}

/**
* Call close on the channel.
* @param {function} cb - Function to call on close.
Expand All @@ -422,9 +427,9 @@ function connect( url, config = {}) {
return new Connection( new Ch({
host: host(url),
port: port(url) || 7687,
// Default to using encryption if trust-on-first-use is available
encrypted : (config.encrypted == null) ? hasFeature("trust_on_first_use") : config.encrypted,
// Default to using trust-on-first-use if it is available
// Default to using ENCRYPTION_NON_LOCAL if trust-on-first-use is available
encrypted : shouldEncrypt(config.encrypted, (hasFeature("trust_on_first_use") ? ENCRYPTION_NON_LOCAL : ENCRYPTION_OFF), host(url)),
// Default to using TRUST_ON_FIRST_USE if it is available
trust : config.trust || (hasFeature("trust_on_first_use") ? "TRUST_ON_FIRST_USE" : "TRUST_SIGNED_CERTIFICATES"),
trustedCertificates : config.trustedCertificates || [],
knownHosts : config.knownHosts
Expand Down
51 changes: 51 additions & 0 deletions src/v1/internal/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

let LOCALHOST_MATCHER = /^(localhost|127(\.\d+){3})$/i;
let ENCRYPTION_ON = "ENCRYPTION_ON";
let ENCRYPTION_OFF = "ENCRYPTION_OFF";
let ENCRYPTION_NON_LOCAL = "ENCRYPTION_NON_LOCAL";

function isLocalHost(host) {
return LOCALHOST_MATCHER.test(host);
}

/* Coerce an encryption setting to a definitive boolean value,
* given a valid default and a target host. If encryption is
* explicitly set on or off, then the mapping is a simple
* conversion to true or false respectively. If set to
* ENCRYPTION_NON_LOCAL then respond according to whether or
* not the host is localhost/127.x.x.x. In all other cases
* (including undefined) then fall back to the default and
* re-evaluate.
*/
function shouldEncrypt(encryption, encryptionDefault, host) {
if (encryption === ENCRYPTION_ON || encryption === true) return true;
if (encryption === ENCRYPTION_OFF || encryption === false) return false;
if (encryption === ENCRYPTION_NON_LOCAL) return !isLocalHost(host);
return shouldEncrypt(encryptionDefault, ENCRYPTION_OFF, host);
}

export {
isLocalHost,
shouldEncrypt,
ENCRYPTION_ON,
ENCRYPTION_OFF,
ENCRYPTION_NON_LOCAL
}
4 changes: 4 additions & 0 deletions src/v1/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class Session {
this._hasTx = false;
}

isEncrypted() {
return this._conn.isEncrypted();
}

/**
* Run Cypher statement
* Could be called with a statement object i.e.: {statement: "MATCH ...", parameters: {param: 1}}
Expand Down
26 changes: 20 additions & 6 deletions test/internal/tls.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var NodeChannel = require('../../lib/v1/internal/ch-node.js');
var neo4j = require("../../lib/v1");
var fs = require("fs");
var hasFeature = require("../../lib/v1/internal/features");
var isLocalHost = require("../../lib/v1/internal/util").isLocalHost;

describe('trust-signed-certificates', function() {

Expand All @@ -34,15 +35,15 @@ describe('trust-signed-certificates', function() {

// Given
driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), {
encrypted: true,
encrypted: "ENCRYPTION_ON",
trust: "TRUST_SIGNED_CERTIFICATES",
trustedCertificates: ["test/resources/random.certificate"]
});

// When
driver.session().run( "RETURN 1").catch( function(err) {
expect( err.message ).toContain( "Server certificate is not trusted" );
done();
expect( err.message ).toContain( "Server certificate is not trusted" );
done();
});
});

Expand All @@ -55,7 +56,7 @@ describe('trust-signed-certificates', function() {

// Given
driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), {
encrypted: true,
encrypted: "ENCRYPTION_ON",
trust: "TRUST_SIGNED_CERTIFICATES",
trustedCertificates: ["build/neo4j/certificates/neo4j.cert"]
});
Expand Down Expand Up @@ -90,7 +91,7 @@ describe('trust-on-first-use', function() {
}

driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), {
encrypted: true,
encrypted: "ENCRYPTION_ON",
trust: "TRUST_ON_FIRST_USE",
knownHosts: knownHostsPath
});
Expand All @@ -115,7 +116,7 @@ describe('trust-on-first-use', function() {
var knownHostsPath = "test/resources/random_known_hosts";

driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), {
encrypted: true,
encrypted: "ENCRYPTION_ON",
trust: "TRUST_ON_FIRST_USE",
knownHosts: knownHostsPath
});
Expand All @@ -128,6 +129,19 @@ describe('trust-on-first-use', function() {
});
});

it('should detect localhost', function() {
expect(isLocalHost('localhost')).toBe(true);
expect(isLocalHost('LOCALHOST')).toBe(true);
expect(isLocalHost('localHost')).toBe(true);
expect(isLocalHost('127.0.0.1')).toBe(true);
expect(isLocalHost('127.0.0.11')).toBe(true);
expect(isLocalHost('127.1.0.0')).toBe(true);

expect(isLocalHost('172.1.0.0')).toBe(false);
expect(isLocalHost('127.0.0.0.0')).toBe(false);
expect(isLocalHost("google.com")).toBe(false);
});

afterEach(function(){
if( driver ) {
driver.close();
Expand Down
10 changes: 5 additions & 5 deletions test/v1/examples.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,8 @@ describe('examples', function() {
var neo4j = neo4jv1;
// tag::tls-require-encryption[]
var driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), {
// In NodeJS, encryption is on by default. In the web bundle, it is off.
encrypted:true
//In NodeJS, encryption is ENCRYPTION_NON_LOCAL on by default. In the web bundle, it is ENCRYPTION_OFF.
encrypted:"ENCRYPTION_ON"
});
// end::tls-require-encryption[]
driver.close();
Expand All @@ -359,7 +359,7 @@ describe('examples', function() {
// in NodeJS, trust-on-first-use is the default trust mode. In the browser
// it is TRUST_SIGNED_CERTIFICATES.
trust: "TRUST_ON_FIRST_USE",
encrypted:true
encrypted:"ENCRYPTION_NON_LOCAL"
});
// end::tls-trust-on-first-use[]
driver.close();
Expand All @@ -374,7 +374,7 @@ describe('examples', function() {
// in NodeJS. In the browser bundle the browsers list of trusted
// certificates is used, due to technical limitations in some browsers.
trustedCertificates : ["path/to/ca.crt"],
encrypted:true
encrypted:"ENCRYPTION_NON_LOCAL"
});
// end::tls-signed[]
driver.close();
Expand All @@ -385,7 +385,7 @@ describe('examples', function() {
// tag::connect-with-auth-disabled[]
var driver = neo4j.driver("bolt://localhost", {
// In NodeJS, encryption is on by default. In the web bundle, it is off.
encrypted:true
encrypted:"ENCRYPTION_NON_LOCAL"
});
// end::connect-with-auth-disabled[]
driver.close();
Expand Down
Loading