我想允许用户使用一些相当灵活的标准来查询数据库。我可以使用以下内容:
String slqCmdTxt = "SELECT * FROM TheTable WHERE " + userExpression;
但是,我知道这对SQL注入是开放的。使用参数是好的,但我没有看到允许非常灵活的查询的方法。
如何在不打开SQL注入的情况下允许灵活的数据库查询?
更多详情:
实际上有两个表,一个主表和一个具有属性的辅助表。一个主记录可能具有许多属性。我们想查询两个表中的值。结果将被处理成一个报告,该报告比简单的表格视图更具可读性。数据由C#程序编写,但当前的方向是从用Java编写的组件查询表。
所以我需要一种方法来提供用户输入然后安全地构建查询。对于一组有限的输入,我编写了代码来构建一个带有给定输入和参数值的查询字符串。然后我查看并添加输入值作为参数。这导致复杂的字符串捕捉,难以改变/扩展。
既然我正在使用Java,那么一些搜索已经发现了像jOOQ这样的SQL语句构造库......
答案 0 :(得分:2)
您应该创建一个UI,用户可以从下拉列表中选择一个表,然后添加过滤器。如果您曾经使用过TOAD或DBVisualizer甚至是SQLDeveloper,那么它们都可以在UI中选择一个表,然后在没有实际编写SQL的情况下,用户可以添加过滤器并从UI控件中进行排序。
然后,当然,在UI背后的代码中,您将验证过滤器输入并将其用作预准备语句中的参数(取决于您使用的语言)。
例如,当您浏览数据库对象并单击表时,这就是DBVisualizer(用Java编写)的UI。请注意,您可以从下拉列表中选择任何列,然后从另一个下拉菜单中选择一个运算符(即=,>,> =,<,< =,LIKE,IN等),然后您就可以输入过滤器值的用户定义值。
您可以在UI中执行非常类似的操作。
如果你包含你的应用程序将要编写的语言,那将会有所帮助。如果我不得不猜测,我会根据你的字符串声明说Java或C#,但它会对你有好处。知道肯定。
答案 1 :(得分:1)
假设现代DBMS(例如,Sql Server或Oracle;我不熟悉MySQL),那么只要您确保他们登录的帐户具有适当的限制,您就可以允许用户编写原始SQL它。
在SQL Server中,您可以限制用户可以对任何db对象执行的操作(SELECT,DELETE,UPDATE,EXECUTE)。我相信在甲骨文中也是如此......我认为它甚至可能扩展到列级别,但我不确定。
答案 2 :(得分:0)
Java提供了 PreparedStatement 来执行参数化查询。使用Prepared Statements构建的查询不太容易被利用。
我们需要进行的查询:
String query = "SELECT col1, col2, col3 FROM table1 WHERE " + user_input;
使用带有参数值的PreparedStatement:
// write the query with "?" placeholder for the user_input
String query = "SELECT col1, col2, col3 FROM table1 WHERE ?";
// Create database connection
Connection conn = source.getConnection();
// Prepare a statement for the query
PreparedStatement stmt = conn.prepareStatement(query);
// set the placeholder with the actual user_input
stmt.setString(1, user_input);
// execute the query
ResultSet result = stmt.executeQuery(query);
按照@phil指出的进行编辑,使用PreparedStatement确实可以阻止非法值的执行。但是我仍然强烈建议清理输入内容,因为当您期望使用“ int”时,用户可能会输入“ String”或随机的特殊字符。
比方说,我们两个表都有两组列,用户也可以输入列名和值。
不是像这样未经过滤的输入: 字符串查询=“ SELECT table1,col2,col3 FROM table1 WHERE?”; 使用一些过滤器。过滤器可以是任何东西。可能是某些字符串函数或字符串比较或输入变量类型检查等。
案例1: 假设用户可以使用“ col1”列进行过滤,并且它是“ Integer”或“ Numeric”类型,我们可以使用Regex过滤输入以查看其中是否有特殊字符:
^[0-9]*$
案例2: 检查输入的列名称是否有效。
private static final Set<String> valid_column_names
= Collections.unmodifiableSet(Stream
.of("col1", "col2", "col3")
.collect(Collectors.toCollection(HashSet::new)));
boolean is_valid = false;
if (valid_column_names.contains(user_column_input)) {
is_valid = true;
}
if(!is_valid){
throw new IllegalArgumentException("Invalid Column input");
}
String query = "SELECT col1, col2, col3 FROM table1 WHERE ?";
// prepare statements and execute
最后的注释:
因此,在采取了所有这些预防措施之后,动态生成的查询是否安全?安全得多,但您不能保证。有很多问题使您的数据库易于注入。
答案 3 :(得分:0)
有很多方法可以保护数据库免受SQL注入。我将一一列出。
对DBMS提供的用户提供的输入使用字符转义功能,如下所示;还要确保必须在服务器级别设置字符集,并且mysql_real_escape_string()函数不会转义%和_通配符。
.as-console-wrapper {
max-height: 100% !important;
}
还可以在SQL查询中使用ESCAPE关键字来保护您,如下所示;
$input = mysqli_real_escape_string($db_connection, $_POST['username']);
使用 Web应用程序防火墙,例如 https://www.cloudflare.com/waf/ 。此防火墙将在您的Web服务器前面运行,并监视进入和访问的流量。在您的网络服务器之外。因此,对WAF的每个请求都将根据规则引擎和威胁情报(例如SQL注入,XSS,参数篡改等)进行检查。因此,任何可疑请求都可以立即被阻止,质询或记录。
避免管理特权,例如数据库根目录访问。至于搜索,您的帐户只需要对所需的那些表和列具有读取权限。
使用存储过程以及特殊的EXECUTE特权。
通过绑定参数使用参数化查询,以便对输入进行引用,并且提供的输入不会导致意图的改变。
输入验证和消毒。
即使您使用诸如jOOQ或JPA之类的SQL语句构造库以及其他使您不必创建手工编码的SQL语句的ORM,也不会保护您免于编写易受攻击的代码。如果您使用Java开发。您可以使用短暂的凭据,例如Spring Cloud Vault。
您可以记录所有内容以防止发生攻击时造成损害。
UNION运算符的块用法。这是一种非常常见的基于联合的SQL注入攻击。例如:
获取yourapp.com/store.php?store=-1 在用户uname ='test'HTTP / 1.1时从用户中选择UNION SELECT 1,pass,cc 主持人:yourapp.com
DROP运算符的块用法。
SELECT * FROM用户;删除用户-