System.Data.SqlClient.SqlException:'USERNAME'附近的语法不正确。

时间:2018-07-14 13:54:54

标签: c#

我正在尝试登录,但不断出现此错误:

  

System.Data.SqlClient.SqlException:“'USERNAME'附近的语法不正确。”

这是我的代码

SqlConnection con = new SqlConnection(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\Omar\Documents\Data.mdf;Integrated Security=True;Connect Timeout=30");
SqlDataAdapter sda = new SqlDataAdapter("Select Count(*)  From [LOGIN] were USERNAME ='" + textBox1.Text +"' and PASSWORD='"+ textBox2.Text +"'",con);
DataTable dt = new DataTable();

sda.Fill(dt);

3 个答案:

答案 0 :(得分:22)

您之所以被否决,不仅是因为这是一个重复的问题,还因为您的尝试将两个安全性问题包装在了一段代码中。

因此,让我们逐步进行此操作。

1)您的实际问题是您在哪里弄错了。您更正后的代码看起来像

SqlConnection con = new SqlConnection(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\Omar\Documents\Data.mdf;Integrated Security=True;Connect Timeout=30");
SqlDataAdapter sda = new SqlDataAdapter("Select Count(*)  From [LOGIN] where USERNAME ='" + textBox1.Text +"' and PASSWORD='"+ textBox2.Text +"'",con);
DataTable dt = new DataTable();
sda.Fill(dt);

但是您不需要数据适配器或表,可以直接从SQL返回计数。所以你可以做类似的事情

string sql = "select count(*) from users where username = '" + username + "' and password = '" + password + "'";
using (SqlConnection conn = new SqlConnection(connStr))
{
    conn.Open();

    using (SqlCommand command = new SqlCommand(query, conn))
    {
        int result = (int)command.ExecuteScalar();
        if (result > 0)
        {
            /// login sucessful
        }
    }
}

这可以工作,但是您有一个称为SQL注入的安全漏洞。

如果我们查看正确的登录名,则您的SQL字符串为

select count(*) from login where username = 'alice' and password = 'bob'

这很好用,但是如果我输入登录页面的SQL注入经典示例作为密码' OR 1=1 --,那么您的SQL字符串就变成这个;

select count(*) from login where username = 'alice' and password = '' OR 1=1 --'

这行SQL将始终返回1,因为它通过SQL注入对SQL进行了劫持,并在语句OR 1=1的末尾添加了OR子句,该子句总是正确的。然后,它使用SQL注释语法注释掉其后的所有内容。因此,现在我可以以任何人的身份登录,甚至包括不存在的用户名。

2)如果不想使用为您执行此操作的ORM(实际上,使用ORM,所有保护都是自动的),则构建SQL字符串的正确方法是使用parameterized query ,它采用输入格式并设置其格式,以使任何特殊字符都不会被视为SQL命令,例如

string sql = "select count(*) from login where username = @username and password = @password";
using (SqlConnection conn = new SqlConnection(connStr))
{
    conn.Open();

    using (SqlCommand command = new SqlCommand(query, conn))
    {
        command.Parameters.Add(new SqlParameter("@username", username));
        command.Parameters.Add(new SqlParameter("@password", password));
        int result = (int)command.ExecuteScalar();
        if (result > 0)
        {
            /// login sucessful
        }
    }
}

在SQL查询中,参数为@parameterName,然后与command.Parameters.Add()添加。现在您避免了SQL注入

3)但是,这仍然是一个安全问题。 您以纯文本格式存储密码。如果我在拥有数据库副本后可以通过SQL注入访问SQL数据库(或者通过使服务器向世界开放),则可以使用您的用户名和密码,而您的公司最终将以{{3 }}。您不应该这样做。密码应该通过haveibeenpwned来保护,而不是通过加密来保护。这会将密码转换为通过单向函数从其派生的值,它将密码取回,并将其输入到一些数学运算中,结果从另一面表示密码,但是您无法返回另一种方式,则无法从加盐的哈希中找出密码。然后,当您比较登录名时,您将再次计算哈希值,并在ORM驱动的查询或参数化查询中将其用于比较中。

现在,考虑到所有这些,我强烈建议您避免自己做任何事情。 C#和ASP.NET在ASP.NET Identity中具有用于用户名和密码的库。我宁愿看到您使用它,不仅是因为我恰好是该应用程序以及整个.NET Security的PM所有者,而且还因为它可以满足您的所有需求,而无需您做任何工作。

如果这是用于真实世界的应用程序,请花一些时间来完成hashing and salting,其中包含许多安全问题的示例以及如何解决这些问题的详细信息。

答案 1 :(得分:14)

请...

  1. 了解参数;您拥有的代码可以进行SQL注入,这是一个巨大问题(尤其是,但不仅限于登录表单)
  2. 了解有关不存储纯文本密码,而使用盐腌的加密散列的信息
  3. 了解IDisposable;这里涉及的多个对象都是一次性的,需要using
  4. 除非您知道为什么使用DataTable,否则请不要使用using (var con = SqlConnection(@"...not shown...")) { var hash = HashPasswordUsingUsernameAsSalt( textBox1.Text, textBox2.Text); // impl not shown int count = con.QuerySingle<int>(@" select COUNT(1) from [LOGIN] where [USERNAME]=@cn and [PW_HASH]=@hash", new { cn = textBox1.Text, hash }); // TODO: do something with count } -没有理由在这里使用
  5. 了解可帮助您完成上述所有任务的工具

并且仅当您具有上述“按下”功能时:修正错字

以下是使用“精简版”的版本,其中涵盖了大部分内容:

'KeyError: u'IteratorGetNext:1'

答案 2 :(得分:2)

在第二行上,您似乎对WHERE子句有错字。

const getUserChoice = (userInput) => {
  userInput = userInput.toLowerCase();
  if (userInput === 'rock' || userInput === 'paper' || userInput === 'scissors') {
    return userInput;
  } else {
    console.log('Error!');
  }
}

const getComputerChoice = () => {
  switch (Math.floor(Math.random() * 3)) {
    case 0:
      return 'rock';
    case 1:
      return 'paper';
    case 2:
      return 'scissors';
  }
}

const determineWinner = (userChoice, computerChoice) => {
  if (userChoice === computerChoice) {
    return 'the game was a tie';
  }

  if (userChoice === 'rock') {
    if (computerChoice === 'paper') {
      return 'computer won!';
    } else {
      return 'you won!';
    }
  }
  if (userChoice === 'paper') {
    if (computerChoice === 'scissors') {
      return 'computer won!';
    } else {
      return 'you won!';
    }
  }
  if (userChoice === 'scissors') {
    if (computerChoice === 'rock') {
      return 'computer won!';
    } else {
      return 'you won!';
    }
  }
}
const playGame = () => {
  const userChoice = getUserChoice('rock');
  const computerChoice = getComputerChoice();
  console.log(`You threw: ${userChoice}`);
  console.log(`The computer threw: ${computerChoice}`);
  console.log(determineWinner(userChoice, computerChoice));
};

playGame();

但是,由于您的SQL容易受到黑客攻击,因此您需要了解有关SQL注入攻击的信息。