Merge branch '592-scrub-addresses-before-logging-them' into 'master'

Scrub addresses before logging them

MAC, IP and onion addresses are now scrubbed before logging to ensure we don't leave any sensitive information in plaintext on the device or send it in crash reports or feedback.

* Bluetooth MAC addresses keep the first and last octets
* IPv4 addresses keep the first and last octets
* IPv6 addresses should be scrubbed completely (couldn't test)
* Onion addresses keep the first three characters

If an address is invalid it will not be scrubbed to enable debugging, because it is most likely not sensitive.

Closes #592

See merge request !290
This commit is contained in:
akwizgran
2016-08-24 17:18:48 +00:00
6 changed files with 119 additions and 33 deletions

View File

@@ -57,6 +57,7 @@ import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.util.PrivacyUtils.scrubMacAddress;
class DroidtoothPlugin implements DuplexPlugin { class DroidtoothPlugin implements DuplexPlugin {
@@ -172,7 +173,7 @@ class DroidtoothPlugin implements DuplexPlugin {
String address = AndroidUtils.getBluetoothAddress(appContext, String address = AndroidUtils.getBluetoothAddress(appContext,
adapter); adapter);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Local address " + address); LOG.info("Local address " + scrubMacAddress(address));
if (!StringUtils.isNullOrEmpty(address)) { if (!StringUtils.isNullOrEmpty(address)) {
// Advertise the Bluetooth address to contacts // Advertise the Bluetooth address to contacts
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
@@ -237,7 +238,7 @@ class DroidtoothPlugin implements DuplexPlugin {
} }
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
String address = s.getRemoteDevice().getAddress(); String address = s.getRemoteDevice().getAddress();
LOG.info("Connection from " + address); LOG.info("Connection from " + scrubMacAddress(address));
} }
backoff.reset(); backoff.reset();
callback.incomingConnectionCreated(wrapSocket(s)); callback.incomingConnectionCreated(wrapSocket(s));
@@ -307,6 +308,7 @@ class DroidtoothPlugin implements DuplexPlugin {
// Validate the address // Validate the address
if (!BluetoothAdapter.checkBluetoothAddress(address)) { if (!BluetoothAdapter.checkBluetoothAddress(address)) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
// not scrubbing here to be able to figure out the problem
LOG.warning("Invalid address " + address); LOG.warning("Invalid address " + address);
return null; return null;
} }
@@ -323,13 +325,15 @@ class DroidtoothPlugin implements DuplexPlugin {
BluetoothSocket s = null; BluetoothSocket s = null;
try { try {
s = d.createInsecureRfcommSocketToServiceRecord(u); s = d.createInsecureRfcommSocketToServiceRecord(u);
if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + address); if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubMacAddress(address));
s.connect(); s.connect();
if (LOG.isLoggable(INFO)) LOG.info("Connected to " + address); if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubMacAddress(address));
return s; return s;
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Failed to connect to " + address); LOG.info("Failed to connect to " + scrubMacAddress(address));
tryToClose(s); tryToClose(s);
return null; return null;
} }
@@ -567,7 +571,8 @@ class DroidtoothPlugin implements DuplexPlugin {
} else if (action.equals(FOUND)) { } else if (action.equals(FOUND)) {
BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE); BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Discovered device: " + d.getAddress()); LOG.info("Discovered device: " +
scrubMacAddress(d.getAddress()));
addresses.add(d.getAddress()); addresses.add(d.getAddress());
} }
} }

View File

@@ -72,6 +72,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
import static org.briarproject.util.PrivacyUtils.scrubOnion;
class TorPlugin implements DuplexPlugin, EventHandler, EventListener { class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@@ -405,7 +406,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
// Publish the hidden service's onion hostname in transport properties // Publish the hidden service's onion hostname in transport properties
String hostname = response.get(HS_ADDRESS); String hostname = response.get(HS_ADDRESS);
if (LOG.isLoggable(INFO)) LOG.info("Hidden service " + hostname); if (LOG.isLoggable(INFO))
LOG.info("Hidden service " + scrubOnion(hostname));
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
p.put(PROP_ONION, hostname); p.put(PROP_ONION, hostname);
callback.mergeLocalProperties(p); callback.mergeLocalProperties(p);
@@ -510,21 +512,25 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
String onion = p.get(PROP_ONION); String onion = p.get(PROP_ONION);
if (StringUtils.isNullOrEmpty(onion)) return null; if (StringUtils.isNullOrEmpty(onion)) return null;
if (!ONION.matcher(onion).matches()) { if (!ONION.matcher(onion).matches()) {
// not scrubbing this address, so we are able to find the problem
if (LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion); if (LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion);
return null; return null;
} }
try { try {
if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + onion); if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubOnion(onion));
controlConnection.forgetHiddenService(onion); controlConnection.forgetHiddenService(onion);
Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT); Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT);
proxy.resolveAddrLocally(false); proxy.resolveAddrLocally(false);
Socket s = new SocksSocket(proxy, onion + ".onion", 80); Socket s = new SocksSocket(proxy, onion + ".onion", 80);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) LOG.info("Connected to " + onion); if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubOnion(onion));
return new TorTransportConnection(this, s); return new TorTransportConnection(this, s);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Could not connect to " + onion + ": " + e.toString()); LOG.info("Could not connect to " + scrubOnion(onion) + ": " +
e.toString());
return null; return null;
} }
} }

View File

@@ -29,6 +29,7 @@ import java.util.logging.Logger;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.util.PrivacyUtils.scrubSocketAddress;
class LanTcpPlugin extends TcpPlugin { class LanTcpPlugin extends TcpPlugin {
@@ -177,7 +178,7 @@ class LanTcpPlugin extends TcpPlugin {
break; break;
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Failed to bind " + addr); LOG.info("Failed to bind " + scrubSocketAddress(addr));
tryToClose(ss); tryToClose(ss);
} }
} }
@@ -205,20 +206,24 @@ class LanTcpPlugin extends TcpPlugin {
if (!isConnectable(remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress(); SocketAddress local = socket.getLocalSocketAddress();
LOG.info(remote + " is not connectable from " + local); LOG.info(scrubSocketAddress(remote) +
" is not connectable from " +
scrubSocketAddress(local));
} }
return null; return null;
} }
Socket s = new Socket(); Socket s = new Socket();
try { try {
if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote); if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubSocketAddress(remote));
s.connect(remote); s.connect(remote);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) LOG.info("Connected to " + remote); if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubSocketAddress(remote));
return new TcpTransportConnection(this, s); return new TcpTransportConnection(this, s);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Could not connect to " + remote); LOG.info("Could not connect to " + scrubSocketAddress(remote));
return null; return null;
} }
} }

View File

@@ -1,7 +1,9 @@
package org.briarproject.plugins.tcp; package org.briarproject.plugins.tcp;
import static java.util.logging.Level.INFO; import org.bitlet.weupnp.GatewayDevice;
import static java.util.logging.Level.WARNING; import org.bitlet.weupnp.GatewayDiscover;
import org.briarproject.api.lifecycle.ShutdownManager;
import org.xml.sax.SAXException;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
@@ -10,10 +12,9 @@ import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.bitlet.weupnp.GatewayDevice; import static java.util.logging.Level.INFO;
import org.bitlet.weupnp.GatewayDiscover; import static java.util.logging.Level.WARNING;
import org.briarproject.api.lifecycle.ShutdownManager; import static org.briarproject.util.PrivacyUtils.scrubInetAddress;
import org.xml.sax.SAXException;
class PortMapperImpl implements PortMapper { class PortMapperImpl implements PortMapper {
@@ -35,7 +36,7 @@ class PortMapperImpl implements PortMapper {
InetAddress internal = gateway.getLocalAddress(); InetAddress internal = gateway.getLocalAddress();
if (internal == null) return null; if (internal == null) return null;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Internal address " + getHostAddress(internal)); LOG.info("Internal address " + scrubInetAddress(internal));
boolean succeeded = false; boolean succeeded = false;
InetAddress external = null; InetAddress external = null;
try { try {
@@ -50,7 +51,8 @@ class PortMapperImpl implements PortMapper {
} }
String externalString = gateway.getExternalIPAddress(); String externalString = gateway.getExternalIPAddress();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("External address " + externalString); LOG.info(
"External address " + scrubInetAddress(externalString));
if (externalString != null) if (externalString != null)
external = InetAddress.getByName(externalString); external = InetAddress.getByName(externalString);
} catch (IOException e) { } catch (IOException e) {

View File

@@ -30,6 +30,7 @@ import java.util.regex.Pattern;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.util.PrivacyUtils.scrubSocketAddress;
abstract class TcpPlugin implements DuplexPlugin { abstract class TcpPlugin implements DuplexPlugin {
@@ -107,14 +108,15 @@ abstract class TcpPlugin implements DuplexPlugin {
public void run() { public void run() {
if (!running) return; if (!running) return;
ServerSocket ss = null; ServerSocket ss = null;
for (SocketAddress addr : getLocalSocketAddresses()) { for (InetSocketAddress addr : getLocalSocketAddresses()) {
try { try {
ss = new ServerSocket(); ss = new ServerSocket();
ss.bind(addr); ss.bind(addr);
break; break;
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Failed to bind " + addr); LOG.info("Failed to bind " +
scrubSocketAddress(addr));
tryToClose(ss); tryToClose(ss);
} }
} }
@@ -128,9 +130,11 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
socket = ss; socket = ss;
backoff.reset(); backoff.reset();
SocketAddress local = ss.getLocalSocketAddress(); InetSocketAddress local =
setLocalSocketAddress((InetSocketAddress) local); (InetSocketAddress) ss.getLocalSocketAddress();
if (LOG.isLoggable(INFO)) LOG.info("Listening on " + local); setLocalSocketAddress(local);
if (LOG.isLoggable(INFO))
LOG.info("Listening on " + scrubSocketAddress(local));
callback.transportEnabled(); callback.transportEnabled();
acceptContactConnections(); acceptContactConnections();
} }
@@ -166,7 +170,8 @@ abstract class TcpPlugin implements DuplexPlugin {
return; return;
} }
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connection from " + s.getRemoteSocketAddress()); LOG.info("Connection from " +
scrubSocketAddress(s.getRemoteSocketAddress()));
backoff.reset(); backoff.reset();
TcpTransportConnection conn = new TcpTransportConnection(this, s); TcpTransportConnection conn = new TcpTransportConnection(this, s);
callback.incomingConnectionCreated(conn); callback.incomingConnectionCreated(conn);
@@ -223,20 +228,25 @@ abstract class TcpPlugin implements DuplexPlugin {
if (!isConnectable(remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress(); SocketAddress local = socket.getLocalSocketAddress();
LOG.info(remote + " is not connectable from " + local); LOG.info(scrubSocketAddress(remote) +
" is not connectable from " +
scrubSocketAddress(local));
} }
continue; continue;
} }
Socket s = new Socket(); Socket s = new Socket();
try { try {
if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote); if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubSocketAddress(remote));
s.connect(remote); s.connect(remote);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) LOG.info("Connected to " + remote); if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubSocketAddress(remote));
return new TcpTransportConnection(this, s); return new TcpTransportConnection(this, s);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Could not connect to " + remote); LOG.info("Could not connect to " +
scrubSocketAddress(remote));
} }
} }
return null; return null;
@@ -255,6 +265,7 @@ abstract class TcpPlugin implements DuplexPlugin {
return new InetSocketAddress(a, p); return new InetSocketAddress(a, p);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
// not scrubbing to enable us to find the problem
LOG.warning("Invalid address: " + addr); LOG.warning("Invalid address: " + addr);
return null; return null;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {

View File

@@ -0,0 +1,57 @@
package org.briarproject.util;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class PrivacyUtils {
public static String scrubOnion(String onion) {
// keep first three characters of onion address
return onion.substring(0, 3) + "[_scrubbed_]";
}
public static String scrubMacAddress(String address) {
if (address == null) return null;
// this is a fake address we need to know about
if (address.equals("02:00:00:00:00:00")) return address;
// keep first and last octet of MAC address
return address.substring(0, 3) +
"[scrubbed]" +
address.substring(14, 17);
}
public static String scrubInetAddress(InetAddress address) {
// don't scrub link and site local addresses
if (address.isLinkLocalAddress() || address.isSiteLocalAddress())
return address.toString();
// completely scrub IPv6 addresses
if (address instanceof Inet6Address) return "[scrubbed]";
// keep first and last octet of IPv4 addresses
return scrubInetAddress(address.toString());
}
public static String scrubInetAddress(String address) {
if (address == null) return null;
int firstDot = address.indexOf(".");
if (firstDot == -1) return "[scrubbed]";
String prefix = address.substring(0, firstDot + 1);
int lastDot = address.lastIndexOf(".");
String suffix = address.substring(lastDot, address.length());
return prefix + "[scrubbed]" + suffix;
}
public static String scrubSocketAddress(InetSocketAddress address) {
InetAddress inetAddress = address.getAddress();
return scrubInetAddress(inetAddress);
}
public static String scrubSocketAddress(SocketAddress address) {
if (address instanceof InetSocketAddress)
return scrubSocketAddress((InetSocketAddress) address);
return scrubInetAddress(address.toString());
}
}