将参数作为字符串的 Sql命令
本文关键字:Sql 命令 字符串 参数 | 更新日期: 2023-09-27 18:32:07
我需要创建一个应用程序,给定一些用户输入的CSV形式,需要将此CSV解析并生成为多种格式。其中一种格式是针对每行 CSV 的一系列 SQL INSERT 语句(作为字符串)。
(在这一点上,您可以假设我已经将CSV解析为值列表或其他内容,因此这不是问题的重点)
鉴于此输入可能包含漏洞,我希望生成已经过验证和清理的 INSERT 语句。
我熟悉创建 SqlCommand 对象并将值添加到其参数列表中,但查看类似的问题,它似乎没有按照我希望的方式工作。
那么有没有办法以我需要的方式生成经过净化的 SQL 语句,作为字符串?
编辑:这是我想做的一个例子。
.CSV:
id,name,country
1,Alice,France
2,Bob,Austria
3,Carol,Germany
.SQL:
...
INSERT INTO Users (id, name, country) VALUES (1, 'Alice', 'France');
INSERT INTO Users (id, name, country) VALUES (2, 'Bob', 'Austria');
INSERT INTO Users (id, name, country) VALUES (3, 'Carol', 'Germany');
...
由于CSV中没有给出数据类型,因此应用程序也必须确定。
与其说是一个答案,不如说是一个警告。如果您最终需要走"经典"逃逸路线来执行此操作,并且确实需要安全(即数据来自不受信任的来源),请不要忘记您需要担心的不仅仅是简单的逃逸。
我们经常听到的基本角色转义:
-
' -> ''
撇号之类的东西非常明显,并且记录在案 - 在一个语句中
;
多个命令 - DB并不总是允许,但很危险
但是,如果您要解析"恶意行为",您是否考虑过:
-
SELECT/*not important*/1/*really...*/FROM/*im serious*/users
-
SELECT%09FIELDS%09FROM%0dTABLE_NAME
-
WHERE username=CONCAT(CHAR(97),CHAR(100),CHAR(109),CHAR(105),CHAR(110))
-
SELECT passwd FROM users WHERE username=0x61646d696e
总结:这里是龙。
http://www.ihteam.net/papers/blind-sqli-regexp-attack.pdf
http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/#HexbasedSamples
如果您不想使用 SQL 对象,那么您将不得不自己清理条目。 我不知道SQL的任何推荐格式,但是对于MySQL,以下内容将起作用。 我已经将其更改为与SQL一起使用,但是我不能保证它涵盖了所有可能的注入攻击。
public string sqlEscape(string VAL)
{
if (VAL == null)
{
return null;
}
return "'"" + Regex.Replace(VAL, @"['r'n'x00'x1a'''""]", @"'$0") + "'"";
}
然后使用你会做(假设你的CSV行存储在一个名为CSV的数组中):
string query = @"INSERT INTO Users (id, name, country) VALUES (" + sqlEscape(csv[0]) + ", " + sqlEscape(csv[1]) + ", " + sqlEscape(csv[2]) + ");";
如果有人可以增强这一点,请告诉我!
你是如何存储变量的,所以我将使用List<Dictionary<String, Object>>()
向你展示一个完整的、可能的示例数据实现:
添加您的示例数据:
var tableName = "Users";
var records = new List<Dictionary<String, Object>>();
var recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 1);
recordFields.Add("name", "Alice");
recordFields.Add("country", "France");
records.Add(recordFields);
recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 2);
recordFields.Add("name", "Bob");
recordFields.Add("country", "Austria");
records.Add(recordFields);
recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 3);
recordFields.Add("name", "Carol");
recordFields.Add("country", "Germany");
records.Add(recordFields);
生成参数化的插入语句:
using (var con = new SqlConnection(Settings.Default.ConnectionString))
{
con.Open();
foreach (var record in records)
{
String insertSql = String.Format("INSERT INTO {0} ({1}) VALUES ({2});"
, tableName
, String.Join(",", record.Select(r => r.Key))
, String.Join(",", record.Select(r => "@" + r.Key)));
using (var insertCommand = new SqlCommand(insertSql, con))
{
foreach (var field in record)
{
var param = new SqlParameter("@" + field.Key, field.Value);
insertCommand.Parameters.Add(param);
}
insertCommand.ExecuteNonQuery();
}
}
}
请注意,这并没有真正经过测试(它编译并且看起来不错),但它应该对您有所帮助。
编辑:
@Oded:我认为问题是丹尼尔不想运行 插入,但显示/输出/保存它们。因此,使用 SqlCommand 参数 不好,因为您看不到生成的 SQL。
@Cylindric 没错
这是不可能的,而且是一个矛盾,你不能用String
SqlParameters
.因此,如果您稍后运行这些插入,恐怕您对SQL注入持开放态度。我建议在实际运行状态时使用上面的代码。