NET中应用SQL参数化时当要注意的一些细节问题

在通过ADO.NET与SQL进行数据交互时,为了防止SQL注入,一般采用参数化的方式,而SqlParameter对应的构造函数如下

// 摘要:
        //     初始化 System.Data.SqlClient.SqlParameter 类的新实例。
        public SqlParameter();
        //
        // 摘要:
        //     用参数名称和新 System.Data.SqlClient.SqlParameter 的一个值初始化 System.Data.SqlClient.SqlParameter
        //     类的新实例。
        //
        // 参数:
        //   parameterName:
        //     要映射的参数的名称。
        //
        //   value:
        //     一个 System.Object,它是 System.Data.SqlClient.SqlParameter 的值。
        public SqlParameter(string parameterName, object value);
        //
        // 摘要:
        //     用参数名称和数据类型初始化 System.Data.SqlClient.SqlParameter 类的新实例。
        //
        // 参数:
        //   parameterName:
        //     要映射的参数的名称。
        //
        //   dbType:
        //     System.Data.SqlDbType 值之一。
        //
        // 异常:
        //   System.ArgumentException:
        //     dbType 参数中提供的值为无效的后端数据类型。
        public SqlParameter(string parameterName, SqlDbType dbType);
        //
        // 摘要:
        //     用参数名称、System.Data.SqlDbType 和大小初始化 System.Data.SqlClient.SqlParameter 类的新实例。
        //
        // 参数:
        //   parameterName:
        //     要映射的参数的名称。
        //
        //   dbType:
        //     System.Data.SqlDbType 值之一。
        //
        //   size:
        //     参数的长度。
        //
        // 异常:
        //   System.ArgumentException:
        //     dbType 参数中提供的值为无效的后端数据类型。
        public SqlParameter(string parameterName, SqlDbType dbType, int size);
        //
        // 摘要:
        //     用参数名称、System.Data.SqlDbType、大小和源列名称初始化 System.Data.SqlClient.SqlParameter
        //     类的新实例。
        //
        // 参数:
        //   parameterName:
        //     要映射的参数的名称。
        //
        //   dbType:
        //     System.Data.SqlDbType 值之一。
        //
        //   size:
        //     参数的长度。
        //
        //   sourceColumn:
        //     源列的名称。
        //
        // 异常:
        //   System.ArgumentException:
        //     dbType 参数中提供的值为无效的后端数据类型。
        public SqlParameter(string parameterName, SqlDbType dbType, int size, string sourceColumn);
        //
        // 摘要:
        //     用参数名称、参数的类型、参数的大小、System.Data.ParameterDirection、参数的精度、参数的小数位数、源列、要使用的 System.Data.DataRowVersion
        //     和参数的值初始化 System.Data.SqlClient.SqlParameter 类的新实例。
        //
        // 参数:
        //   parameterName:
        //     要映射的参数的名称。
        //
        //   dbType:
        //     System.Data.SqlDbType 值之一。
        //
        //   size:
        //     参数的长度。
        //
        //   direction:
        //     System.Data.ParameterDirection 值之一。
        //
        //   isNullable:
        //     如果字段的值可为 null,则为 true;否则为 false。
        //
        //   precision:
        //     要将 System.Data.SqlClient.SqlParameter.Value 解析为的小数点左右两侧的总位数。
        //
        //   scale:
        //     要将 System.Data.SqlClient.SqlParameter.Value 解析为的总小数位数。
        //
        //   sourceColumn:
        //     源列的名称。
        //
        //   sourceVersion:
        //     System.Data.DataRowVersion 值之一。
        //
        //   value:
        //     一个 System.Object,它是 System.Data.SqlClient.SqlParameter 的值。
        //
        // 异常:
        //   System.ArgumentException:
        //     dbType 参数中提供的值为无效的后端数据类型。
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public SqlParameter(string parameterName, SqlDbType dbType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, object value);
        //
        // 摘要:
        //     初始化 System.Data.SqlClient.SqlParameter 类的一个新实例,该类使用参数名、参数的类型、参数的长度、方向、精度、小数位数、源列名称、System.Data.DataRowVersion
        //     值之一、用于源列映射的布尔值、SqlParameter 的值、此 XML 实例的架构集合所在的数据库的名称、此 XML 实例的架构集合所在的关系架构以及此参数的架构集合的名称。
        //
        // 参数:
        //   parameterName:
        //     要映射的参数的名称。
        //
        //   dbType:
        //     System.Data.SqlDbType 值之一。
        //
        //   size:
        //     参数的长度。
        //
        //   direction:
        //     System.Data.ParameterDirection 值之一。
        //
        //   precision:
        //     要将 System.Data.SqlClient.SqlParameter.Value 解析为的小数点左右两侧的总位数。
        //
        //   scale:
        //     要将 System.Data.SqlClient.SqlParameter.Value 解析为的总小数位数。
        //
        //   sourceColumn:
        //     源列的名称。
        //
        //   sourceVersion:
        //     System.Data.DataRowVersion 值之一。
        //
        //   sourceColumnNullMapping:
        //     如果源列可以为 null,则为 true;如果不可以为 null,则为 false。
        //
        //   value:
        //     一个 System.Object,它是 System.Data.SqlClient.SqlParameter 的值。
        //
        //   xmlSchemaCollectionDatabase:
        //     此 XML 实例的架构集合所在的数据库的名称。
        //
        //   xmlSchemaCollectionOwningSchema:
        //     包含此 XML 实例的架构集合的关系架构。
        //
        //   xmlSchemaCollectionName:
        //     此参数的架构集合的名称。
        public SqlParameter(string parameterName, SqlDbType dbType, int size, ParameterDirection direction, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, object value, string xmlSchemaCollectionDatabase, string xmlSchemaCollectionOwningSchema, string xmlSchemaCollectionName);

对于这又臭又长的构造函数,一般情况下,我们都会采用第二个构造函数(如果你们不是,好吧,那是我懒),因为它只要两个参数,然后SqlParameter内部会根据value的类型自动推断其它的参数,比如SqlDbType,一切都是那么的美好,But,the question is coming!

1、绝对不要直接用 new SqlParameter(“@Test”,0),具体原因在msdn上有

2、查询时,对于字符串,会出现一些你意想不到的事情,以下是真实案例,亲身经历(注:数据库字段均为varchar,两个参数的构造函数默认会设置SqlDbType为nvarchar)

a) 根据用户名查询相关信息,简单的一个where userName=@UserName,数据量不大,查询时间需要几秒,直接在SQL查询分析器里面毫秒,具体原因可以见这里

b) Like查询,假定要查询%L%,有些包含L的能被查询出来,有些却查不出来,Why?好吧,暂时我还没找到相关的书面解释,但猜测还是跟a同样的原因,而对于此问题的推断依据是:能查询出来的字符串长度都是在几百以内,而查不出来的字符串都是非常长的字符串,虽然没具体统计过,但长度至少在4000以上(nvarchar后面为具体数字时最大只能4000)

PS:MSSQL的隐式转换遵循向上转换原则,类似C#,即(long)50与(int)50进行比较时,(int)50会被隐式转化成(long)50

注:虽然此处是以MSSQL作为例子,但相信同样的问题在其它关系型数据库中也会存在,所以最好还是不要偷懒,毕竟

new SqlParameter("@Test",0)

var param = new SqlParameter("@Test", SqlDbType.Int);
param.Value = 0;         
相比,其实也没少多少代码是不是


其实没搞懂为啥微软没提供个如下的构造函数呢

public SqlParameter(string parameterName,SqlDbType dbType,object value);


你可能感兴趣的:(sql注入,C#,ADO.NET)