使用预准备语句的以下代码似乎无法选择表home_address
的{{1}}:
students
实际上,这些指令只返回一个名为“?”的列,其中填充了字符串“home_address”,并且包含与表SET @mycolumn = 'home_address' ;
SET @s = 'SELECT ? FROM students' ;
PREPARE statement FROM @s;
EXECUTE statement USING @mycolumn ;
一样多的行。
我怎么能让这个工作?我知道这种语法是可行的,因为以下示例(取自Is it possible to execute a string in MySQL?)起作用:
students
答案 0 :(得分:2)
这是不可能的。区别在于您动态引用数据库的对象,而不是传入字符串。
预备语句通过为您传入的字符串/值的占位符完整地指定SQL语句来工作。然后,您的RDBMS可以在传递参数之前解析查询并确定它的完整执行路径。完成该步骤后,它将获取参数并获取数据。这就是准备好的陈述如此安全的原因。执行路径是预先确定的,因此无法传递更多SQL并进行更改。
因此,如果您不知道列或表,那么它无法解析和构建执行路径。相反,您必须使用连接和执行动态构建SQL。如果您从用户输入中获取列名或表名,那么您必须尽可能地对其进行消毒,并祈祷您的卫生工作比您偷偷摸摸的用户注入SQL的能力更好。
答案 1 :(得分:1)
要扩展JNevill所说的内容,而不是根据原始帖子中的示例动态构建SQL查询,您可以从[student]
中选择所有可能的列,并让应用程序层只返回一列想。虽然这意味着在您的后端和前端之间传输更多数据,但它会更加安全。例如,如果您使用C#.NET构建(它可以是任何语言/平台 - 仅以此为例向您展示我的意思):
DataTable dataTable = new DataTable();
string columnName = "home_address";
string connString = @"your connection string here";
string query = "SELECT home_address, work_address, name /*... any other columns the user may want*/ FROM students";
//Alternatively, you can use the following, but it leaves you exposed to SQL injections, even after sanitizing:
//string query = $"SELECT [{columnName}] FROM students";
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dataTable);
conn.Close();
da.Dispose();
List<object> list = new List<object>();
foreach (DataRow row in dataTable.Rows)
{
list.Add(row[columnName]);
}
这样,您的应用程序层就可以处理一些数据管理。