XSS过滤
XSS过滤封装用法
封装到app01/form.py文件中进行验证
from django.forms import Form,widgets,fields class ArticleForm(Form): title = fields.CharField(max_length=64) content = fields.CharField( widget=widgets.Textarea(attrs={'id':'i1'})) #此处为xss验证 def clean_content(self): old = self.cleaned_data['content'] from utils.xss import xss return xss(old)
CONTENT='' def create_article(request,site): from app01.form import ArticleForm if request.method == 'GET': obj = ArticleForm() return render(request,'creat_article.html',{'site':site}) else: obj = ArticleForm(request.POST) if obj.is_valid(): content = obj.cleaned_data['content'] global CONTENT #这里记得要设置全局变量 CONTENT = content return HttpResponse('上传成功') # 查看文章内容,只是简单的 def see(request): return render(request,'see.html',{'CONTENT':CONTENT})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>$Title$</title> </head> <body> <h3>发布文章</h3> <form action="/background/{{ site }}/create_article.html" method="POST"> {% csrf_token %} 文章标题<input type="text"> <textarea name="content" id="i1" cols="30" rows="10"></textarea> <input type="submit" value="提交" style="margin-left: 35%"> </form> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> // KindEditor 上传的瞬间,帮你生成iframe+form进行伪Ajax操作 KindEditor.create('#i1',{ width:'1000px', height:'500px', resizeType:2, // 默认是否可以拖动改变高度和宽带,0,1,2,其中默认是2,可以拖动改变宽度和高度。 uploadJson:'/upload_img.html', // 上传文件位置,注意不能写目录/static/files...类似这种,识别不了。要写url // 注意:上传文件时,是以POST请求提交的,但是要写上{% csrf_token %},上面表单中写的上传文件时没法用到,要配置 extraFileUploadParams:{'csrfmiddlewaretoken':"{{ csrf_token }}"} }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>$Title$</title> </head> <body> {{ CONTENT|safe }} </body> </html>
from bs4 import BeautifulSoup def xss(old): soup = BeautifulSoup(old,'html.parser') tags = soup.find_all() vaild_tag = {'p':['class','id'],'img':['src'],'div':['class']} for tag in tags: if tag.name not in vaild_tag: tag.decompose() if tag.attrs: for k in list(tag.attrs.keys()): if k not in vaild_tag[tag.name]: del tag.attrs[k] comment_str=soup.decode() return comment_str
url(r'^background/(\w+)/create_article.html$', views.create_article), url(r'^see.html', views.see),
下面是xss.py内容的注释
# 通过beautifulsoup4 模块可以避免写正则表达式来完成过滤上传文件中的恶意攻击 from bs4 import BeautifulSoup content = """ <p id='i1' a='123' b='999'> <script>alert(123)</script> </p> <p id='i2'> <div> <p>asdfasdf</p> </div> <img id='i3' src="/static/imgs\1.jpg" alt="" /> </p> """ soup = BeautifulSoup(content,'html.parser') # parser为beautifulsoup4 模块的内置解析块,将html解析成对象 tag = soup.find(name='img') # 获取的是img标签,name= 标签名 # print(tag) #<img alt="" id="i3" src="/static/imgs.jpg"/> ,为字符串?? tag = soup.find(name='p') #获取的是 p 标签及 p 标签内的子内容 # print(tag) #<p a="123" b="999" id="i1"><script>alert(123)</script></p> v = soup.find(name='p',attrs={'id':'i2','name':''}) # 也可以通过id 和 name 获取,条件是且的关系 # print(v) #<p id='i2'><div><p>asdfasdf</p></div><img id='i3' src="/static/imgs\1.jpg" alt="" /></p> # 以上find 获取的都是选中的内容和其内部包含的子内容,且获取的是字符串类型 #find_all 获取的是列表类型,列表里是对象 v = soup.find_all(name='p') # print(v) #递归找到所有的标签,步骤:找第一个父类,父类和子类中符合的取出来,然后再把子类符合的标签取出来。子类符合的标签可能被取出来很多次 #[<p a="123" b="999" id="i1"><script>alert(123)</script></p>, <p id="i2"><div><p>asdfasdf</p></div><img alt="" id="i3" src="/static/imgs.jpg"/></p>, <p>asdfasdf</p>] #以下为过滤部分 vaild_tag = ['p','img','div'] #白名单,设置白名单不设黑名单的原因是xss攻击的方式多种多样,而且不断更新 tags = soup.find_all() for tag in tags: if tag.name not in vaild_tag: # tag.clear() # tag.clear()只是把tag标签的内容删掉,标签本身不被删掉 tag.decompose() #删除的是标签内的内容和标签本身 print(soup) #soup是对象,要拿到过滤后的结果,需要decode下 comment_str=soup.decode() #comment_str 这是拿到最终被过滤后的结果 #白名单也可以设置为字典格式,标签部分属性设置为白名单 vaild_tag = {'p':['class','id'],'img':['src'],'div':['class']} for tag in tags: if tag.name not in vaild_tag: tag.decompose() if tag.attrs: for k in list(tag.attrs.keys()): if k not in vaild_tag[tag.name]: del tag.attrs[k] comment_str=soup.decode() #将soup对象转换成字符串,encode()将soup对象转换成字节