【问题标题】:simple password validation简单的密码验证
【发布时间】:2013-03-04 16:52:10
【问题描述】:

我不想制作 1000 个密码并将它们提供给用户。 密码包含 6 位数字 - 它们看起来像随机的 6 位数字。

用户来的时候除了这个密码没有其他信息,我想验证一下。

我可以遍历数据库来查找这个密码,但是我想将这个操作的难度从 O(N) 降低到 O(1),其中 N 是密码的数量。

是否有任何方法可以生成 1000 个“可能随机”的密码并通过简​​单的检查方式?

UPD:现在我正在考虑进行加密。例如(Python)

key = 'top_secret'
N = 1000
passwords = [encrypt(i, key) for i in range(N)]

def check(s):
    try:
        return int(decrypt(s, key))<1000
    except ValueError:
        return False

不过这个我想有更好的解决办法

UPD2:3 位和 6 位仅为示例。它们可能是 64 位和 128 位数字

【问题讨论】:

  • 这不是你想要的解决方案,而是简单地通过索引密码,你可以将搜索操作减少到O(log N)
  • 如果您在 1000000 个可能的值中有 1000 个密码,这意味着 1/1000 次猜测将起作用。曾几何时,有一家名为 MetroCall 的长途电话卡公司使用 6 位代码,并为每 1000 个用户添加一个新的拨号号码,认为这“足够安全”。 25 年后,今天有很多 40 岁的人可以准确地告诉你他们的第一个调制解调器平均需要多长时间才能尝试 1000 个随机数……
  • 那么 - 你实际上是在给用户一个“身份验证令牌”吗?
  • 如果 N=1000 只需将密码哈希存储在内存中(set 或 dict)。 Python 中的字典查找非常接近 O(1)。这当然会将复杂性转移到内存中,这可能会成为 N 值较大的问题。

标签: python encryption hash passwords


【解决方案1】:

创建一个以密码为键的散列。在 Perl 中的示例中,散列提供恒定时间查找 (O(1))。

这可能看起来像这样:

my %pass;

foreach ( 1 .. 1000 ) {
        # generate_next_pass() is your algorithm
        # of getting new password.
        my $p = generate_next_pass();
        $pass{$p} = 1;
}

然后 O(1) 检查密码是否在哈希中:

# $user_input is a password to be validated.
if ( exists $pass{$user_input} ) {
  # password correct
} else {
  # password incorrect
}

【讨论】:

  • 我没抓到。所以我需要生成这个散列,然后在这些散列中查看传入字符串的散列?
  • @ПавелТявин - 完全正确。只需检查用户给出的通行证是否存在于哈希中。这个操作在 Perl 中是 O(1)。我在答案中添加了一个示例。
  • 您确定检查该数组是否包含某个值是其大小的 O(1) 吗?正如上面 DavidA 所说,它是 O(log(N))
  • 通常称为amortized O(1) - stackoverflow.com/questions/3949217/…
【解决方案2】:

如果密码只有位数字,您可以将它们用作列表索引:

# Create a list of all possible passwords
pwds = [False for n in xrange(10**6)]
# Make 1000 valid passwords
for j in xrange(1000):
    i = random.randint(0, 10**6)
    print 'password is {:06d}'.format(i)
    pwds[i] = True

查找它们应该是 O(1):

testpw = '103076'
if pwds[int(testpw)]:
    print 'Password is OK'
else:
    print 'Invalid password!'

请注意,六位数密码不是很安全。它们很容易被猜到。

更好的方法是让用户选择密码。然后将该密码的 SHA512 哈希存储为字典的键,例如用户的数据作为值。当用户给出其密码时,对其进行散列并查看该密钥是否在字典中。

【讨论】:

    【解决方案3】:

    不是 100% 确定,但您可以生成 uuid4 并将其存储为身份验证->用户名查找,例如:

    import uuid
    
    userpass = {uuid.uuid4().hex:'user{:05}'.format(idx) for idx in xrange(1, 1001)}
    an_item = next(iter(userpass)) # one we know is in there
    for check in (an_item, 'bob'): # 'bob' we know won't be...
        print check, 'belongs to', userpass.get(check, '*nobody*')
    
    # 8411d50aa7ec42d8a6b4736284d1837b belongs to user00381
    # bob belongs to *nobody*
    

    【讨论】:

      【解决方案4】:

      由于密码很短,您可能需要使用位图。您需要 1000000 位 = 125000 字节 ~= 122kB,即相当少量的内存。

      所以,创建一个bitmap 并初始化它(完全未设置)。生成您的密码并在位图中设置适当的位。当用户尝试登录时检查位图。

      非常重要的警告!

      你在这里尝试做的是非常不安全

      您写道,用户仅使用其密码登录,因此,与您选择保存密码的数据结构无关,登录系统只需尝试猜测 一个密码。由于 1000 存在于 [0;1000000[ 范围内,第一次尝试猜测密码的概率为 0,001,这是一个 非常 高的值。本质上,正如您所描述的,您的系统不要求输入密码 - 它要求输入用户名。

      在某些情况下可以使用 6 位密码,但切勿将密码用作用户名+密码。

      【讨论】:

      • 正如我所说的(upd2),3位和6位只是示例。它们可以是 64 位和 128 位数字
      • 与大小无关,我的警告仍然存在。想象一下,ATM 机不需要卡,只需要大头针。你会去一个,编一个 pin(例如,12345),如果你有很多用户,组成 pin 属于 某人 的概率是相当大的。
      • 不大。正如我所说,它可以是 2^(-64)
      • “问题”最终不是密码有多少位数,而是有多少用户。
      • 没有注册用户。每个用户都是匿名的。
      猜你喜欢
      • 2016-04-05
      • 1970-01-01
      • 2016-12-13
      • 2017-04-02
      • 1970-01-01
      • 2011-12-19
      • 2020-11-07
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多