代码之家  ›  专栏  ›  技术社区  ›  Rune Grimstad

避免没有参数的SQL注入

  •  113
  • Rune Grimstad  · 技术社区  · 16 年前

    让我用C#给你举一个简单的例子:

    string sql = "SELECT * FROM Users WHERE Name=@name";
    SqlCommand getUser = new SqlCommand(sql, connection);
    getUser.Parameters.AddWithValue("@name", userName);
    //... blabla - do something here, this is safe
    

    而其他人想这样做:

    string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
    SqlCommand getUser = new SqlCommand(sql, connection);
    //... blabla - are we safe now?
    

    string SafeDBString(string inputValue) 
    {
        return "'" + inputValue.Replace("'", "''") + "'";
    }
    

    现在,只要我们在查询中的所有字符串值上使用SafeDBString,我们就应该是安全的。正确的

    使用SafeDBString函数有两个原因。首先,这是自石器时代以来的做法,其次,由于您看到了在数据库上运行的excact查询,因此更容易调试sql语句。

    有人能打破这个吗?你会怎么做?

    21 回复  |  直到 8 年前
        1
  •  82
  •   JulianR    16 年前

        2
  •  71
  •   Marc Gravell    16 年前

        3
  •  27
  •   Matthew Christensen    16 年前

        4
  •  19
  •   Joel Coehoorn    16 年前

    string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
    SqlCommand getUser = new SqlCommand(sql, connection);
    

    转义单引号是不够的。 SafeDBString()


    此外,JulianR在下面提出了一个很好的观点: 试着自己做安保工作。安全编程很容易以微妙的方式出错 即使经过彻底的测试,也能正常工作。然后时间流逝,一年后你发现你的系统在六个月前被破解了,直到那时你才知道。

        5
  •  10
  •   Jim T    16 年前

    保证 从不

    此外,有两种方法可以计算出实际运行的内容——滚动自己的LogCommand函数,这是一个简单的函数 没有安全问题

        string LogCommand(SqlCommand cmd)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(cmd.CommandText);
            foreach (SqlParameter param in cmd.Parameters)
            {
                sb.Append(param.ToString());
                sb.Append(" = \"");
                sb.Append(param.Value.ToString());
                sb.AppendLine("\"");
            }
            return sb.ToString();
        }
    

        6
  •  7
  •   Steve Willcock    16 年前

        7
  •  5
  •   RedBlueThing    16 年前

    我使用了这两种方法来避免SQL注入攻击,并且肯定更喜欢参数化查询。当我使用级联查询时,我使用了一个库函数来转义变量(如mysql_real_escape_string),并且不确定我是否已经涵盖了专有实现中的所有内容(看起来你也一样)。

        8
  •  4
  •   Tim Scarborough    16 年前

        9
  •  3
  •   joshcomley    16 年前

    http://www.mywebsite.com/profile/?id=7;DROP DATABASE DB
    

    最终会变成:

    SELECT * FROM DB WHERE Id = 7;DROP DATABASE DB
    
        10
  •  2
  •   John Saunders    16 年前

    在必须将SQL放入代码的地方,我使用参数,这是唯一有意义的。提醒持不同意见的人,有比他们更聪明的黑客,并且有更好的动机破解试图战胜他们的代码。使用参数,这根本不可能,也不难。

        11
  •  2
  •   Darren Greaves    16 年前


    数据库将始终编译您的查询并缓存它,然后重用缓存的查询(这对于后续请求显然更快)。

        12
  •  1
  •   quamrana Ryuzaki L    16 年前

    由于已经给出的原因,参数是一个非常好的主意。但我们讨厌使用它们,因为创建参数并将其名称分配给变量以供以后在查询中使用是一种三重间接的破坏。

    var bldr = new SqlBuilder( myCommand );
    bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId, SqlDbType.Int);
    //or
    bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName, SqlDbType.NVarChar);
    myCommand.CommandText = bldr.ToString();
    

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace myNamespace
    {
        /// <summary>
        /// Pour le confort et le bonheur, cette classe remplace StringBuilder pour la construction
        /// des requêtes SQL, avec l'avantage qu'elle gère la création des paramètres via la méthode
        /// Value().
        /// </summary>
        public class SqlBuilder
        {
            private StringBuilder _rq;
            private SqlCommand _cmd;
            private int _seq;
            public SqlBuilder(SqlCommand cmd)
            {
                _rq = new StringBuilder();
                _cmd = cmd;
                _seq = 0;
            }
            //Les autres surcharges de StringBuilder peuvent être implémenté ici de la même façon, au besoin.
            public SqlBuilder Append(String str)
            {
                _rq.Append(str);
                return this;
            }
            /// <summary>
            /// Ajoute une valeur runtime à la requête, via un paramètre.
            /// </summary>
            /// <param name="value">La valeur à renseigner dans la requête</param>
            /// <param name="type">Le DBType à utiliser pour la création du paramètre. Se référer au type de la colonne cible.</param>
            public SqlBuilder Value(Object value, SqlDbType type)
            {
                //get param name
                string paramName = "@SqlBuilderParam" + _seq++;
                //append condition to query
                _rq.Append(paramName);
                _cmd.Parameters.Add(paramName, type).Value = value;
                return this;
            }
            public SqlBuilder FuzzyValue(Object value, SqlDbType type)
            {
                //get param name
                string paramName = "@SqlBuilderParam" + _seq++;
                //append condition to query
                _rq.Append("'%' + " + paramName + " + '%'");
                _cmd.Parameters.Add(paramName, type).Value = value;
                return this; 
            }
    
            public override string ToString()
            {
                return _rq.ToString();
            }
        }
    }
    
        13
  •  1
  •   HLGEM    16 年前

        14
  •  1
  •   David    16 年前

    http://www.sommarskog.se/dynamic_sql.html

    http://unixwiz.net/techtips/sql-injection.html

    就我个人而言,我宁愿永远不允许任何动态代码接触我的数据库,要求所有联系都是通过sps(而不是使用动态SQl的)。这意味着除了我授予用户的权限外,什么都不能做,内部用户(除了极少数出于管理目的具有生产访问权限的用户)不能直接访问我的表,造成破坏、窃取数据或实施欺诈。如果你运行一个金融应用程序,这是最安全的方法。

        15
  •  1
  •   bbsimonbb    11 年前

    最终,替代安全机制依赖于

        16
  •  1
  •   VulstaR    11 年前

    因此,让数据库自己完成识别输入的工作,更不用说当你需要实际插入奇怪的字符时,它也节省了大量的麻烦,否则这些字符会被转义或更改。它甚至可以节省一些宝贵的运行时间,因为不必计算输入。

        17
  •  1
  •   meh-uk    10 年前

    我没有看到任何其他answsers谈到“为什么自己做不好”的这一面,但考虑一下 SQL Truncation attack .

    QUOTENAME 如果你不能说服他们使用params,T-SQL函数可能会很有帮助。它抓住了很多(所有?)逃避的问题。

        19
  •  0
  •   Powerlord    16 年前

    以下是使用参数化查询的几个原因:

        20
  •  0
  •   Dennis C    16 年前

        21
  •  0
  •   Paul Fisher    16 年前

    另一个重要的考虑因素是跟踪逃逸和未逃逸的数据。有大量的应用程序,无论是Web还是其他应用程序,似乎都无法正确跟踪数据何时是原始Unicode;-编码、格式化的HTML等。很明显,很难跟踪哪些字符串是 ''