mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Tighten definition of what's accepted as a URI, to exclude some potentially malicious ones
This commit is contained in:
parent
45d89bce80
commit
2179c52ee3
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package com.google.zxing.client.result;
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple result type encapsulating a URI that has no further interpretation.
|
* A simple result type encapsulating a URI that has no further interpretation.
|
||||||
*
|
*
|
||||||
|
@ -25,8 +23,6 @@ import java.util.regex.Pattern;
|
||||||
*/
|
*/
|
||||||
public final class URIParsedResult extends ParsedResult {
|
public final class URIParsedResult extends ParsedResult {
|
||||||
|
|
||||||
private static final Pattern USER_IN_HOST = Pattern.compile(":/*([^/@]+)@[^/]+");
|
|
||||||
|
|
||||||
private final String uri;
|
private final String uri;
|
||||||
private final String title;
|
private final String title;
|
||||||
|
|
||||||
|
@ -45,15 +41,11 @@ public final class URIParsedResult extends ParsedResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the URI contains suspicious patterns that may suggest it intends to
|
* @deprecated see {@link URIResultParser#isPossiblyMaliciousURI(String)}
|
||||||
* mislead the user about its true nature. At the moment this looks for the presence
|
|
||||||
* of user/password syntax in the host/authority portion of a URI which may be used
|
|
||||||
* in attempts to make the URI's host appear to be other than it is. Example:
|
|
||||||
* http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
|
|
||||||
* to connect to yourbank.com at first glance.
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public boolean isPossiblyMaliciousURI() {
|
public boolean isPossiblyMaliciousURI() {
|
||||||
return USER_IN_HOST.matcher(uri).find();
|
return URIResultParser.isPossiblyMaliciousURI(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,6 +28,9 @@ import java.util.regex.Pattern;
|
||||||
*/
|
*/
|
||||||
public final class URIResultParser extends ResultParser {
|
public final class URIResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private static final Pattern ALLOWED_URI_CHARS_PATTERN =
|
||||||
|
Pattern.compile("[-._~:/?#\\[\\]@!$&'()*+,;=%A-Za-z0-9]+");
|
||||||
|
private static final Pattern USER_IN_HOST = Pattern.compile(":/*([^/@]+)@[^/]+");
|
||||||
// See http://www.ietf.org/rfc/rfc2396.txt
|
// See http://www.ietf.org/rfc/rfc2396.txt
|
||||||
private static final Pattern URL_WITH_PROTOCOL_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-.]+:");
|
private static final Pattern URL_WITH_PROTOCOL_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-.]+:");
|
||||||
private static final Pattern URL_WITHOUT_PROTOCOL_PATTERN = Pattern.compile(
|
private static final Pattern URL_WITHOUT_PROTOCOL_PATTERN = Pattern.compile(
|
||||||
|
@ -44,7 +47,22 @@ public final class URIResultParser extends ResultParser {
|
||||||
return new URIParsedResult(rawText.substring(4).trim(), null);
|
return new URIParsedResult(rawText.substring(4).trim(), null);
|
||||||
}
|
}
|
||||||
rawText = rawText.trim();
|
rawText = rawText.trim();
|
||||||
return isBasicallyValidURI(rawText) ? new URIParsedResult(rawText, null) : null;
|
if (!isBasicallyValidURI(rawText) || isPossiblyMaliciousURI(rawText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new URIParsedResult(rawText, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the URI contains suspicious patterns that may suggest it intends to
|
||||||
|
* mislead the user about its true nature. At the moment this looks for the presence
|
||||||
|
* of user/password syntax in the host/authority portion of a URI which may be used
|
||||||
|
* in attempts to make the URI's host appear to be other than it is. Example:
|
||||||
|
* http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
|
||||||
|
* to connect to yourbank.com at first glance.
|
||||||
|
*/
|
||||||
|
static boolean isPossiblyMaliciousURI(String uri) {
|
||||||
|
return !ALLOWED_URI_CHARS_PATTERN.matcher(uri).matches() || USER_IN_HOST.matcher(uri).find();
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isBasicallyValidURI(String uri) {
|
static boolean isBasicallyValidURI(String uri) {
|
||||||
|
|
|
@ -146,7 +146,7 @@ public final class CalendarParsedResultTestCase extends Assert {
|
||||||
"GEO:-12.345\r\n" +
|
"GEO:-12.345\r\n" +
|
||||||
"END:VEVENT\r\nEND:VCALENDAR", null, null, BarcodeFormat.QR_CODE);
|
"END:VEVENT\r\nEND:VCALENDAR", null, null, BarcodeFormat.QR_CODE);
|
||||||
ParsedResult result = ResultParser.parseResult(fakeResult);
|
ParsedResult result = ResultParser.parseResult(fakeResult);
|
||||||
assertSame(ParsedResultType.URI, result.getType());
|
assertSame(ParsedResultType.TEXT, result.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -238,7 +238,7 @@ public final class ParsedReaderResultTestCase extends Assert {
|
||||||
doTestResult("BEGIN:VEVENT\r\nSUMMARY:foo\r\nDTSTART:20080504\r\nEND:VEVENT",
|
doTestResult("BEGIN:VEVENT\r\nSUMMARY:foo\r\nDTSTART:20080504\r\nEND:VEVENT",
|
||||||
"foo\n" + formatDate(2008, 5, 4), ParsedResultType.CALENDAR);
|
"foo\n" + formatDate(2008, 5, 4), ParsedResultType.CALENDAR);
|
||||||
doTestResult("BEGIN:VEVENT\r\nDTEND:20080505T\r\nEND:VEVENT",
|
doTestResult("BEGIN:VEVENT\r\nDTEND:20080505T\r\nEND:VEVENT",
|
||||||
"BEGIN:VEVENT\r\nDTEND:20080505T\r\nEND:VEVENT", ParsedResultType.URI);
|
"BEGIN:VEVENT\r\nDTEND:20080505T\r\nEND:VEVENT", ParsedResultType.TEXT);
|
||||||
// Yeah, it's OK that this is thought of as maybe a URI as long as it's not CALENDAR
|
// Yeah, it's OK that this is thought of as maybe a URI as long as it's not CALENDAR
|
||||||
// Make sure illegal entries without newlines don't crash
|
// Make sure illegal entries without newlines don't crash
|
||||||
doTestResult(
|
doTestResult(
|
||||||
|
|
|
@ -93,6 +93,12 @@ public final class URIParsedResultTestCase extends Assert {
|
||||||
doTestIsPossiblyMalicious("http://google.com/@@", false);
|
doTestIsPossiblyMalicious("http://google.com/@@", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMaliciousUnicode() {
|
||||||
|
doTestIsPossiblyMalicious("https://google.com\u2215.evil.com/stuff", true);
|
||||||
|
doTestIsPossiblyMalicious("\u202ehttps://dylankatz.com/moc.elgoog.www//:sptth", true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExotic() {
|
public void testExotic() {
|
||||||
doTest("bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y", "bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y", null);
|
doTest("bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y", "bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y", null);
|
||||||
|
@ -124,9 +130,10 @@ public final class URIParsedResultTestCase extends Assert {
|
||||||
assertEquals(text, result.getDisplayResult());
|
assertEquals(text, result.getDisplayResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void doTestIsPossiblyMalicious(String uri, boolean expected) {
|
private static void doTestIsPossiblyMalicious(String uri, boolean malicious) {
|
||||||
URIParsedResult result = new URIParsedResult(uri, null);
|
Result fakeResult = new Result(uri, null, null, BarcodeFormat.QR_CODE);
|
||||||
assertEquals(expected, result.isPossiblyMaliciousURI());
|
ParsedResult result = ResultParser.parseResult(fakeResult);
|
||||||
|
assertSame(malicious ? ParsedResultType.TEXT : ParsedResultType.URI, result.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue