【问题标题】:What are possible mistakes when connecting to SQL Server from C#?从 C# 连接到 SQL Server 时可能出现哪些错误?
【发布时间】:2019-04-02 02:49:40
【问题描述】:

我在 C# 中创建了一个连接到 SQL Server 2008 的登录表单。数据读取器出现错误。我该怎么办?

这是我的决赛项目

SqlConnection con = new SqlConnection(@"Data Source=SAMSUNG-PC\SQLEXPRESS;Initial Catalog=LOGIN;Integrated Security=True");//this is my sql pc server name
SqlDataReader dr;

SqlCommand cmd = new SqlCommand("Select * from tbl_log where Username ='" + textBox1.Text + "' and Password = '", con);

con.Open();

cmd.Parameters.AddWithValue("un", textBox1.Text);
cmd.Parameters.AddWithValue("pw", textBox2.Text);

dr = cmd.ExecuteReader();//this is my problem//

if (dr.HasRows)//and this//
{
    menu menu = new menu();
    menu.Show();
    this.Hide();
}
else
{
    MessageBox.Show("error");
}
con.Close();

我希望得到这样的输出:如果密码正确,则转到新表单 如果密码不正确,则会出现消息框并显示“错误”。

【问题讨论】:

  • 在数据访问中可能犯的最严重错误是:SQL 注入;你已经做到了……你的意思是"Select * from tbl_log where Username = @un and Password = @pw"吗?您在安全方面可能犯的第二个最严重的错误是将密码存储为文本。你是二换二!第三个错误可能是:没有“处理”一次性物品 - 所以......在concmddr 周围有很多using。第四个错误是让代码在一个方法中同时触及数据库 UI :) 如果我要进行第五个错误,那就是:返回您不需要/使用的列。
  • 在我看来,您至少试图通过使用Parameters.AddWithValue 来避免 SQL 注入,但您做错了。我怀疑您已经阅读了一些关于如何实现所需行为的不同来源,但是以一种没有意义的方式将它们混合在一起。 @Marc Gravell 有一些你应该听的非常好的观点。
  • 哦,最后:使用 SQL Server 2008 - 虽然我在这里公平地说,并承认 提供了另外 3 个月的扩展支持。但是……来吧;现在是 2019 年!

标签: c# sql-server


【解决方案1】:

这里有很多需要不同的地方。重要的是:

  • 正确的查询参数化(避免 sql 注入攻击)
  • 不要以纯文本形式存储密码。 甚至不要在学习/练习代码中这样做!密码应该单向散列(也不要加密它们)使用有信誉的算法。
  • using 阻止,以避免在引发异常时保持连接打开,以避免对您的数据库造成拒绝服务情况
  • 不要混合使用 UI 和数据库访问。真的,即使我下面的代码也应该有两层,其中身份验证代码与数据库代码位于一个单独的类中

这样的东西要好得多:

public static class DB 
{
    private static string ConnectionString = @"Data Source=SAMSUNG-PC\SQLEXPRESS;Initial Catalog=LOGIN;Integrated Security=True";

    public static bool ValidateUserCredentials(string username, string password)
    {
                        //PwdHash column should be Char(60) (not VarChar, not NChar)
        string sql = "Select PwdHash from tbl_log where Username = @User";

        string hash = "";
        using (var cn = new SqlConnection(ConnectionString))
        using (var cmd = new SqlCommand(sql, cn))
        {
            //use actual column types and lengths from the database here
            // Do NOT use AddWithValue()!
            cmd.Parameters.Add("@User", SqlDbType.NVarChar, 20).Value = username;

            //keep the connection active for as brief a time as possible
            cn.Open();
            using (var rdr = cmd.ExecuteReader())
            {
                if (!rdr.Read()) return false;    
                hash = (string)rdr["PwdHash"];
            }
        }
        //based on this NuGet bcrypt library:
        //https://www.nuget.org/packages/BCrypt-Official/
        if (BCrypt.Net.BCrypt.Verify(password, hash)) return true;
        return false;
    }
}

然后你可以像这样从用户界面使用它:

if (DB.ValidateUserCredentials(textBox1.Text, textBox2.Text))
{
    menu mu = new menu(); //can't give a variable the same name as it's type
    mu.Show();
    this.Hide();
}
else
{
    MessageBox.Show("error");
}

【讨论】:

  • 另一个需要特别注意的好注意事项(不仅仅是在代码中)是避免addwithvalue
【解决方案2】:

在您从@Marc Gravell 收到的所有正确评论等...

要专门针对您的实际问题, 你应该改正

SqlCommand cmd = new SqlCommand("Select * from tbl_log where Username ='" + textBox1.Text + "' and Password = '", con);

//Here you're trying to add parameters that don't exist    
cmd.Parameters.AddWithValue("un", textBox1.Text);
cmd.Parameters.AddWithValue("pw", textBox2.Text);

SqlCommand cmd = new SqlCommand("Select * from tbl_log where Username = @UserName and Password = @Password", con);

cmd.Parameters.AddWithValue("@UserName", textBox1.Text);
cmd.Parameters.AddWithValue("@Password", textBox2.Text);

还可以帮助您理解错误(并且因为它是对 SQL 的调用), 你应该用try-catch 包装代码

最后一句话,因为你是初学者...... 使用有意义的名称。尽量避免缩写,这是一个坏习惯。

【讨论】:

  • con/cn/conn+cmd+dr/rdr 很常见,我更担心如果他们真的写出整个“命令”或“阅读器”,他们可能会“经验不足”名字。
  • 这可能是对原始问题的最佳答案。最初的问题不是要求最佳实践或安全代码,而是如何让非工作代码工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-27
  • 2023-04-04
  • 1970-01-01
相关资源
最近更新 更多