【问题标题】:Secure website from SQL Injection ' using ASP.net and an Access database使用 ASP.net 和 Access 数据库从 SQL 注入中保护网站
【发布时间】:2013-03-14 16:02:32
【问题描述】:

我目前有一个正常注册和登录的网站,使用 ASP.net 编码。 我正在使用 Access 数据库,同时使用我朋友编写的 C# 类来处理大多数数据库操作(executeQuery、executeRead、isExits...)。

现在我几乎已经完成了我的网站的构建,我想开始添加安全性 - 主要是添加到我的数据库中。我已经搜索了一段时间来寻找有关该主题的教程,但是除了一篇旧的 microsoft msdn 文章之外,我找不到任何好的东西,我无法真正让它的代码工作。 我现在最远的就是不允许在用户名和密码中使用任何危险字符(例如',--,;),但感觉好像这是我可以使用的更糟糕的解决方案(为什么不应该我的用户不使用这些字符吗?)。

我认为我找到的最佳解决方案是以某种方式在声明后将变量插入查询字符串(与“WHERE username=@user”或类似的东西有关),但我无法得到它使用 Access 和我的 oleDBManager。

这是我目前的注册码。 handle() 正在从字符串中删除所有 ',而 Validate() 会检查字符串中的危险部分。

        string username = user.Text;
        string password = pass.Text;
        bool isThingy = false;
        if (handle(ref password)) isThingy = true;
        if (handle(ref username)) isThingy = true;

        if (username != "" && username != null)
        {
            if (password != "" && password != null)
            {
                if (Validate(username, password))
                {
                    if ((db.IsExist("SELECT * FROM Table1 WHERE username='" + username + "'") == false))
                    {
                        int a = db.ExecuteQuery("INSERT INTO `Table1`(`username`, `password`, `logins`, `email`, `fname`, `lname`, `country`, `city`, `birthday`, `userid`) VALUES ('" + username + "', '" + password + "', '0', '', '', '', '', '', '', '" + Convert.ToString(Convert.ToInt32(db.ExecuteCellRead("SELECT MAX(userid) FROM Table1")) + 1) + "');");

                        if (!isThingy) errorLabel.Text = "Your user has been successfully registered";
                        else errorLabel.Text = "The ' token is invalid. your user was registered absence the '.";
                    }
                    else
                        errorLabel.Text = "This username is already taken";
                }
                else errorLabel.Text = "Invalid name format";

            }
            else errorLabel.Text = "Please enter a password";
        }
        else errorLabel.Text = "Please enter a user name";

至于 oleDBManager(在我的代码中命名为 db):

    private OleDbConnection link; // The link instance
    private OleDbCommand command; // The command object
    private OleDbDataReader dataReader; // The data reader object
    private OleDbDataAdapter dataAdapter; // the data adapter object
    private DataTable dataTable; // the data table object
    private string dbName; // the Database filename
    private int version; // the usersTableG office version
    private string connectionString; // the connection string for the database connection
    private string provider; // the matching driver string for the connection string
    private string path; // the path to the database file


...


    public int ExecuteQuery(string query)
    {
        this.link.Open();
        int rowsAffected;
        // ---
        this.command = new OleDbCommand(query, this.link);
        try
        {
            rowsAffected = this.command.ExecuteNonQuery();
        }
        catch (InvalidOperationException e)
        {
            if (e.Data == null)
                throw;
            else
                rowsAffected = -1;
        }
        finally
        {
            this.command.Dispose();
            this.link.Close();
        }
        // ---
        return rowsAffected;
    }

    public bool IsExist(string query)
    {
        this.link.Open();
        // ---
        this.command = new OleDbCommand(query, this.link);
        this.dataReader = this.command.ExecuteReader();
        bool a = this.dataReader.Read();
        // ---
        this.command.Dispose();
        this.link.Close();
        // ---
        return a;
    }

    public string ExecuteCellRead(string query)
    {
        string output = "";
        this.dataTable = this.ExcecuteRead(query);

        foreach (DataRow row in this.dataTable.Rows)
        {
            foreach (object obj in row.ItemArray)
            {
                output += obj.ToString();
            }
        }

        return output;
    }

所以,如您所见,主要问题是用户现在不能将字符用作 '. 它认为最好的解决方案是在 SQL 查询中使用 @ 变量,但我不知道如何。

[感谢您的帮助] PS。我已经更改了我的表名;)

编辑:你们中的大多数人都在告诉我使用这些参数化查询,但是如果你能给我一个如何使用它们的例子,那就太好了,因为我从来没有这样做过



所以,感谢@Remou,我的FINAL代码是:

    db.DoWeirdStackOverFlowStuff(
    "INSERT INTO `Table1`(`username`, `password`, `logins`) VALUES (@username, @password, '0');"
    , new string[] { "@username", "@password" }
    , new string[] { username, password });

    public int DoWeirdStackOverFlowStuff(string query, string[] vars, string[] reps)
    {
        this.link.Open();
        int rowsAffected;
        // ---
        this.command = new OleDbCommand();
        this.command.CommandText = query;
        this.command.CommandType = System.Data.CommandType.Text;
        this.command.Connection = this.link;

        //Parameters in the order in which they appear in the query
        for (int i = 0; i < vars.Length; i++)
            this.command.Parameters.AddWithValue(vars[i], reps[i]);

        try
        {
            rowsAffected = this.command.ExecuteNonQuery();
        }
        catch (InvalidOperationException e)
        {
            if (e.Data == null)
                throw;
            else
                rowsAffected = -1;
        }
        finally
        {
            this.command.Dispose();
            this.link.Close();
        }
        // ---
        return rowsAffected;
    }

给需要这个的人=]

【问题讨论】:

  • Visual Studio 和在线 MSDN 都有参数化 sql 命令的简单示例。
  • 这里是 SQL 注入预防的介绍 owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet(我知道它不是特定于 .NET,但原则是相同的)
  • 使用参数化的 sql 命令。黑名单字符不会阻止您进行 sql 注入。你应该只在你的数据库中保存密码哈希。不是他们自己的密码。并使用盐来保护它们。
  • 这个SELECT MAX(userid) FROM Table1")) + 1) 非常不安全。为什么不使用自动编号?
  • 另外,为什么你的代码中有sql?在带参数的MS Access中创建查询,引用查询名称cmd.CommandText = "CreateUser"cmd.CommandType = adCmdStoredProc

标签: asp.net sql ms-access security sql-injection


【解决方案1】:

一些笔记

在 MS Access 中,我保存了一个名为 UpdateUser 的查询,它看起来像这样:

       UPDATE INTERNETSETTINGS 
       SET url = [@url], 
           databasename = [@databasename], 
           port = [@port], 
           username = [@username],
           [password] = [@password]

我可以使用命令对象在我的代码中按名称引用此查询:

        OleDbCommand Command = new OleDbCommand();

        Command.CommandText = "UpdateUser"; //saved query
        Command.CommandType = System.Data.CommandType.StoredProcedure;
        Command.Connection = cn; //a connection to the database

        //Parameters in the order in which they appear in the query
        Command.Parameters.AddWithValue("@url", "a"); //a,b,c etc for my test run
        Command.Parameters.AddWithValue("@databasename", "b");
        Command.Parameters.AddWithValue("@port","c");
        Command.Parameters.AddWithValue("@username", "d");
        Command.Parameters.AddWithValue("@password", "e");

        Command.ExecuteNonQuery();

【讨论】:

  • 我还没有真正进入网络编程的这一部分,因为我已经准备好 C# 类来为我做所有这些,所以如果我有点慢,我很抱歉。查询在您编写的代码中位于何处? (我应该写它而不是“UpdateUser”吗?)顺便感谢您的快速帮助,“a”,“b”,“c”和其他应该用真实变量替换,对(只是检查)
  • 我添加了一些额外的注释。您可以为 CommandText 保存 sql 字符串或保存的查询,您需要使用适当的 CommandType。保存的查询是最好的,因为您可以测试它们并且它们不会弄乱您的代码。只需使用查询名称。至于参数,名称无关紧要,除了提示您,但您必须以正确的顺序获取它们。正如你所说,a、b、c 等用于测试,我建议你做类似的事情,直到它按照你想要的方式工作。
  • 再次感谢您的帮助,但是,还有一个问题 - 当我运行代码时,它在Command.ExecuteNonQuery(); 行中断,说“EXECUTE 后的预期查询名称”。我会在一分钟内将我当前的代码添加到问题中。
  • 您没有更改命令类型,它不是存储过程,因为您输入的是 SQL 字符串,所以:command.CommandType = System.Data.CommandType.Text;
  • 是的,工作!感谢您所做的一切以及快速解释清楚的答案=]
【解决方案2】:

我不记得这里的 Access 是否与 SQL Server 做同样的事情,但在 SQL Server 中,您可以通过加倍来转义单引号:

username = username.Replace("'", "''");

所以你可以在字符串中包含单引号,你可以将它们存储在数据库中,并且它们不能用作恶意字符串终止符。

【讨论】:

  • 但是用户名又发生了变化(假设从“joe's”变为“joe's”,这不是很好
  • 实际上 - 它没有。写入数据库字段的是“joe's”。但是你必须记住每次在sql中使用这个值时都要进行替换。不过,参数化查询是解决这个问题的正确方法。
  • 这种方法,虽然总比没有好,但仍然可以打败一些人 - 看看this answer and links,其中某种时髦的 Unicode hack 使引号转义技术无效。
  • U+02BC hack 很可爱,但它在这里不起作用,因为没有对 Ascii 的隐式转换。转义引号是合理的。
猜你喜欢
  • 2020-05-06
  • 2011-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
相关资源
最近更新 更多