【问题标题】:Secure way to POST to Django from a trusted external (Node-RED) server?从受信任的外部(Node-RED)服务器 POST 到 Django 的安全方式?
【发布时间】:2021-08-18 21:25:13
【问题描述】:

我想要什么

我在一台服务器上有一个 Node-RED 实例,用于对数据流进行实时分析。我希望它能够将任何可操作的发现安全地通知不同服务器上的 Django 应用程序。

[编辑:我将调查结果捆绑到将请求的速率限制为 1/秒,但任何解决方案都需要足够高的性能,不会进一步减慢它。]

问题

默认情况下,Django 通过在呈现包含{% csrf_token %} 模板标签或用csrf_protect 装饰的视图。

上面似乎只允许从 Django 应用程序本身提供的页面开始的 POSTS(这可能是重点)。我的 Node-RED 实例没有从 Django 请求页面开始,因此它没有令牌/秘密可包含在 POST 中。

目前考虑的方法:

改用 GET 请求

Node-RED 发送的请求在 Django 中造成副作用,我不希望第三方发送虚假请求并让 Django 无缘无故地翻筋斗。

使用 Django 的 csrf_exempt 装饰器删除检查

见上文。

[编辑 2:我认为我实际上会选择这个选项,因为 1)Node-RED 和 Django 服务器都位于无法从外部网络访问的内部网络上,并且 2)仔细观察,似乎我的请求应该触发的操作已经可以在 csrf_exempt 的视图中访问。]

使用 HTTP 基本认证

Node-RED http request 节点有一个选项可以设置用户名/密码以在(我认为)WWW-Authenticate 标头中发送,我发现 this decorator 将基本身份验证映射到 django 身份验证。

不过,默认情况下,基本身份验证以明文形式发送,这是错误http request 节点也有启用 SSL/TLS 并指定证书、私钥等的选项,但不幸的是 Django 站点不使用 HTTPS(我讨厌这个,但它不是我雇主 atm 的优先事项)。

信任来自 [Node-RED 主机名] 的 POST

我不确定最好的方法是什么。据我所知,没有保证从请求中确定客户端主机名的方法。您可以获得 IP,但我的 Node-RED 服务器使用 DHCP,因此它的 IP 不是静态的。像这样可能会起作用:

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from socket import gethostbyaddr

@csrf_exempt
def my_view(request):
    # retrieve the client IP from the request
    # use gethostbyaddr to do a DNS lookup on the IP and get a hostname
    # if hostname matches [Node-RED hostname]:
        # process the body of the request
        return HttpResponse(status=200)
    # else:
        return HttpResponse(status=403)

我觉得这不是密封的;我对网络的理解很薄弱,但如果 Node-RED 服务器因为重新启动或网络负载过重或其他原因而失去其 DHCP 租约,攻击者可能会获得该主机名的租约并发送恶意请求。

如果客户端主机名不可靠且客户端 IP 是动态的,则在请求标头中放置某种自定义机密可能很有用,但前提是它已加密 - 否则复制起来很容易。我不知道这样做会是什么体面的方法。

这些方法中是否有接近好的解决方案?有没有针对这种情况内置的东西?

【问题讨论】:

  • Node-RED 实例如何向 Django 进行身份验证?
  • 为什么不加密 POST 数据?
  • 附带说明:对于像Let's Encrypt 这样的服务,真的没有理由使用 HTTPS。它是免费的,易于设置,甚至可以完全自动化。
  • @knbk:目前,Node-RED 不会以任何方式向 Django 进行身份验证;它只是与 Django 公开的 URL 进行交互。我以前在我自己的东西上设置了 Let's Encrypt 并喜欢它,但我在这里是一个相当初级的开发人员,没有影响力来推动它。至少这都是仅限内部的,并且在公司防火墙/代理之后。
  • 如果不使用身份验证,CSRF 不是问题,因此您可能应该使用@csrf_exempt。我不推荐 ad-hoc 加密,加密很难正确(即使使用加密库),并且设置 HTTPS 比设置任何 ad-hoc 加密更容易且不易出错。主机名很容易被欺骗,所以这也没用。 Node RED 实例是否在您的内部网络上?是否可以从网络外部访问 Django?

标签: django authentication post csrf node-red


【解决方案1】:

要使用 django API,您可以考虑使用Django REST framework,因为它允许您在没有任何 CSRF 令牌的情况下执行 HTTP 请求。

例如,使用Views Tutorial 创建一个端点并使用AllowAny Permission

views.py

from django.contrib.auth.models import User
from rest_framework import serializers
from rest_framework import viewsets
from rest_framework import permissions

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['url', 'username', 'email', 'groups']

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer
    permission_classes = [permissions.AllowAny] # or permissions.IsAuthenticated

urls.py

from django.urls import include, path
from rest_framework import routers
from tutorial.quickstart import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

然后运行 ​​manage.py runserver 并执行 GET 或 POST 请求:

  • POST /users/ {"username": "test"} -> 201 Created
  • 获取 /users/ -> 200 OK

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 2016-01-15
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多