Django开发最佳实践(下)

PART 1. 安全第一

修复漏洞的最佳时机便是开发的时候。

1.1 CSRF TOKEN

是安全体系中的一项非常重要的安全措施。但是很多情况下,一些刚刚接触的同学会发现自己好不容易写出来的表单,在的时候报错了,经过一番查找发现是的问题,然后按照网上的方法三下五除二将中的配置全部移除了,代码正常跑起来了。熟不知这种操作将极大的影响网站的安全性,且提高了后期修补漏洞的成本;而在开发阶段消灭安全问题,是成本最低的时候。关于的相关内容,官方文档上有十分详细的说明,具体用法这里不再阐述了。这里推荐一种比较方便的用法,在开发的时候对开发人员的感知较小,不用特别去关心是否已经发送成功了。在总的父模板页面中添加,并且在部分进行如下设置:

functioncsrfSafeMethod(method){

// these HTTP methods do not require CSRF protection

return(/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));

}

$.ajaxSetup({

beforeSend:function(xhr,settings){

if(!csrfSafeMethod(settings.type)&&!this.crossDomain){

xhr.setRequestHeader("X-CSRFToken",csrftoken);

}

}

});

这样所有通过中的的AJAX发起的非请求都会自动包含(获取的部分代码未列出),如果使用的是其他HTTP库或Fetch,进行对应的设置即可。

1.2 API安全设计

某些情况下,我们的Web服务还会对外提供一些API服务,供其他系统调用,对于这些API接口,CSRF是一定要关闭的,不然根本无法正常使用。除此之外,我们还应当做一些其他的安全措施,防止API接口被滥用。除了正常的传递参数外,我们应当保证这些参数不被中间人篡改,并且只允许有权限的人调用对应的接口,为此我们需要额外引入几个传递的参数:timestamp, sign, access_key, access_token。

和,这是一对参数。相当于标识调用方是谁,则是相当于调用方的秘钥,内容不应该能被简单的预测到,而可以为了方便记忆选择较为简单的字符串。只有当两个参数匹配后,才认为本次API的调用是合法的。

时间戳,根据自己的业务情况选择精确到秒或是毫秒。添加时间戳主要是为了防止本次调用被重放攻击,服务端应当校验客户端传递的时间戳是否在一个时间范围内,超时的时间戳都应当被认为是非法的请求。但是时间戳在重放的时候还是有被篡改的风险,所以就需要引入下一个参数来保证参数的真实性。

是所有参数的签名值,是所有参数值参与计算产生的,参数每变动一点,都需要重新生成,借此保证参数的真实性。常用的一套算法是:根据参数的字母序,将参数进行排序,并且使用特定的分隔符连接起所有的参数,然后进行计算,并将参数一同传递给服务端。举个例子,现有参数,根据字母序排序完成后为,加入分隔符后为,然后将这个字符串计算,生成值。用Python代码写的话比较简单:

data={

"access_token":"123456",

"access_key":"654321",

"timestamp":int(time.time()),

"key1":"value1",

"key2":"value2"

}

sep="(|)"

raw_params=sep.join(

[

str(x[1])forxinsorted(data.items(),key=lambdaitem:item[])

]

)

sign=hashlib.sha1(raw_params.encode()).hexdigest()

data["sign"]=sign

resp=requests.post(url,json=data)

result=resp.json()

1.3 一些杂项

除了上面两个比较重要的点之外,还有一些地方需要额外关注一下。

在生产环境关闭模式;

不要将添加到版本管理中,并且保护好;

设置好;

尽可能的使用提供的,避免通过等方法直接执行语句,如果无法避免,一定要将参数正确转义以避免出现注入漏洞;

尽可能的禁用,如果一定需要使用,请修改掉默认的;

PART 2. 如何部署

从git上拉下代码,使用pip安装项目依赖,通过manage.py运行服务,这一切看起来很美好,可是你真的打算在生产环境中这样做吗?

2.1 隔离环境

一般情况下,我们的服务器上都只会有一个Python环境,部署的时候一般都是通过来安装项目所需要的依赖,这些包都会被安装到全局的目录中。但是当我们部署多个项目的时候,这种安装依赖的方式很容易出现依赖冲突。举个简单的例子,我们现在有Project-A和Project-B两个项目,A和B都依赖第三方包third-A的不同版本,当我们通过的时候,依赖被安装到了全局的Python环境中,当我们再次安装的时候,也会再次安装,这个时候,如果两个项目依赖的版本不一致,譬如A项目需要版本,而B项目需要版本,就会产生依赖冲突,进而导致安装依赖失败。那么如何解决这个问题呢?我们很容易想到的就是,如果我们有多个互不相干的隔离环境,每个项目部署在一个独立的环境中,那么这个问题就迎刃而解了。正是为了解决这个问题而诞生的,它可以为每个项目创建一个单独的运行环境,从而避免依赖冲突的问题。

2.2 版本管理

前面我们知道了如何创建隔离环境并且在不同的环境中部署不同的项目,但是这里有个问题,所有环境使用的版本是一样的。如果恰好你需要部署多个不同版本的项目,例如、和项目,一个一个安装就显得有些复杂,甚至编译安装的时候一不小心少了某个编译参数而需要重新编译,都在某种程度上加大了部署工作量。我们可以通过使用来管理多个版本的问题,进一步通过的插件来管理多版本、多虚拟环境的问题。

2.3 网关接口

当我们解决了各种环境的问题后,是时候来考虑如何将项目跑起来了,如果你想的是,那就有点太过于简单了。中已经内置了一个简单的WSGI实现来供我们通过上述方式启动Web服务,如果你只是想调试或者只提供服务给几个人用的小程序,那也不失为一种可选择的方案,虽然这种方案看上去并不是那么优雅。如果你真的想将应用部署到实际的生产环境中,那么你还需要一个高性能的WSGI Server,而不是django提供的简单的。和两种都是比较主流的,根据实际部署环境,从中选择一个就好。不过我个人比较偏向于,虽然在众多的性能测试中都占了上风,选择的理由是它与相比十分简单,没有非常复杂的极少用到的功能,并且中的一些功能已经逐步被所支持,且配置起来也较为简单方便。顺带一提,如果你在Windows上部署,你可能需要使用。

2.4 反向代理

当我们的启动就绪后,就要考虑一下反向代理的问题了,之所以前面再挡一层Nginx进行反向代理,有以下几点原因:

需要来处理静态资源。如果你将的模式设置为,就会发现很多以及等静态资源加载不到了,这是因为并不会主动去处理这些请求,这些都需要来帮忙处理;

通过来进行多个的负载均衡。如果你的服务部署在多台服务器上,或是进行了一主一备的部署,这些都可以通过在上进行简单的设置来实现;

直接将或暴露出来存在一定的安全隐患,使用处理的问题会更加方便;

除此之外,还有一些理由就不在此列举了,还是上面的那句话,如果你的服务很简单,只有几个人访问,是不需要做这么复杂的设置的。

2.5 进程守护

到现在为止,我们的服务已经部署成功了,并且可以开始提供正常的服务了。但是我们少考虑了一点,如果我们的不幸因为一些未知的原因退出了,那么我们的Web服务就会变成502了。为了保证服务的稳定性,我们需要对进程进行守护,当其出现未知问题而导致异常退出的时候,需要自动将需要的进程拉起来。我们可以使用来守护进程,保证其稳定存活。但是有一点需要注意,小心不要出现Supervisord的远程命令执行漏洞,从而造成更大的事故。

PART 3. 后台服务

通常来讲,如果想启动后台服务的话,celery是一个万能的选择,但是很多时候我们并不想引入这么重的依赖,就需要自己想办法来启动后台服务了。

一个简单的方法就是做成manage.py的command,通过的方式来启动我们的后台服务,并且通过编写shell脚本控制服务的启动与停止,或者通过进行管理。如果你想让后台进程随着Web服务同时启动和停止,那么放到wsgi.py中是个不错的选择,在wsgi.py中初始化相关的后台服务,并且启动。但是这样的做法不够灵活,当需要单独更新Web服务或后台服务时,需要将二者全部重启,而采用第一种方式的情况下可以单独更新其中的某个服务。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180320G14FGB00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动