在 Django/Django Rest 中添加两步验证

San*_*096 10 python django django-rest-framework

我知道这个主题已经被广泛讨论,但大多数示例都是关于标准 Django 模板中的两因素身份验证,而在我的例子中,我想向一个项目添加两因素身份验证,其中 Django 在后端用作 API,而前端是一个原生 VueJS 应用程序。

对于与身份验证相关的所有内容,我使用内置的 Django 会话身份验证,因为前端和后端都部署在同一服务器上。

我的问题是:如何将两因素身份验证(使用 google 身份验证器或 yubikey)添加到使用 django 作为 API 的项目中?

问题是/accounts/login:最简单的方法是让用户从前端登录,一旦用户从(内置的 django 身份验证视图)登录,就提交一个表单,用户必须在其中输入代码。这种方法的问题是,一旦用户登录,Django 将创建一个会话,因此即使用户尚未提交两因素代码request.user.is_authenticated也会返回True,因此一切都取决于前端。我不喜欢这种方法,因为我担心有人可能会找到一种方法来避免提交两因素表单并在网站的其余部分上导航(因为根据 Django,会话将被验证),而无需两因素身份验证

我尝试过的:我仍然需要为此编写大部分代码,因为我想首先了解它的安全性。但这是我的方法:

第一种方法

  1. 用户提交登录表单
  2. 提交登录表单后,带有凭据的 POST 请求将发送到我的 Django 应用程序中调用的端点/authenticate。此端点将使用 Django 内置authenticate()方法来检查这些凭据是否属于用户,而无需创建会话。
  3. 如果凭证属于某个用户,它将返回True给该用户。此时,用户将提交包含 2FA 代码的表单,如果代码正确,则会将请求发送到,该请求/accounts/login将再次检查密码和电子邮件,并实际登录用户并创建会话,这一次。

第二种方法 另一种更好的方法是覆盖 Django-Allauth 登录视图,以便我可以添加对令牌的检查,例如(警告:伪代码):

if provided_code == user_code:
   login()
   return HttpResponse({'Result': 'Logged in!'})
else:
   return HttpResponse({'Result': 'incorrect code'})
Run Code Online (Sandbox Code Playgroud)

provided_code用户提供的 2FA 代码在哪里?user_code我将从另一个函数(例如get_user_2fa_code(user).

我不是安全专家,但我没有想出更好的方法。会有多安全呢?有没有更好的方法将 2FA 身份验证添加到 Django 项目(其中 django 作为后端 API)?

这是登录视图

class LoginView(
    RedirectAuthenticatedUserMixin, AjaxCapableProcessFormViewMixin, FormView
):
    form_class = LoginForm
    template_name = "account/login." + app_settings.TEMPLATE_EXTENSION
    success_url = None
    redirect_field_name = "next"

    @sensitive_post_parameters_m
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView, self).dispatch(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super(LoginView, self).get_form_kwargs()
        kwargs["request"] = self.request
        return kwargs

    def get_form_class(self):
        return get_form_class(app_settings.FORMS, "login", self.form_class)

    def form_valid(self, form):
        success_url = self.get_success_url()
        try:
            return form.login(self.request, redirect_url=success_url)
        except ImmediateHttpResponse as e:
            return e.response

    def get_success_url(self):
        # Explicitly passed ?next= URL takes precedence
        ret = (
            get_next_redirect_url(self.request, self.redirect_field_name)
            or self.success_url
        )
        return ret

    def get_context_data(self, **kwargs):
        ret = super(LoginView, self).get_context_data(**kwargs)
        signup_url = passthrough_next_redirect_url(
            self.request, reverse("account_signup"), self.redirect_field_name
        )
        redirect_field_value = get_request_param(self.request, self.redirect_field_name)
        site = get_current_site(self.request)

        ret.update(
            {
                "signup_url": signup_url,
                "site": site,
                "redirect_field_name": self.redirect_field_name,
                "redirect_field_value": redirect_field_value,
            }
        )
        return ret
Run Code Online (Sandbox Code Playgroud)

rob*_*rob 3

这可能会有所帮助..我不确定。大多数现有的 django 2fa 项目都是按照你描述的方式工作的——我建议检查他们是如何做的以获得一些更好的提示。

  1. 首先使用 检查用户名/密码django.contrib.auth.authenticate。此身份验证是凭据,但不会创建会话。
  2. 如果用户有 2FA-将他们路由到页面以执行验证步骤
  3. 验证步骤完成后 - 使用以下命令登录django.contrib.auth.login

对于登录身份验证视图和发布凭据 - 我做了这样的事情:

class TwoFactorAwareLoginView(TemplateView):
    def post(self, request, *args, **kwargs):
        form = self.form_class(data=request.POST, request=request)
        if form.is_valid():
            username = form.cleaned_data.get("username")
            password = form.cleaned_data.get("password")
            user = authenticate(request, username=username, password=password)
            if user is not None:
                devices = <something to get your 2FA device blobs>
                if not devices: #no user devices- log them in
                    login(request, user)
                    return HttpResponseRedirect(reverse("cool-page"))
                else: #2FA devices exist- route them to verification view and perform auth_login once complete
                    # add some context needed for verification view in session or context
                    return HttpResponseRedirect(reverse("two-factor:verify-login"))
        return render(request, self.template_name, {"form": form})
Run Code Online (Sandbox Code Playgroud)