Django 实现有点安全的网站登录认证机制——口令存储

前面一篇实现了一个漏洞百出的网站登录认证机制,这里在前儿的基础上加上一点安全机制

口令存储安全

在基于口令的认证机制中,一个口令对于用户的重要性不用多说,掌握了口令就拥有了这个用户的使用权,所以怎样避免口令泄漏是一个基础却又相当重要的安全问题

明文存储问题

作为应用的服务端,我们的后台数据库中存储着所有用户的口令数据,怎样实现安全的存储方案就是我们所需要考虑的

而在前一篇实现的认证机制中,我们的用户口令是明文存储在数据库中的
前实现
可以看出,这里存储的 password 是直接从表单中获取的字符序列值,中间并没有任何加密处理的过程
显然这很不安全,容易被社会工程渗透,而且一旦攻击者入侵了数据库,这些口令就都白给了
所以我们需要先对口令进行加密处理,再存储到数据库中

摘要处理

一个常用的处理方法就是,使用散列算法对口令进行摘要,存储得到的散列值
这样的话,即使攻击者爆破了数据库,能获取到的也只是口令的散列值,因为散列是一个单向的过程,所以不可能逆推得到口令
当要验证口令时,只需要用相同的散列算法对用户输入的口令进行摘要,把计算得到的散列值和数据库中存储的散列值进行对比,如果一致则认证通过
在这里插入图片描述
但这样也还不够安全,毕竟散列值的长度是固定的,说明散列值的范围是有限的。这样必然会出现碰撞情况,即多个不同字符序列的散列值却是一样的
即便从散列值无法逆推得到完全正确的原口令,只需要找到一个跟原口令散列碰撞的字符序列,同样可以使用这个字符序列通过认证
而且,有限代表可以遍历。如果彩虹表足够大,通过查询彩虹表,不难找到一个与散列值对应的序列
所以在对口令进行摘要的过程,我们还需要加盐(salt)

撒盐

原序列每一位比特的变化对散列结果的影响都是千差地别的,对撒了盐的口令取摘要,即便攻击者获取了撒盐口令的散列值,他不仅需要猜测口令,还需要猜测盐值,口令破解的难度呈指数剧增

这里可以直接调用 Django 内置哈希模块 django.contrib.auth.hashers 的口令生成函数 make_password

下面是经过改进的注册视图函数

# views.py

from django.contrib.auth.hashers import make_password
from django.shortcuts import render
from Blog.models import User

def register(request):
    if request.session.get('is_login', None):
        request.session.flush()
    if request.method == 'POST':
        username = request.POST.get('username', '').strip()
        password = request.POST.get('password', '').strip()
        re_password = request.POST.get('re_password', '').strip()
        if password == re_password:
            try:
                User.objects.get(username=username)
            except User.DoesNotExist:
                encrypted_password = make_password(password, None, 'pbkdf2_sha256')
                new_user = User(username=username, password=encrypted_password)
                new_user.save()
                return render(request, 'register.html', {'mess': '用户注册成功'})
            else:
                return render(request, 'register.html', {'mess': '该用户已被注册'})
        else:
            return render(request, 'register.html', {'mess': '两次密码输入不一致'})
    return render(request, 'register.html')

make_password(password, salt=None, hasher=‘default’)
其中,第二个参数指定盐值,如果为 None 则随机生成盐值(建议随机)
存储
这是一个设置盐值为 ‘set’ 的口令在数据库中存储的值,其使用的散列算法是 pbkdf2_sha256

在登录验证时,使用和 make_password 配套的 check_password 函数进行口令验证

# views.py

from django.contrib.auth.hashers import check_password
from django.shortcuts import render, redirect
from Blog.models import User

def login(request):
    if request.session.get('is_login', None):  # 检查是否已登录
        return redirect('/index/')
    if request.method == 'POST':  # 如果有提交表单
        username = request.POST.get('username', '').strip()
        password = request.POST.get('password', '').strip()
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            return render(request, 'login.html', {'mess': '用户不存在'})
        else:
            if check_password(password, user.password):
                request.session['is_login'] = 'T'  # 标记已登录
                request.session['user_id'] = user.id
                request.session['username'] = username
                return redirect('/index/')
            else:
                return render(request, 'login.html', {'mess': '密码输入错误'})
    return render(request, 'login.html')

一些安全建议

世界上不存在绝对的安全,再强大的安全机制也存在被破坏、绕过的可能。同样,再安全的机制遇到不安全的开发或维护习惯也远远发挥不出其预期的效果。这里给出一些安全建议,有助于培养良好安全的习惯

首先,网站关联的数据库用户不能被给予太高的权限,可满足工作条件的最小权限即可,不然被攻击者通过 SQL 注入获取了数据库的后台就很容易造成更大的破坏。
比如,仅开放网站应用数据库的访问权限,禁止访问其他与网站应用不相关的数据库。虽然不能完全杜绝攻击者对其他数据库的入侵(攻击者可以通过一些漏洞进行提权),但可以增大攻击者入侵的难度,一定程度上提高了安全性

其次,尽可能少的开放外网 IP 可访问数据库的用户权限,尽量在内部环境对数据库管理和维护。对于必须开放外网 IP 访问权限的用户,需设置强口令且不定期更换

除此之外,做好对数据库的管理账户和口令的保密工作,不可明文进行记录,避免社会工程渗透。维护人员若非工作尽可能少的使用管理账户,工作结束应当及时注销。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值