总体设计
后端设计如下:
URL | 视图 | 模板 | 说明 |
/index/ | ScanWebShell.views.index | user/index.html | 主页与项目信息 |
/user/login/ | user.views.login | user/login.html | 登录 |
/user/register/ | user.views.register | user/register.html | 注册 |
/user/logout/ | user.views.logout | 无需专门的页面 | 登出 |
/job/upload | job.views.upload_file | job/upload.html | 上传文件 |
/job/count | job.views.countResult | job/count.html | 显示所有任务信息 |
/job/search/?task_id | job.views.searchResult | job/search.html | 根据 task_id 返回任务的详细信息 |
/job/scan?file | job.views.scan_file | job/scan.html | 扫描文件 |
index 项目信息
user 与用户相关的处理
login 登录
register 登录
forget 忘记密码
logut 登出
job 任务相关处理
upload 上传 webshell 文件
scan 对上传的文件进行扫描
count 统计当前用户所有的扫描任务与已上传的文件
search 具体描述某一任务,重点为改任务的结果
其中,仅 index 允许游客访问。
环境为开发环境,上传的是生产环境
User 应用设计
后端设计
参考的项目
User 其 MVT 中的 Module 和 view 部分,参考于基于 Django2.2 可重用登录与注册系统
模型如下:
classUser(models.Model):"""
用户模型
"""name=models.CharField(max_length=128,unique=True)password=models.CharField(max_length=256)email=models.EmailField(unique=True)c_time=models.DateTimeField(auto_now_add=True)has_confirmed=models.BooleanField(default=False)def__str__(self):returnself.nameclassMeta:ordering=["-c_time"]verbose_name="用户"verbose_name_plural="用户"classConfirmString(models.Model):"""
邮箱确认模型
"""code=models.CharField(max_length=256)user=models.OneToOneField('User',on_delete=models.CASCADE)c_time=models.DateTimeField(auto_now_add=True)def__str__(self):returnself.user.name+": "+self.codeclassMeta:ordering=["-c_time"]verbose_name="确认码"verbose_name_plural="确认码"
其他功能
之后的 View 部分是在基于 Django2.2 可重用登录与注册系统的基础上,补充部分功能:
忘记密码
重置密码
django simple captcha refresh
忘记密码
其中重置密码没有独立出来,是属于忘记密码的一部分
相关模型如下:
classConfirmString(models.Model):"""
邮箱确认模型
"""code=models.CharField(max_length=256)user=models.OneToOneField('User',on_delete=models.CASCADE)c_time=models.DateTimeField(auto_now_add=True)def__str__(self):returnself.user.name+": "+self.codeclassMeta:ordering=["-c_time"]verbose_name="确认码"verbose_name_plural="确认码"
应用逻辑如下:
用户在 user/forget/index 的表单中,添加需要重置密码的用户邮箱
若无改用户,则弹出无该用户的警告
有该邮箱,则往用户邮箱发送重置密码的链接,此时
重置密码的链接大致为 user/forget/confirm/?code=*
当 code 是在 ConfirmString 实例中时,将 user 的 has_confirmed,使其在重置密码期间无法登录,之后携带 code 转到 user/forget/change/?code=*
若数据库中没有该 code,则拒绝
user/forget/change/?code=* 中,根据 code 查询一对一匹配的 user,再根据添加的表单修改密码,之后 confirm.user.save() 和 confirm.delete()
django simple captcha refresh
在原项目基础上,需要修改 Template 和 urls.py
urls.py
captcha.views 内置就有刷新验证码的方法
fromcaptcha.viewsimportcaptcha_refresh# 验证码刷新功能,captcha_refresh为captcha.views内置方法,不需要我们单独写urlpatterns=[...path('refresh/',captcha_refresh),# 点击可以刷新验证码]
Template
{#刷新验证码的脚本,放到body部分的最后面即可#}
<script>$('.captcha').click(function(){$.getJSON('/captcha/refresh/',function(result){$('.captcha').attr('src',result['image_url']);$('#id_captcha_0').val(result['key']);});});</script>
前端设计
前端设计上是基本参考于 bootstrapdoc 5.0 example.
index.html
login.html
Job 应用设计
后端设计
Job 的应用设计上,个人在设计时,分为一些几个功能:
Upload 上传 WebShell 文件
Count 统计当前用户的上传文件和扫描任务
Scan 根据 file 文件创建扫描任务
Search 根据 task_id 查询扫描任务结果
在四个任务中,upload、count、search 设计相对简单,网上参考也相对较多,这里只是简单介绍。而 scan 中的设计相对麻烦,本质上是利用 celery 来处理扫描任务。
upload
在 models 中设计相关模型,且添加装饰器,用于在 admin 可以方便地同时删除文件对象和磁盘中的文件。
models.py
classModelWithFileField(models.Model):tmp_file=models.FileField(upload_to='./FileUpload/')# 上传目录为 FileUploadfile_user=models.ForeignKey(User,on_delete=models.CASCADE,null=True)'''
值得注意的一点是,FileUpload中已经存在相同文件名的文件时,会对上传文件的文件名重命名
如 1.png 转为 1_fIZVhN3.png
且存储的文件为 1_fIZVhN3.png
'''c_time=models.DateTimeField(auto_now_add=True)def__str__(self):returnself.tmp_file.nameclassMeta:ordering=["-c_time"]verbose_name="文件"verbose_name_plural="文件"# 添加装饰器@receiver(post_delete,sender=ModelWithFileField)defdelete_upload_files(sender,instance,**kwargs):files=getattr(instance,'tmp_file')ifnotfiles:returnfname=os.path.join(settings.MEDIA_ROOT,str(files))ifos.path.isfile(fname):os.remove(fname)
views.py
defupload_file(request):"""
上传文件
:param request:
:return:
"""ifnotrequest.session.get('is_login',None):# 不允许重复登录returnredirect('/user/index/')ifrequest.method=='POST':form=UploadFileForm(request.POST,request.FILES)ifform.is_valid():user_id=request.session.get('user_id')ifuser_id:tmp_user=models.User.objects.get(id=user_id)instance=ModelWithFileField(tmp_file=request.FILES['file'],file_user=tmp_user)instance.save()message="上传成功!\n存储的文件名为:\n"+instance.tmp_file.namereturnrender(request,'job/upload.html',{'message_success':message})else:returnrender(request,'job/upload.html',{'message_warning':"上传失败"})else:form=UploadFileForm()returnrender(request,'job/upload.html',{'form':form})
scan
scan 设计思路如下:
利用 celery 和 redis,作为任务调度模块
当 scan 成功访问,file 文件存在和无相关任务时,后台分别创建 ScanTaskField 实例和启动 celery 中的 scanTask.delay(file_name=file_name)
当 celery 中任务完成,自动更新 ScanTaskField 实例(同样需要添加装饰器)
参考文档 docs.celeryproject.org/en/v5.0.5/django/first-steps-with-django
前端设计
前端设计上同样是基本参考于 bootstrapdoc 5.0 example.
job/count
job/upload
应用配置
邮箱功能需要在 settings.py 中配置如下参数:
在 celery 中设置 worker 为 redis,需要
`docker run --name=redis -d -p 6379:6379 redis`
celery 启动
celery -A ScanWebShell worker -l info
完整代码:https://download.csdn.net/download/pythonyanyan/87390586