2009-09-11 64 views
3

哪一部分(S)比方说,我有这样的SQL语句来检查用户登录:确定的WHERE语句失败

SELECT * FROM users 
WHERE username='[email protected]', password='abc123', expire_date>=NOW(); 

是否有SQL的方式来确定具体哪些WHERE条件达不到,不必须将每个条件分解为自己的查询并单独测试?

在这个特定的例子中,开发人员可以准确地告诉用户他们尝试登录失败的原因。

为了我的目的,我使用的是PHP/MySQL。

+1

expire_date的时间部分可能小于NOW()中返回的值,导致查询不返回您期望的结果。 – 2009-09-11 17:17:20

+0

谢谢,我意识到,即使名称有些误导,该字段也会存储时间。 – macinjosh 2009-09-11 17:19:37

+3

你打算把失败的原因传给用户吗?这可能会让黑客更容易获得访问权限。 – 2009-09-11 17:25:54

回答

0

这里是我想出了:

SELECT 
    IF(mem_username='[email protected]','true','Error: Bad Username') AS mem_username, 
    IF(mem_password ='abc123','true','Error: Bad Password') AS mem_password' 
FROM MEMBERS 
WHERE mem_username='[email protected]' AND mem_password='abc123' 

这样我可以在代码中发现了错误信息,然后显示它们是必要的。

注意:对于大家引用示例代码的安全问题,感谢您的关注。但是,这不是真正的生产代码,这仅仅是一个简单的例子来证明我的问题。很明显,您不应该以明文形式存储密码,也不应该为用户提供关于登录失败原因的具体信息。

+0

不要帮助黑客让他们知道他们已经得到用户名和密码错误!另请参阅其他人关于不在数据库中明确存储密码的回复! – 2009-09-11 17:52:49

+0

这个SQL真的很拗口;首先,重复一个字段名称作为别名是不好的计划; 秒,它永远不会给你一个错误信息 - WHERE子句将只返回使两个IF子句都为真的记录; 第三,'糟糕的用户名​​'测试有点离奇;你真的想告诉某人'你有正确的密码,但对于不同的用户'? 我建议 SELECT IF(mem_password_hash = 'c0e1732fa',真,假) 议员WHERE mem_username='[email protected]” 你已经知道用户名;如果它返回true,则它们通过,false表示错误的密码,NULL表示错误的用户名。 – 2009-09-11 23:26:34

6

那么,你可以做的一件事就是改变你的查询,所以它只匹配用户名。然后在代码中检查密码和到期日期,返回适当的错误。

另外,我希望你的例子查询是一个简化;当然,你应该腌制/加密/散列你的密码,并且你应该包括一些限制在一定时间内失败的尝试次数等等......

就你的实际问题而言正在寻找),没有办法从where子句中获取该信息。你可以做最接近的将是这样的:

SELECT *, 
    CASE WHEN Password = 'asdf' THEN 1 ELSE 0 END AS IsPasswordMatch, 
    CASE WHEN Expiration >= NOW() THEN 1 ELSE 0 END AS IsActiveAccount 
FROM Users 
WHERE Username = 'user' 
+0

由于我们目前的代码结构,只有SQL的解决方案是理想的。 – macinjosh 2009-09-11 17:22:37

+1

这是推荐的方法。根据名称获取用户行(如果失败,没有用户),然后比较密码散列(如果失败,则输入错误密码),最后存储行ID(用户ID)以及会话中的其他内容并将其登录。 – Xeoncross 2009-09-11 17:25:52

1

不行,where子句被应用为块,以及各种技术被用来使得不是所有行先要进行扫描。另外,你怎么知道哪一行是所需的?

另外,您可能不想告诉用户太多关于登录尝试失败的原因。说太多就会导致诸如帐号挖掘和密码攻击等攻击。

编辑如果你真的不想要显示给你的用户,然后分裂你的逻辑分成不同的部分:

  1. 验证身份
    操作:从数据库 获取相应的用户行
    结果:
    • 如果没有这样的行存在=>无效帐户
    • 如果返回行,继续步骤2
  2. 验证凭证
    操作:检查所存储的凭证(密码,密码的散列或加密的密码)对以相同的方式处理提供的密码凭证被存储。
    结果:
    • 没有匹配=>无效的密码/凭据
    • 匹配=>成功登录尝试
  3. 登录用户
    操作:将数据添加到会话等。
+0

在我的具体情况中,我们确实想告诉用户这些信息。我们已考虑到安全风险。 – macinjosh 2009-09-11 17:22:02

+0

这(和法案)仍然是最好的答案。仅仅因为你的查询返回结果并不意味着你实际上已经把所有的信息都提供给了用户。 – longneck 2009-09-11 17:34:59

+0

对,我同意。一个人不必公开auth失败的原因。但开发人员可能仍然想知道给定用户帐户的重复失败密码,例如建立锁定。 – 2009-09-11 17:40:51

0

你可能只需要对部分分开与where子句“与”

SELECT * FROM users 
WHERE username='[email protected]' 
    And password='abc123' 
    And expire_date>=NOW(); 
4

在MySQL中,你可以把布尔表达式在选择列表中。布尔表达式的计算结果为true时为整数1,否则为false。

SELECT password = 'abc123' AS is_authenticated, 
     expire_date >= NOW() AS is_not_expired 
FROM users 
WHERE username='[email protected]'; 

注:如果你需要写上其他品牌的RDBMS的工作的查询,请记住这个使用布尔表达式的是非标准的。使用其他人发布的CASE语法。

PS:这是您的问题的切线,但我强烈建议您不要以明文形式存储密码。存储腌制密码的散列摘要。见How does password salt help against a rainbow table attack?

+0

感谢您对我的安全性的关注,这真的只是一个查询不是真实的事情!我同意哈希是必须的! – macinjosh 2009-09-11 17:35:23

+0

好的,酷,我想也许你的查询是从真实的东西简化了,但我想确保提及哈希以防万一。 :) – 2009-09-11 17:38:57