电子公文传输系统安全性设计与实现
电子公文传输系统安全性设计与实现
源代码:码云
一、系统概述
电子公文传输系统网站分为网站首页、公文列表、公文详情、公文批改、公文搜索、用户管理、上传下载等7个功能模块。
建议直接放到D盘下运行,否则使用绝对路径的会报错。
1.网站首页
用户必须属于登录状态
根据登录用户的密级显示其对应的公文,主要根据搜索量、上传时间、查看量、下载量、公文分类方便用户快速定位到自己想要看的公文,如果都未能找到的话可以直接在搜索框中搜索(支持模糊搜索)
下图是普通用户登录后的主页面:
有密级的用户登录可以额外看到有密级的公文:
2.公文列表
公文列表由左侧的公文分类点击进入,默认查看所有公文,或者根据左边的公文分类按类查看,或者直接搜索
根据查看量进行排序,可以点击封面图片或者标题进入详情页
无密级用户与有密级用户相比没有有密级的公文展示
普通用户:12篇无密级的公文
有密级用户:还有5篇有密级公文
3.公文详情
可以查看公文的所有信息,有下载(加密后的)的功能,在线查看功能,公文批复功能(没有批复权限的用户没有这个按钮),搜索框可以搜索公文,详情页展示公文的图片、公文的相关信息和公文摘要,还有观看列表、相关公文来帮助用户提高效率,还加入了公文纠错的功能。
在线查看:
下载:
2号公文为有密级的公文,如果无密级的用户访问(以user1为例)会被直接拒绝,并报告管理员,报警灯亮,记录日志
4.公文批改
在文本框中输入批改意见(需要批改权限)
无批改权限的用户(以user1为例)点击批改会直接报403无权限的错
5.公文搜索
没有做单独的页面,都在其他的页面中
6.用户管理
用户登录,验证用户名、密码和验证码
用户注册
用户注册后,会被赋予一个默认的普通用户的权限,管理员用户在项目开发阶段产生一个,如无特殊需要不再产生,且不允许通过注册产生。
普通用户的权限主要是查询一些表(与公文数据相关),而管理员有增、改、删的权限,超级管理员拥有全部权限,游客组无权限。
如下图所示:
用户中心,可以查看自己看过哪些公文,可以上传公文(用户登录在前)
上传:有密级的用户才能上传有密级的公文,普通用户只能上传公开公文
普通用户视图,只能上传无密级的文件:
有密级用户视图:
后台管理:
管理员登录:
可以实现用户、用户组、公文的数据管理
7.上传下载
下载在公文详情中已经介绍过了
上传在用户中心介绍过了
二、安全性措施
-
身份认证(用户名-口令)
- 对口令做了加盐的sha-256哈希保护用户的个人隐私
-
访问控制(只能访问不大于自己密级的文件)
- 低密级的用户的页面中不存在高密级的文件
- 低密级的用户试图通过路由猜测文件的位置会被返回失败并记录日志
-
防御跨站脚本攻击(XSS)
- 转义特殊字符来防御XSS攻击:<转义为<, >转义为>, ’转义为' “转义为" ,& 转义为&
-
防御跨站点请求伪造(CSRF)
- 通过检查每一个 POST 请求中的密文来实现。这保证恶意用户不能“复现”一个表单并用 POST 提交到你的网页,并让一个已登录用户无意中提交该表单。恶意用户必须知道特定于用户的密文(使用 cookie)
-
防御 SQL 注入
- 查询的 SQL 代码与查询的参数是分开定义的。参数可能来自用户从而不安全,因此它们由底层数据库引擎进行转义。
-
防御访问劫持
- 在框架或 iframe 中加载资源。如果响应包含值为
SAMEORIGIN
的头,那么只有当请求来自同一个网站时,浏览器才会在框架中加载资源
- 在框架或 iframe 中加载资源。如果响应包含值为
-
SSL/HTTPS
- 恶意用户不能在客户端和服务器之间嗅探验证资格或其他信息
-
Host 头部验证
- 当
DEBUG
为True
和ALLOWED_HOSTS
为空时,主机将根据['.localhost', '127.0.0.1', '[::1]']
进行验证,防止非预期的访问
- 当
-
Referrer 策略
- 保护用户隐私
-
会话安全
- 要求一个被部署的网页应让不受信任的用户不能访问任何子域
-
文件加密传输
- 用非对称密钥(SM2)保护对称密钥,再用对称密钥实现对文件的加解密
-
文件安全性校验
- 检查文件二进制格式,防止PE文件冒名传输
三、实现的功能
-
用户注册登录
-
用户的权限管理
-
文件在线查看
-
文件上传、下载
-
文件批改
1.密码算法
实现了基于python库gmssl和pysmx的sm2、sm3、sm4算法,加密公文还使用了rc4对称算法,采用sha-256算法保护用户的密码
sm2加解密、签名验签代码如下
from pysmx.SM2 import generate_keypair
import pysmx
import base64
len_para = 64
pk, sk = generate_keypair(len_para)
sig = pysmx.SM2.Sign("20181204", sk, '12345678', len_para)
print('签名验签')
print("签名:", base64.b64encode(sig))
print("明文:20181204")
print("验签结果:",pysmx.SM2.Verify(sig, "20181204", pk, len_para))
hash_algorithm = "sm3"
e = b"20181204"
print("-"*100)
print('加解密')
print('明文 = %s' % e)
C = pysmx.SM2.Encrypt(e, pk, len_para, 0, hash_algorithm=hash_algorithm)
print('密文 = %s' % C)
m = pysmx.SM2.Decrypt(C, sk, len_para, hash_algorithm=hash_algorithm)
print('解密结果 = %s' % m)
sm3哈希算法如下
from pysmx.SM3 import digest
from pysmx.SM3 import SM3
'''
s = '20181204'
print(digest(s))
'''
sm3 = SM3()
sm3.update('20181204')
print(sm3.hexdigest())
sm4对称算法加解密代码如下
from pysmx.SM4 import Sm4, ENCRYPT, DECRYPT
import base64
key_data = b'2018120420181204' # 至少16字节
sm4 = Sm4()
input_data = b"2018120420181204"
print("明文",input_data)
sm4.sm4_set_key(key_data, ENCRYPT)
msg = sm4.sm4_crypt_ecb(input_data)
print("密文",msg)
sm4.sm4_set_key(key_data, DECRYPT)
m_b = sm4.sm4_crypt_ecb(msg)
print("解密后的明文",m_b)
利用gmssl库实现的sm2、3、4(由同组同学李长兴写的)
#! /usr/bin/python
# encoding : utf-8
import time
import base64
from gmssl import sm2,sm4
import codecs
SM2_PRIVATE_KEY = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
SM2_PUBLIC_KEY = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=SM2_PUBLIC_KEY, private_key=SM2_PRIVATE_KEY)
SM4_KEY = b'3l5butlj26hvv313'
SM4_IV = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # bytes类型
sm4_crypt = sm4.CryptSM4()
def base64_en(png_org,png_en):
with open(png_org, 'rb') as f:
image_base64 = str(base64.b64encode(f.read()), encoding='utf-8')
with open(png_en,'wt') as f_en:
f_en.write(image_base64)
def base64_de(png_en,png_de):
with open(png_en,"r") as f:
imgdata = base64.b64decode(f.read())
with open(png_de,'wb+') as f_de:
f_de.write(imgdata)
def sm2_en(png_org,png_en):
with open(png_org, 'rb') as f:
image_base64 = base64.b64encode(f.read())
image_sm2 = str(sm2_crypt.encrypt(image_base64))
with open(png_en,'wt') as f_en:
f_en.write(image_sm2)
def sm2_de(png_en,png_de):
with open(png_en,"r") as f:
image_sm2 = codecs.escape_decode(bytes(f.read()[2:-1],encoding="utf-8"), "hex-escape")[0]
image_base64 = sm2_crypt.decrypt(image_sm2)
imgdata = base64.b64decode(image_base64)
with open(png_de,'wb') as f_de:
f_de.write(imgdata)
def sm4_en(png_org,png_en):
sm4_crypt.set_key(SM4_KEY, sm4.SM4_ENCRYPT)
time_start = time.time()
print(time_start)
with open(png_org, 'rb') as f:
image_base64 = base64.b64encode(f.read())
time_1 = time.time()
print(time_1-time_start)
image_sm4 = str(sm4_crypt.crypt_ecb(image_base64))
time_2 = time.time()
print(time_2 - time_1)
with open(png_en,'wt') as f_en:
f_en.write(image_sm4)
def sm4_de(png_en,png_de):
sm4_crypt.set_key(SM4_KEY, sm4.SM4_DECRYPT)
with open(png_en,"r") as f:
image_sm4 = codecs.escape_decode(bytes(f.read()[2:-1],encoding="utf-8"), "hex-escape")[0]
image_base64 = sm4_crypt.crypt_ecb(image_sm4)
imgdata = base64.b64decode(image_base64)
with open(png_de,'wb') as f_de:
f_de.write(imgdata)
if __name__ == '__main__':
#f_org ='bas.txt'
#f_en ='basen.txt'
#f_de ='basde.txt'
# f_org = '1.png'
# f_en = '12321.txt'
# f_de = '123321.png'
# pngfile_base64_en(p_org,p_en)
# pngfile_base64_de(p_en,p_de)
f_org = 'abc.pdf'
f_en = '2.txt'
f_de = 'asdf+z44.pdf'
#sm2_en(f_org,f_en)
#sm2_de(f_en,f_de)
time_start = time.time()
sm4_en(f_org,f_en)
time_mid = time.time()
print(time_mid-time_start)
sm4_de(f_en,f_de)
time_end = time.time()
print(time_end-time_mid)
2.密钥管理
采用了较为安全的secerts库
生成公私钥算法如下:
alphabet = string.ascii_letters + string.digits + string.punctuation
while True:
password = ''.join(secrets.choice(alphabet) for i in range(16))
if (any(c.islower() for c in password)
and any(c.isupper() for c in password)
and sum(c in string.punctuation for c in password) <= 2
and sum(c.isdigit() for c in password) >= 3):
break
print(password)
SM2_PRIVATE_KEY = secrets.token_hex(66)
SM2_PUBLIC_KEY = secrets.token_hex(128)
# SM2_PRIVATE_KEY = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
# SM2_PUBLIC_KEY = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=SM2_PUBLIC_KEY, private_key=SM2_PRIVATE_KEY)
前一部分是生成一个长度为16的安全密钥,包括大小写字母、数字、特殊字符
下面是生成SM2公私钥对的代码,注释部分为生成后的结果样例
3.信息资产的保护
公文一经上传就是加密的状态,可以查看公文的口令保存经过SM2的加密保护存储在数据库中,用户的密码通过加盐哈希的算法保存在数据库中
用户密码:
公文口令:
公文加密:
四、安全性测试
抓包结果
https启动
已经有https加密
SQL注入测试:
' or 1=1#
注入失败
XSS测试:
<h1>1111</h1>
攻击失败
五、运行注意事项
需要安装的python库
pip3 install django
pip3 install filetype
pip3 install django-sslserver
pip3 install gmssl
pip3 install secrets
pip3 install PyPDF2
https启动方法:
在setting.py中加入sslserver
python manage.py runsslserver --certificate D:\ProgramData\Anaconda3\Lib\site-packages\sslserver\certs\development.crt --key D:\ProgramData\Anaconda3\Lib\site-packages\sslserver\certs\development.key
六、分工
代码框架部分是借鉴的,文件上传下载,用户组,数据库,安全性等基本上都是自主设计的,代码中的一些问题是小组共同讨论研究出来的
七、数据库设计
概念模型:
八、实验感想
经过本次实验,我基本熟悉了web开发的流程,可以独自开发简单的web项目,基本上实现了数据的安全传输,可以通过国密算法保护用户数据的完整性、机密性、不可否认性,可以开发一个产品使其基本上又好用、又安全,了解了配置服务器的方法,申请域名的方法、申请SSL证书的方法。