小议wtforms的validator之URL格式校验

今天在flask项目中用到了wtforms中关于URL校验的类,我的forms中定义的url字段如下:

url=StringField(u'URL地址',validators=[DataRequired(message=u"URL不能为空"),URL(require_tld=True,message=u"URL格式不正确")])

本着开发自测的原则,想看看这个URL校验到底靠不靠谱,结果发现任何前缀都被视为了正常的URL,就连“hhhh://baidu.com”这货都能顺利通过校验。。

然后就去翻URL的源码,关键代码是这么写的:

def __init__(self, require_tld=True, message=None):
    regex = r'^[a-z]+://(?P<host>[^/:]+)(?P<port>:[0-9]+)?(?P<path>\/.*)?$'
    super(URL, self).__init__(regex, re.IGNORECASE, message)
    self.validate_hostname = HostnameValidation(
        require_tld=require_tld,
        allow_ip=True,
    )

瞬间亮瞎我的钛合金狗眼,这正则表达式醉了,真是可以匹配任何协议前缀啊!所以这个地方是存在安全漏洞的,假如我弄一个javascript开头的前缀,就可以远程执行javascript,进行XSS攻击啊。所以改了他的源码,regex正则改为如下:

regex = r'^(http|ftp|https)+://(?P<host>[^/:]+)(?P<port>:[0-9]+)?(?P<path>\/.*)?$'

但是修改后怎么也不生效,思来想去不知道什么原因,干脆继承URL,重写一个校验类,但发现这样也有问题,因为regex是在URL类的init方法中定义的,只要调用父类初始化函数,regex就又会恢复到原来的,所以,只能继承Regexp了,如下:

from wtforms.validators import  URL,Regexp,HostnameValidation
class NewURL(Regexp):
    def __init__(self,require_tld=True, message=None):
        regex = r'^(http|ftp|https)+://(?P<host>[^/:]+)(?P<port>:[0-9]+)?(?P<path>\/.*)?$'
        super(NewURL, self).__init__(regex, re.IGNORECASE, message)
        self.validate_hostname = HostnameValidation(
            require_tld=require_tld,
            allow_ip=True,
        )

    def __call__(self, form, field):
        message = self.message
        if message is None:
            message = field.gettext('Invalid URL.')

        match = super(NewURL, self).__call__(form, field, message)
        if not self.validate_hostname(match.group('host')):
            raise ValidationError(message)

然后,我们在form表单定义中,使用我们的NewURL校验就可以了:
url=StringField(u'URL地址',validators=[DataRequired(message=u"URL不能为空"),NewURL(message=u"URL格式不正确")])

标签: wtforms, validators

三剑客-一站式接口测试与管理平台:sanjianke.tech

转载声明:除非特别声明,本博客文章均属原创,转载请注明出处,谢谢!

PS:本博采用老薛主机托管,欢迎购买老薛主机并使用推荐码:yanyaozhen,初次购买享受25%折扣。