[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [onionoo/master] Accept hashed fingerprints in search and lookup URLs.
commit b32605525e737559ba1f501a63dd69e27336a513
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Mon Mar 19 15:23:28 2012 +0100
Accept hashed fingerprints in search and lookup URLs.
More precisely, accept hashed relay fingerprints and hashed hashed bridge
fingerprints.
The problem addressed here is that both */lookup/ and */search/ URLs
require a non-hashed fingerprint to look up a relay and a hashed
fingerprint to look up a bridge. This works fine if a) users know how to
hash fingerprints and b) the client knows whether the user provided a
hashed or non-hashed fingerprint. But what if either a) or b) is not the
case? If the client sees a 40-character fingerprint it shouldn't simply
submit it to the server. It might be a non-hashed bridge fingerprint.
The change made here is to make */lookup/ and */search/ URLs accept hashed
relay fingerprints and hashed hashed bridge fingerprints. Clients can now
be advised to always hash any 40-character hex input they receive from
users and include that in their request to the server. If the user
provides a non-hashed fingerprint of a bridge the client won't reveal the
non-hashed fingerprint in the URL. If the user provides a hashed
fingerprint, looking up the hash of the hash still works and returns the
bridge the user was looking for.
Implements #5368.
---
src/org/torproject/onionoo/ResourceServlet.java | 161 +++++++++++++++--------
web/index.html | 36 ++++-
2 files changed, 134 insertions(+), 63 deletions(-)
diff --git a/src/org/torproject/onionoo/ResourceServlet.java b/src/org/torproject/onionoo/ResourceServlet.java
index b28eca1..e11c7ac 100644
--- a/src/org/torproject/onionoo/ResourceServlet.java
+++ b/src/org/torproject/onionoo/ResourceServlet.java
@@ -8,8 +8,10 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
@@ -18,6 +20,10 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.digest.DigestUtils;
+
public class ResourceServlet extends HttpServlet {
private static final long serialVersionUID = 7236658979947465319L;
@@ -31,6 +37,9 @@ public class ResourceServlet extends HttpServlet {
private String relaysPublishedLine = null, bridgesPublishedLine = null;
private List<String> relayLines = new ArrayList<String>(),
bridgeLines = new ArrayList<String>();
+ private Map<String, String>
+ relayFingerprintSummaryLines = new HashMap<String, String>(),
+ bridgeFingerprintSummaryLines = new HashMap<String, String>();
private void readSummaryFile() {
File summaryFile = new File("/srv/onionoo/out/summary.json");
if (!summaryFile.exists()) {
@@ -40,6 +49,8 @@ public class ResourceServlet extends HttpServlet {
if (summaryFile.lastModified() > this.summaryFileLastModified) {
this.relayLines.clear();
this.bridgeLines.clear();
+ this.relayFingerprintSummaryLines.clear();
+ this.bridgeFingerprintSummaryLines.clear();
try {
BufferedReader br = new BufferedReader(new FileReader(
summaryFile));
@@ -53,16 +64,43 @@ public class ResourceServlet extends HttpServlet {
} else if (line.startsWith("\"relays\":")) {
while ((line = br.readLine()) != null && !line.equals("],")) {
this.relayLines.add(line);
+ int fingerprintStart = line.indexOf("\"f\":\"");
+ if (fingerprintStart > 0) {
+ fingerprintStart += "\"f\":\"".length();
+ String fingerprint = line.substring(fingerprintStart,
+ fingerprintStart + 40);
+ String hashedFingerprint = DigestUtils.shaHex(
+ Hex.decodeHex(fingerprint.toCharArray())).
+ toUpperCase();
+ this.relayFingerprintSummaryLines.put(fingerprint, line);
+ this.relayFingerprintSummaryLines.put(hashedFingerprint,
+ line);
+ }
}
} else if (line.startsWith("\"bridges\":")) {
while ((line = br.readLine()) != null && !line.equals("]}")) {
this.bridgeLines.add(line);
+ int hashedFingerprintStart = line.indexOf("\"h\":\"");
+ if (hashedFingerprintStart > 0) {
+ hashedFingerprintStart += "\"h\":\"".length();
+ String hashedFingerprint = line.substring(
+ hashedFingerprintStart, hashedFingerprintStart + 40);
+ String hashedHashedFingerprint = DigestUtils.shaHex(
+ Hex.decodeHex(hashedFingerprint.toCharArray())).
+ toUpperCase();
+ this.bridgeFingerprintSummaryLines.put(hashedFingerprint,
+ line);
+ this.bridgeFingerprintSummaryLines.put(
+ hashedHashedFingerprint, line);
+ }
}
}
}
br.close();
} catch (IOException e) {
return;
+ } catch (DecoderException e) {
+ return;
}
}
this.summaryFileLastModified = summaryFile.lastModified();
@@ -222,56 +260,61 @@ public class ResourceServlet extends HttpServlet {
private void writeMatchingRelays(PrintWriter pw, String searchTerm,
String resourceType) {
- pw.print("\"relays\":[");
- int written = 0;
- for (String line : this.relayLines) {
- boolean lineMatches = false;
- if (searchTerm.startsWith("$")) {
- /* Search is for $-prefixed fingerprint. */
- if (line.contains("\"f\":\""
- + searchTerm.substring(1).toUpperCase())) {
- /* $-prefixed fingerprint matches. */
+ if (searchTerm.length() == 40) {
+ Set<String> fingerprints = new HashSet<String>();
+ fingerprints.add(searchTerm);
+ this.writeRelaysWithFingerprints(pw, fingerprints, resourceType);
+ } else {
+ pw.print("\"relays\":[");
+ int written = 0;
+ for (String line : this.relayLines) {
+ boolean lineMatches = false;
+ if (searchTerm.startsWith("$")) {
+ /* Search is for $-prefixed fingerprint. */
+ if (line.contains("\"f\":\""
+ + searchTerm.substring(1).toUpperCase())) {
+ /* $-prefixed fingerprint matches. */
+ lineMatches = true;
+ }
+ } else if (line.toLowerCase().contains("\"n\":\""
+ + searchTerm.toLowerCase())) {
+ /* Nickname matches. */
+ lineMatches = true;
+ } else if ("unnamed".startsWith(searchTerm.toLowerCase()) &&
+ line.startsWith("{\"f\":")) {
+ /* Nickname "Unnamed" matches. */
+ lineMatches = true;
+ } else if (line.contains("\"f\":\"" + searchTerm.toUpperCase())) {
+ /* Non-$-prefixed fingerprint matches. */
+ lineMatches = true;
+ } else if (line.substring(line.indexOf("\"a\":[")).contains("\""
+ + searchTerm.toLowerCase())) {
+ /* Address matches. */
lineMatches = true;
}
- } else if (line.toLowerCase().contains("\"n\":\""
- + searchTerm.toLowerCase())) {
- /* Nickname matches. */
- lineMatches = true;
- } else if ("unnamed".startsWith(searchTerm.toLowerCase()) &&
- line.startsWith("{\"f\":")) {
- /* Nickname "Unnamed" matches. */
- lineMatches = true;
- } else if (line.contains("\"f\":\"" + searchTerm.toUpperCase())) {
- /* Non-$-prefixed fingerprint matches. */
- lineMatches = true;
- } else if (line.substring(line.indexOf("\"a\":[")).contains("\""
- + searchTerm.toLowerCase())) {
- /* Address matches. */
- lineMatches = true;
- }
- if (lineMatches) {
- String lines = this.getFromSummaryLine(line, resourceType);
- if (lines.length() > 0) {
- pw.print((written++ > 0 ? ",\n" : "\n") + lines);
+ if (lineMatches) {
+ String lines = this.getFromSummaryLine(line, resourceType);
+ if (lines.length() > 0) {
+ pw.print((written++ > 0 ? ",\n" : "\n") + lines);
+ }
}
}
+ pw.print("\n],\n");
}
- pw.print("\n],\n");
}
private void writeRelaysWithFingerprints(PrintWriter pw,
Set<String> fingerprints, String resourceType) {
pw.print("\"relays\":[");
int written = 0;
- for (String line : this.relayLines) {
- for (String fingerprint : fingerprints) {
- if (line.contains("\"f\":\"" + fingerprint.toUpperCase()
- + "\",")) {
- String lines = this.getFromSummaryLine(line, resourceType);
- if (lines.length() > 0) {
- pw.print((written++ > 0 ? ",\n" : "\n") + lines);
- }
- break;
+ for (String fingerprint : fingerprints) {
+ if (this.relayFingerprintSummaryLines.containsKey(
+ fingerprint.toUpperCase())) {
+ String summaryLine = this.relayFingerprintSummaryLines.get(
+ fingerprint.toUpperCase());
+ String lines = this.getFromSummaryLine(summaryLine, resourceType);
+ if (lines.length() > 0) {
+ pw.print((written++ > 0 ? ",\n" : "\n") + lines);
}
}
}
@@ -314,33 +357,37 @@ public class ResourceServlet extends HttpServlet {
if (searchTerm.startsWith("$")) {
searchTerm = searchTerm.substring(1);
}
- pw.print("\"bridges\":[");
- int written = 0;
- for (String line : this.bridgeLines) {
- if (line.contains("\"h\":\"" + searchTerm.toUpperCase())) {
- String lines = this.getFromSummaryLine(line, resourceType);
- if (lines.length() > 0) {
- pw.print((written++ > 0 ? ",\n" : "\n") + lines);
+ if (searchTerm.length() == 40) {
+ Set<String> fingerprints = new HashSet<String>();
+ fingerprints.add(searchTerm);
+ this.writeBridgesWithFingerprints(pw, fingerprints, resourceType);
+ } else {
+ pw.print("\"bridges\":[");
+ int written = 0;
+ for (String line : this.bridgeLines) {
+ if (line.contains("\"h\":\"" + searchTerm.toUpperCase())) {
+ String lines = this.getFromSummaryLine(line, resourceType);
+ if (lines.length() > 0) {
+ pw.print((written++ > 0 ? ",\n" : "\n") + lines);
+ }
}
}
+ pw.print("\n]}\n");
}
- pw.print("\n]}\n");
}
private void writeBridgesWithFingerprints(PrintWriter pw,
Set<String> fingerprints, String resourceType) {
pw.print("\"bridges\":[");
int written = 0;
- for (String line : this.bridgeLines) {
- for (String fingerprint : fingerprints) {
- if (line.contains("\"h\":\"" + fingerprint.toUpperCase()
- + "\",")) {
- String lines = this.getFromSummaryLine(line,
- resourceType);
- if (lines.length() > 0) {
- pw.print((written++ > 0 ? ",\n" : "\n") + lines);
- }
- break;
+ for (String fingerprint : fingerprints) {
+ if (this.bridgeFingerprintSummaryLines.containsKey(
+ fingerprint.toUpperCase())) {
+ String summaryLine = this.bridgeFingerprintSummaryLines.get(
+ fingerprint.toUpperCase());
+ String lines = this.getFromSummaryLine(summaryLine, resourceType);
+ if (lines.length() > 0) {
+ pw.print((written++ > 0 ? ",\n" : "\n") + lines);
}
}
}
diff --git a/web/index.html b/web/index.html
index 4e63c28..8533962 100755
--- a/web/index.html
+++ b/web/index.html
@@ -118,12 +118,20 @@ $-prefixed)</font> fingerprint, IP address, or <font
color="blue">(possibly $-prefixed)</font> hashed fingerprint.
Added the option to search for $-prefixed (hashed) fingerprints on
February 25, 2012.
-Searches are case-insensitive.</td>
+Searches are case-insensitive.
+<font color="blue">Full fingerprints should always be hashed using SHA-1,
+regardless of searching for a relay or a bridge, in order to not
+accidentally leak non-hashed bridge fingerprints in the URL.</font>
+Added the option to search for hashed fingerprints on March 19, 2012.</td>
</tr>
<tr>
<td><b>GET summary/lookup/<i>:fingerprint</i></b></td>
<td>Return the summary of the relay or bridge with <i>:fingerprint</i>
-matching the fingerprint or hashed fingerprint.</td>
+matching the fingerprint or hashed fingerprint.
+<font color="blue">Fingerprints should always be hashed using SHA-1,
+regardless of looking up a relay or a bridge, in order to not accidentally
+leak non-hashed bridge fingerprints in the URL.</font>
+Added the option to look up hashed fingerprints on March 19, 2012.</td>
</tr>
</table>
<br>
@@ -329,12 +337,20 @@ $-prefixed)</font> fingerprint, IP address, or <font
color="blue">(possibly $-prefixed)</font> hashed fingerprint.
Added the option to search for $-prefixed (hashed) fingerprints on
February 25, 2012.
-Searches are case-insensitive.</td>
+Searches are case-insensitive.
+<font color="blue">Full fingerprints should always be hashed using SHA-1,
+regardless of searching for a relay or a bridge, in order to not
+accidentally leak non-hashed bridge fingerprints in the URL.</font>
+Added the option to search for hashed fingerprints on March 19, 2012.</td>
</tr>
<tr>
<td><b>GET details/lookup/<i>:fingerprint</i></b></td>
<td>Return the details of the relay or bridge with <i>:fingerprint</i>
-matching the fingerprint or hashed fingerprint.</td>
+matching the fingerprint or hashed fingerprint.
+<font color="blue">Fingerprints should always be hashed using SHA-1,
+regardless of looking up a relay or a bridge, in order to not accidentally
+leak non-hashed bridge fingerprints in the URL.</font>
+Added the option to look up hashed fingerprints on March 19, 2012.</td>
</tr>
</table>
<br>
@@ -467,12 +483,20 @@ color="blue">(possibly $-prefixed)</font> fingerprint, IP address, or
<font color="blue">(possibly $-prefixed)</font> hashed fingerprint.
Added the option to search for $-prefixed (hashed) fingerprints on
February 25, 2012.
-Searches are case-insensitive.</td>
+Searches are case-insensitive.
+<font color="blue">Full fingerprints should always be hashed using SHA-1,
+regardless of searching for a relay or a bridge, in order to not
+accidentally leak non-hashed bridge fingerprints in the URL.</font>
+Added the option to search for hashed fingerprints on March 19, 2012.</td>
</tr>
<tr>
<td><b>GET bandwidth/lookup/<i>:fingerprint</i></b></td>
<td>Return the bandwidth document of the relay or bridge with
-<i>:fingerprint</i> matching the fingerprint or hashed fingerprint.</td>
+<i>:fingerprint</i> matching the fingerprint or hashed fingerprint.
+<font color="blue">Fingerprints should always be hashed using SHA-1,
+regardless of looking up a relay or a bridge, in order to not accidentally
+leak non-hashed bridge fingerprints in the URL.</font>
+Added the option to look up hashed fingerprints on March 19, 2012.</td>
</tr>
</table>
</body>
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits