时间:2021-07-01 10:21:17 帮助过:6人阅读
问题来了,当初我在项目里面碰到过这个问题,是采用参数化命令来解决这个问题,那会时间紧没有功夫研究,现在想想,到底是为什么能够通过下面的方式,就能够避免注入了,如果ID也是拼接成攻击性的sql语句,为什么就不会产生注入问题。
string sql = "SELECT * FROM Person where ID = @ID";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@ID", ID);
看这个问题首先得看看SqlCommand的编译原理了,虽然这可想而知是一条漫长的路,但是我觉得编程还是需要有颗探究所以然的心,哪怕看不懂,万事开头难。
我通过ILSpy看了下SQLCommand的源码,从ExcuteNonQuery一直往下深究,我的思路是,总归传入的parameters有地方调用,虽然很多的参数我并不一定知晓其中的原理,但是只要我看到怎么处理parameter时,我就能找到我想要的,于是我一层层往下看,至少往下找了5层吧,发现了这个方法:
private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlParameterCollection parameters, ref _SqlRPC rpc)
{
int num = this.CountSendableParameters(parameters);
int num2;
if (num > 0)
{
num2 = 2;
}
else
{
num2 = 1;
}
this.GetRPCObject(num + num2, ref rpc);
rpc.ProcID = 10;
rpc.rpcName = "sp_executesql";
if (commandText == null)
{
commandText = this.GetCommandText(behavior);
}
SqlParameter sqlParameter = new SqlParameter(null, (commandText.Length << 1 <= 8000) ? SqlDbType.NVarChar : SqlDbType.NText, commandText.Length);
sqlParameter.Value = commandText;
rpc.parameters[0] = sqlParameter;
if (num > 0)
{
string text = this.BuildParamList(this._stateObj.Parser, this.BatchRPCMode ? parameters : this._parameters);
sqlParameter = new SqlParameter(null, (text.Length << 1 <= 8000) ? SqlDbType.NVarChar : SqlDbType.NText, text.Length);
sqlParameter.Value = text;
rpc.parameters[1] = sqlParameter;
bool inSchema = CommandBehavior.Default != (behavior & CommandBehavior.SchemaOnly);
this.SetUpRPCParameters(rpc, num2, inSchema, parameters);
}
}
大家看到这个标黄的地方,我瞬间明白了,本质上在C#里面调用类库执行sql语句,其实都是在走sp_executesql这个命令,熟悉sql的筒子们都知道,sp_executesql这个命令是用来处理动态sql的,实际上是将数值参数化,要执行的动态Sql是不会变化,即不会重新编译,只是参数在不停的变化。如此就解决了我的疑问,实际上C#并不会去对SQL语句做过多的处理,而是将对应的语句和参数传给SQL去处理。
这是我在博客园上写的第一篇文章,这个技术点其实也没有特别高深,只是我在工作过程中突然碰到想到了这个问题,然后上网搜也没有对应的解答,所以想要仔细研究下,不管写的怎么样,希望我自己和大家都能有个探索本质的精神,而不是仅仅停留在表面,由此写下来与大家分享下我的心得,谢谢。
如果有什么不对的地方,希望大家指正,谢谢。
最后粘一段我觉得在我研究过程中比较有用的代码
private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlParameterCollection parameters)
{
int parameterCount = this.GetParameterCount(parameters);
int num = startCount;
TdsParser parser = this._activeConnection.Parser;
bool isYukonOrNewer = parser.IsYukonOrNewer;
for (int i = 0; i < parameterCount; i++)
{
SqlParameter sqlParameter = parameters[i];
sqlParameter.Validate(i, CommandType.StoredProcedure == this.CommandType);
if (!sqlParameter.ValidateTypeLengths(isYukonOrNewer).IsPlp && sqlParameter.Direction != ParameterDirection.Output)
{
sqlParameter.FixStreamDataForNonPLP();
}
if (SqlCommand.ShouldSendParameter(sqlParameter))
{
rpc.parameters[num] = sqlParameter;
if (sqlParameter.Direction == ParameterDirection.InputOutput || sqlParameter.Direction == ParameterDirection.Output)
{
rpc.paramoptions[num] = 1;
}
if (sqlParameter.Direction != ParameterDirection.Output && sqlParameter.Value == null && (!inSchema || SqlDbType.Structured == sqlParameter.SqlDbType))
{
byte[] expr_B4_cp_0 = rpc.paramoptions;
int expr_B4_cp_1 = num;
expr_B4_cp_0[expr_B4_cp_1] |= 2;
}
num++;
}
}
}
SQL注入问题的深入研究
标签: