Permalink
Browse files

Merge pull request #186 from GeoffWilliams/VirtualTable_escapeSql_sup…

…port

Support for escaping sql in VirtualTable class
  • Loading branch information...
2 parents f819900 + 0c03847 commit aee0785984a9665a6224becacdfb042fa0f495d3 @aaime aaime committed Apr 20, 2013
View
41 modules/library/jdbc/src/main/java/org/geotools/jdbc/EscapeSql.java
@@ -0,0 +1,41 @@
+/*
+ * GeoTools - The Open Source Java GIS Toolkit
+ * http://geotools.org
+ *
+ * (C) 2002-2013, Open Source Geospatial Foundation (OSGeo)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+package org.geotools.jdbc;
+
+/**
+ * Perform basic SQL validation on input string. This is to allow safe encoding
+ * of parameters that must contain quotes, while still protecting users from SQL
+ * injection.
+ *
+ * We prevent SQL from breaking out of quotes by replacing any quotes in input
+ * stream with double quotes. Backslashes are too risky to allow so are removed
+ * completely
+ */
+public class EscapeSql {
+ public static String escapeSql(String str) {
+
+ // ' --> ''
+ str = str.replaceAll("'", "''");
+
+ // " --> ""
+ str = str.replaceAll("\"", "\"\"");
+
+ // \ --> (remove backslashes)
+ str = str.replaceAll("\\\\", "");
+ return str;
+ }
+}
View
44 modules/library/jdbc/src/main/java/org/geotools/jdbc/VirtualTable.java
@@ -41,7 +41,14 @@
* the geometry type and native srid (as in most databases those informations are not available on.
*
* The sql query can contain named parameters. Each parameter has a name, a default value and a way
- * to validate its contents to prevent sql injection
+ * to validate its contents to prevent sql injection.
+ *
+ * As well as passing validation, parameters are also passed through a function to escape double
+ * quotes, single quotes and strip backslashes to guard against the cases where quotes are desired
+ * in the parameters or backslashes have been allowed by an overly lax regular expression.
+ *
+ * Escaping is enabled by default and can be controlled by a constructor argument or via the
+ * setEscapeSql() method.
*
* @author Andrea Aime - OpenGeo
*
@@ -64,6 +71,8 @@
Map<String, VirtualTableParameter> parameters = new ConcurrentHashMap<String, VirtualTableParameter>();
+ boolean escapeSql = false;
+
/**
* Builds a new virtual table stating its name and the query to be executed to work on it
*
@@ -76,6 +85,19 @@ public VirtualTable(String name, String sql) {
}
/**
+ * Builds a new virtual table stating its name, the query to be executed to work on it
+ * and a flag to indicate if SQL special characters should be escaped.
+ *
+ * @param name
+ * @param sql
+ * @param escapeSql
+ */
+ public VirtualTable(String name, String sql, boolean escapeSql) {
+ this(name,sql);
+ this.escapeSql = escapeSql;
+ }
+
+ /**
* Clone a virtual table under a different name
* @param name
* @param other
@@ -87,6 +109,7 @@ public VirtualTable(String name, VirtualTable other) {
this.nativeSrids = new ConcurrentHashMap<String, Integer>(other.nativeSrids);
this.parameters = new ConcurrentHashMap<String, VirtualTableParameter>(other.parameters);
this.primaryKeyColumns = new ArrayList<String>(other.primaryKeyColumns);
+ this.escapeSql = other.escapeSql;
}
/**
@@ -101,6 +124,7 @@ public VirtualTable(VirtualTable other) {
this.nativeSrids = new ConcurrentHashMap<String, Integer>(other.nativeSrids);
this.parameters = new ConcurrentHashMap<String, VirtualTableParameter>(other.parameters);
this.primaryKeyColumns = new ArrayList<String>(other.primaryKeyColumns);
+ this.escapeSql = other.escapeSql;
}
/**
@@ -170,6 +194,12 @@ public String expandParameters(Hints hints) throws SQLException {
if(param.getValidator() != null) {
try {
param.getValidator().validate(value);
+
+ // Parameter value has passed validation, perform sql escaping
+ // if enabled
+ if (escapeSql) {
+ value = EscapeSql.escapeSql(value);
+ }
} catch(IllegalArgumentException e) {
// fully log the exception, but only rethrow a more generic description as
// the message could be exposed to attackers
@@ -264,7 +294,13 @@ public int getNativeSrid(String geometryName) {
return srid;
}
-
+ public boolean isEscapeSql() {
+ return escapeSql;
+ }
+
+ public void setEscapeSql(boolean escapeSql) {
+ this.escapeSql = escapeSql;
+ }
@Override
public int hashCode() {
@@ -276,6 +312,7 @@ public int hashCode() {
result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
result = prime * result + ((primaryKeyColumns == null) ? 0 : primaryKeyColumns.hashCode());
result = prime * result + ((sql == null) ? 0 : sql.hashCode());
+ result = prime * result + ((escapeSql) ? 1: 0);
return result;
}
@@ -318,6 +355,9 @@ public boolean equals(Object obj) {
return false;
} else if (!sql.equals(other.sql))
return false;
+ if (escapeSql != other.escapeSql) {
+ return false;
+ }
return true;
}
View
45 modules/library/jdbc/src/test/java/org/geotools/jdbc/EscapeSqlTest.java
@@ -0,0 +1,45 @@
+/*
+ * GeoTools - The Open Source Java GIS Toolkit
+ * http://geotools.org
+ *
+ * (C) 2002-2013, Open Source Geospatial Foundation (OSGeo)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+package org.geotools.jdbc;
+
+import java.util.Collections;
+import junit.framework.TestCase;
+import org.geotools.factory.Hints;
+
+/**
+ * Check that SQL escape code generates the correct strings
+ */
+public class EscapeSqlTest extends TestCase {
+
+ public void testSqlEscaping() throws Exception {
+ VirtualTable vt = new VirtualTable("test", "%param1%");
+ vt.setEscapeSql(true);
+ vt.addParameter(new VirtualTableParameter("param1", "default_value", new RegexpValidator(".*")));
+ String singleQuote = vt.expandParameters(new Hints(
+ Hints.VIRTUAL_TABLE_PARAMETERS, Collections.singletonMap("param1", "o'shea")));
+ assertEquals("single quotes should be doubled", "o''shea", singleQuote);
+
+ String doubleQuote = vt.expandParameters(new Hints(
+ Hints.VIRTUAL_TABLE_PARAMETERS, Collections.singletonMap("param1", "If you hear a voice within you say \"you cannot paint,\" then by all means paint, and that voice will be silenced.")));
+ assertEquals("double quotes should be doubled", "If you hear a voice within you say \"\"you cannot paint,\"\" then by all means paint, and that voice will be silenced.",
+ doubleQuote);
+
+ String backslash = vt.expandParameters(new Hints(
+ Hints.VIRTUAL_TABLE_PARAMETERS, Collections.singletonMap("param1", "abc\\n")));
+ assertEquals("backslashes should be removed", "abcn", backslash);
+ }
+}

0 comments on commit aee0785

Please sign in to comment.