import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.util.regex.Pattern; import java.util.regex.Matcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class SanitizationTests extends HttpServlet { private static final String VALID_URI = "http://lgtm.com"; private HttpClient client = HttpClient.newHttpClient(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { URI uri = new URI(request.getParameter("uri")); // $ Source // BAD: a request parameter is incorporated without validation into a Http // request HttpRequest r = HttpRequest.newBuilder(uri).build(); // $ Alert client.send(r, null); // $ Alert // GOOD: sanitisation by concatenation with a prefix that prevents targeting an arbitrary host. // We test a few different ways of sanitisation: via string conctentation (perhaps nested), // via a stringbuilder (for which we consider appends done in the constructor, chained onto // the constructor and applied in subsequent statements) and via String.format. String safeUri3 = "https://example.com/" + request.getParameter("uri3"); HttpRequest r3 = HttpRequest.newBuilder(new URI(safeUri3)).build(); client.send(r3, null); String safeUri4 = "https://example.com/" + ("someprefix" + request.getParameter("uri4")); HttpRequest r4 = HttpRequest.newBuilder(new URI(safeUri4)).build(); client.send(r4, null); StringBuilder safeUri5 = new StringBuilder(); safeUri5.append("https://example.com/").append(request.getParameter("uri5")); HttpRequest r5 = HttpRequest.newBuilder(new URI(safeUri5.toString())).build(); client.send(r5, null); StringBuilder safeUri5a = new StringBuilder("https://example.com/"); safeUri5a.append(request.getParameter("uri5a")); HttpRequest r5a = HttpRequest.newBuilder(new URI(safeUri5a.toString())).build(); client.send(r5a, null); StringBuilder safeUri5b = (new StringBuilder("https://example.com/")).append("dir/"); safeUri5b.append(request.getParameter("uri5b")); HttpRequest r5b = HttpRequest.newBuilder(new URI(safeUri5b.toString())).build(); client.send(r5b, null); StringBuilder safeUri5c = (new StringBuilder("prefix")).append("https://example.com/dir/"); safeUri5c.append(request.getParameter("uri5c")); HttpRequest r5c = HttpRequest.newBuilder(new URI(safeUri5c.toString())).build(); client.send(r5c, null); String safeUri6 = String.format("https://example.com/%s", request.getParameter("uri6")); HttpRequest r6 = HttpRequest.newBuilder(new URI(safeUri6)).build(); client.send(r6, null); String safeUri7 = String.format("%s/%s", "https://example.com", request.getParameter("uri7")); HttpRequest r7 = HttpRequest.newBuilder(new URI(safeUri7)).build(); client.send(r7, null); String safeUri8 = String.format("%s%s", "https://example.com/", request.getParameter("uri8")); HttpRequest r8 = HttpRequest.newBuilder(new URI(safeUri8)).build(); client.send(r8, null); String safeUri9 = String.format("http://%s", "myserver.com") + "/" + request.getParameter("uri9"); HttpRequest r9 = HttpRequest.newBuilder(new URI(safeUri9)).build(); client.send(r9, null); // BAD: cases where a string that would sanitise is used, but occurs in the wrong // place to sanitise user input: String unsafeUri3 = request.getParameter("baduri3") + "https://example.com/"; // $ Source HttpRequest unsafer3 = HttpRequest.newBuilder(new URI(unsafeUri3)).build(); // $ Alert client.send(unsafer3, null); // $ Alert String unsafeUri4 = ("someprefix" + request.getParameter("baduri4")) + "https://example.com/"; // $ Source HttpRequest unsafer4 = HttpRequest.newBuilder(new URI(unsafeUri4)).build(); // $ Alert client.send(unsafer4, null); // $ Alert StringBuilder unsafeUri5 = new StringBuilder(); unsafeUri5.append(request.getParameter("baduri5")).append("https://example.com/"); // $ Source HttpRequest unsafer5 = HttpRequest.newBuilder(new URI(unsafeUri5.toString())).build(); // $ Alert client.send(unsafer5, null); // $ Alert StringBuilder unafeUri5a = new StringBuilder(request.getParameter("uri5a")); // $ Source unafeUri5a.append("https://example.com/"); HttpRequest unsafer5a = HttpRequest.newBuilder(new URI(unafeUri5a.toString())).build(); // $ Alert client.send(unsafer5a, null); // $ Alert StringBuilder unsafeUri5b = (new StringBuilder(request.getParameter("uri5b"))).append("dir/"); // $ Source unsafeUri5b.append("https://example.com/"); HttpRequest unsafer5b = HttpRequest.newBuilder(new URI(unsafeUri5b.toString())).build(); // $ Alert client.send(unsafer5b, null); // $ Alert StringBuilder unsafeUri5c = (new StringBuilder("https")).append(request.getParameter("uri5c")); // $ Source unsafeUri5c.append("://example.com/dir/"); HttpRequest unsafer5c = HttpRequest.newBuilder(new URI(unsafeUri5c.toString())).build(); // $ Alert client.send(unsafer5c, null); // $ Alert String unsafeUri6 = String.format("%shttps://example.com/", request.getParameter("baduri6")); // $ Source HttpRequest unsafer6 = HttpRequest.newBuilder(new URI(unsafeUri6)).build(); // $ Alert client.send(unsafer6, null); // $ Alert String unsafeUri7 = String.format("%s/%s", request.getParameter("baduri7"), "https://example.com"); // $ Source HttpRequest unsafer7 = HttpRequest.newBuilder(new URI(unsafeUri7)).build(); // $ Alert client.send(unsafer7, null); // $ Alert String unsafeUri8 = String.format("%s%s", request.getParameter("baduri8"), "https://example.com/"); // $ Source HttpRequest unsafer8 = HttpRequest.newBuilder(new URI(unsafeUri8)).build(); // $ Alert client.send(unsafer8, null); // $ Alert String unsafeUri9 = request.getParameter("baduri9") + "/" + String.format("http://%s", "myserver.com"); // $ Source HttpRequest unsafer9 = HttpRequest.newBuilder(new URI(unsafeUri9)).build(); // $ Alert client.send(unsafer9, null); // $ Alert String unsafeUri10 = String.format("%s://%s:%s%s", "http", "myserver.com", "80", request.getParameter("baduri10")); // $ Source HttpRequest unsafer10 = HttpRequest.newBuilder(new URI(unsafeUri10)).build(); // $ Alert client.send(unsafer10, null); // $ Alert // GOOD: sanitisation by regexp validation String param10 = request.getParameter("uri10"); if (param10.matches("[a-zA-Z0-9_-]+")) { HttpRequest r10 = HttpRequest.newBuilder(new URI(param10)).build(); client.send(r10, null); } String param11 = request.getParameter("uri11"); validate(param11); HttpRequest r11 = HttpRequest.newBuilder(new URI(param11)).build(); client.send(r11, null); String param12 = request.getParameter("uri12"); if (Pattern.matches("[a-zA-Z0-9_-]+", param12)) { HttpRequest r12 = HttpRequest.newBuilder(new URI(param12)).build(); client.send(r12, null); } Pattern pattern = Pattern.compile("[a-zA-Z0-9_-]+"); String param13 = request.getParameter("uri13"); Matcher matcher = pattern.matcher(param13); if (matcher.matches()) { HttpRequest r13 = HttpRequest.newBuilder(new URI(param13)).build(); client.send(r13, null); } } catch (Exception e) { // TODO: handle exception } } private void validate(String s) { if (!s.matches("[a-zA-Z0-9_-]+")) { throw new IllegalArgumentException("Invalid ID"); } } }