宝塔nginx免费防火墙 8.3,Python3.12 报 cgi 模块缺失修复

今天升级宝塔面板 并把Nginx 升到1.31之后,打开免费防火墙安全日志直接报错

然而这个nginx免费防火墙 8.3没有新版本。

原因是 Python3.12 已经彻底移除 cgi 模块,nginx免费防火墙 8.3还在调用 cgi 做页面转义。简单把 cgi 替换成 html 库后,会遇到磁盘上有攻击日志,但面板页面看不到任何记录。这里的原因html.escape 默认会把 " 转成 ",导致 json.loads 失败,正确的修改方式,get_safe_logs 函数, 解析日志 json 文本不处理字段转义html.escape(line, quote=False)。

# coding: utf-8
import sys, os

#if sys.version_info[0] == 2:
    #reload(sys)
    #sys.setdefaultencoding('utf-8')
os.chdir('/www/server/panel')
import json, time, public, string, re, hashlib

if __name__ != '__main__':
    from panelAuth import panelAuth


class free_waf_main:
    __path = '/www/server/free_waf/'
    __state = {True: '开启', False: '关闭', 0: '停用', 1: '启用'}
    __config = None
    __PATH = '/www/server/panel/plugin/free_waf/'

    def return_site(self, get):
        data = public.M('sites').field('name,path').select()
        ret = {}
        for i in data:
            ret[i['name']] = i['path']
        return public.returnMsg(True, ret)

    def is_ip_zhuanhuang(self,ip,ip2=False,ip_duan=False):
        try:
            ret = []
            if ip_duan:
                ip_ddd=int(ip.split('/')[1])
                ip = ip.split('/')[0].split('.')
                if ip_ddd>=32:return False
                if ip_ddd>=24:ip[-1]="0"
                if ip_ddd>=16 and ip_ddd<24:
                    ip[-1]="0"
                    ip[-2]="0"
                if ip_ddd>=8 and ip_ddd<16:
                    ip[-1]="0"
                    ip[-2]="0"
                    ip[-3]="0"
                from IPy import IP
                ip2 = IP("{}/{}".format('.'.join(ip),ip_ddd))
                return self.is_ip_zhuanhuang(str(ip2[0]),str(ip2[-1]))
            else:
                if not ip2:
                    ip=ip.split('.')
                    ret2=[]
                    for i in ip:
                        ret2.append(int(i))
                    ret.append(ret2)
                    ret.append(ret2)
                    return ret
                if ip2:
                    ip = ip.split('.')
                    ret2 = []
                    for i in ip:
                        ret2.append(int(i))
                    ret.append(ret2)
                    ip2 = ip2.split('.')
                    ret2 = []
                    for i in ip2:
                        ret2.append(int(i))
                    ret.append(ret2)
                    return ret
        except:return False


    def import_data(self, get):
        name = get.s_Name;
        if name=='ip_white' or name=='ip_black':
            padata =get.pdata.strip().split()
            if not padata:return public.returnMsg(False, '数据格式不正确')
            iplist = self.__get_rule(name)
            for i in padata:
                if re.search("\d+.\d+.\d+.\d+-\d+.\d+.\d+.\d+$",i):
                    ip=i.split('-')
                    ips = self.is_ip_zhuanhuang(ip[0],ip[1])
                    if not ips: continue
                    if ips in iplist: continue
                    iplist.insert(0, ips)
                if re.search("\d+.\d+.\d+.\d+/\d+$",i):
                    ips = self.is_ip_zhuanhuang(i,ip_duan=True)
                    if not ips: continue
                    if ips in iplist: continue
                    iplist.insert(0, ips)
                if re.search("\d+.\d+.\d+.\d+$",i):
                    ips=self.is_ip_zhuanhuang(i)
                    if not ips:continue
                    if ips in iplist: continue
                    iplist.insert(0, ips)
            self.__write_rule(name, iplist)
            return public.returnMsg(True, '导入成功!')
        else:
            try:
                pdata = json.loads(get.pdata)
            except:
                return public.returnMsg(False, '数据格式不正确');
            if not pdata: return public.returnMsg(False, '数据格式不正确');
            iplist = self.__get_rule(name);
            for ips in pdata:
                if ips in iplist: continue;
                iplist.insert(0, ips)
            self.__write_rule(name, iplist)
            return public.returnMsg(True, '导入成功!')

    # 获取规则
    def shell_get_rule(self, get):
        ret = []
        if os.path.exists(self.__PATH + 'rule.json'):
            try:
                data = json.loads(public.ReadFile(self.__PATH + 'rule.json'))
                return data
            except:
                return False
        else:
            return False

    # 查询站点跟目录
    def getdir(self, dir, pc='', lis=[]):
        list = os.listdir(dir)
        for l in list:
            try:
                if os.path.isdir(dir + '/' + l):
                    lis = self.getdir(dir + '/' + l, pc, lis)
                elif str(l.lower())[-4:] == '.php' and str(dir + '/' + l).find(pc) == -1:
                    print(dir + '/' + l)
                    lis.append(dir + '/' + l)
            except:continue
        return lis

    # 目录
    def getdir_list(self, get):
        path = get.path
        if os.path.exists(path):
            pc = 'hackcnm'
            rs = self.getdir(path, pc)
            return rs
        else:
            return False

    def scan(self, filelist, rule):
        self.__webshell_data = []
        import time
        time_data = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        ret = []
        for file in filelist:
            try:
                data = open(file).read()
            except:
                pass
            for r in rule:
                if re.compile(r).findall(data):
                    result = {}
                    result[file] = r
                    self.__webshell_data.append(result)
                    self.send_baota2(file)
                    data = ("%s [!] %s %s  \n" % (time_data, file, r))
                    self.insert_log(data)
        return self.__webshell_data

    def insert_log(self, data):
        public.writeFile(self.__PATH + 'webshell.log', data, 'a+')

    def get_log(self, get):
        path = self.__PATH + 'webshell.log'
        if not os.path.exists(path): return False
        return public.GetNumLines(path, 3000)

    def path_json(self, path, data):
        path_file = str(path).replace('/', '')
        if os.path.exists(path):
            if os.path.exists(self.__PATH + path_file + '.json'):
                try:
                    path_data = json.loads(public.ReadFile(self.__PATH + path_file + '.json'))
                except:
                    ret = []
                    public.WriteFile(self.__PATH + path_file + '.json', json.dumps(ret))
                    path_data = []
                if not path_data: return self.__webshell_data
                for i in self.__webshell_data:
                    for i2 in path_data:
                        if i2 in i:
                            self.__webshell_data.remove(i)
                return self.__webshell_data
            else:
                ret = []
                public.WriteFile(self.__PATH + path_file + '.json', json.dumps(ret))
                path_data = []
                return self.__webshell_data

    def san_dir(self, get):
        file = self.getdir_list(get)
        if not file: return public.returnMsg(False, "目录不存在")
        rule = self.shell_get_rule(get)
        if not rule: return public.returnMsg(False, "规则为空或者规则文件错误")
        self.__webshell_data = []
        result = self.scan(file, rule)
        result = self.path_json(get.path, result)
        return self.__webshell_data

    def xssencode(self, text):
        import html
        list = ['`', '~', '&', '<', '>']
        ret = []
        for i in text:
            if i in list:
                i = ''
            ret.append(i)
        str_convert = ''.join(ret)
        text2 = html.escape(str_convert, quote=True)
        return text2

    def shell_add_rule(self, get):
        rule = self.xssencode(get.rule)
        ret = []
        if os.path.exists(self.__PATH + 'rule.json'):
            try:
                data = json.loads(public.ReadFile(self.__PATH + 'rule.json'))
                if rule in data:
                    return public.returnMsg(False, '已经存在此规则')
                else:
                    data.append(rule)
                    public.WriteFile(self.__PATH + 'rule.json', json.dumps(data))
                    return public.returnMsg(True, '添加成功')
            except:
                return public.returnMsg(False, '规则库解析错误')
        else:
            return public.returnMsg(False, '规则库文件不存在')

    def shell_del_rule(self, get):
        rule = get.rule
        if os.path.exists(self.__PATH + 'rule.json'):
            try:
                data = json.loads(public.ReadFile(self.__PATH + 'rule.json'))
                if rule in data:
                    data.remove(rule)
                    public.WriteFile(self.__PATH + 'rule.json', json.dumps(data))
                    return public.returnMsg(True, '删除成功')
                else:
                    return public.returnMsg(False, '规则库不存在此规则')
            except:
                return public.returnMsg(False, '规则库解析错误')
        else:
            return public.returnMsg(False, '规则库文件不存在')

    def lock_not_webshell(self, get):
        path = get.path
        not_path = get.not_path
        if not os.path.exists(not_path): return public.returnMsg(False, '文件不存在')
        path_file = str(path).replace('/', '')
        if not os.path.exists(self.__PATH + path_file + '.json'):
            ret = []
            ret.append(not_path)
            public.WriteFile(self.__PATH + path_file + '.json', json.dumps(ret))
        else:
            try:
                path_data = json.loads(public.ReadFile(self.__PATH + path_file + '.json'))
                if not not_path in path_data:
                    path_data.append(not_path)
                    public.WriteFile(self.__PATH + path_file + '.json', json.dumps(path_data))
                    return public.returnMsg(True, '添加成功')
                else:
                    return public.returnMsg(False, '已经存在')
            except:
                ret = []
                ret.append(not_path)
                public.WriteFile(self.__PATH + path_file + '.json', json.dumps(ret))
                return public.returnMsg(True, '11111111')

    def upload_file_url(self, get):
        try:
            if os.path.exists(get.filename):
                data = public.ExecShell(
                    '/usr/local/curl/bin/curl https://scanner.baidu.com/enqueue -F archive=@%s' % get.filename)
                data = json.loads(data[0])
                time.sleep(3)
                import requests
                default_headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
                }
                data_list = requests.get(url=data['url'], headers=default_headers, verify=False)
                data2 = data_list.json()
                if 'data' in data2[0]:
                    if len(data2[0]['data']) >= 1:
                        if 'descr' in data2[0]['data'][0]:
                            if 'WebShell' in data2[0]['data'][0]['descr']:
                                return public.returnMsg(True, '此文件为webshell')
                return public.returnMsg(True, '未查出风险,需等待一段时间后查询')
            else:
                return public.returnMsg(False, '文件不存在')
        except:
            return public.returnMsg(True, '未查出风险,需等待一段时间后查询')

    def read_file_md5(self, filename):
        if os.path.exists(filename):
            with open(filename, 'rb') as fp:
                data = fp.read()
            file_md5 = hashlib.md5(data).hexdigest()
            return file_md5
        else:
            return False

    def send_baota2(self, filename):
        cloudUrl = 'http://www.bt.cn/api/panel/btwaf_submit'
        pdata = {'codetxt': public.ReadFile(filename), 'md5': self.read_file_md5(filename), 'type': '0',
                 'host_ip': public.GetLocalIp(), 'size': os.path.getsize(filename)}
        ret = public.httpPost(cloudUrl, pdata)
        return True

    def send_baota(self, get):
        if 'filename' not in get: return public.returnMsg(False, '请选择你需要上传的文件')
        if not os.path.exists(get.filename): return public.returnMsg(False, '文件不存在')
        cloudUrl = 'http://www.bt.cn/api/panel/btwaf_submit'
        pdata = {'codetxt': public.ReadFile(get.filename), 'md5': self.read_file_md5(get.filename), 'type': '0',
                 'host_ip': public.GetLocalIp(), 'size': os.path.getsize(get.filename)}
        ret = public.httpPost(cloudUrl, pdata)
        if ret == '1':
            return self.check_webshell(get)
        elif ret == '-1':
            return self.check_webshell(get)
        else:
            return public.returnMsg(False, '系统错误')

    def check_webshell(self, get):
        if 'filename' not in get: return public.returnMsg(False, '请选择你需要上传的文件')
        if not os.path.exists(get.filename): return public.returnMsg(False, '文件不存在')
        cloudUrl = 'http://www.bt.cn/api/panel/btwaf_check_file'
        pdata = {'md5': self.read_file_md5(get.filename), 'size': os.path.getsize(get.filename)}
        ret = public.httpPost(cloudUrl, pdata)
        if ret == '0':
            return public.returnMsg(True, '未查出风险,需等待一段时间后查询')
        elif ret == '1':
            return public.returnMsg(True, '该文件经过系统检测为webshell!!!!')
        elif ret == '-1':
            return public.returnMsg(True, '未查询到该文件,请上传检测')
        else:
            return public.returnMsg(False, '系统错误')

    def __get_md5(self, s):
        m = hashlib.md5()
        m.update(s.encode('utf-8'))
        return m.hexdigest()

    def get_config(self, get):
        config = json.loads(public.readFile(self.__path + 'config.json'));

        if not 'from_data' in config:
            config['from_data'] = True

        if not 'retry_cycle' in config:
            config['retry_cycle'] = 60;
            self.__write_config(config);
        if config['start_time'] == 0:
            config['start_time'] = time.time();
            self.__write_config(config);
        return config

    def get_site_config(self, get):
        if not os.path.exists('/www/wwwlogs/free_waf_log'): os.mkdir('/www/wwwlogs/free_waf_log')
        site_config = public.readFile(self.__path + 'site.json')
        data = self.__check_site(json.loads(site_config))
        if get:
            total_all = self.get_total(None)['sites']
            site_list = []
            for k in data.keys():
                if not k in total_all: total_all[k] = {}
                data[k]['total'] = self.__format_total(total_all[k])
                siteInfo = data[k];
                siteInfo['siteName'] = k;
                site_list.append(siteInfo);
            data = sorted(site_list, key=lambda x: x['log_size'], reverse=True)
        return data

    def get_site_config_byname(self, get):
        site_config = self.get_site_config(None);
        config = site_config[get.siteName]
        config['top'] = self.get_config(None)
        return config

    def set_open(self, get):
        config = self.get_config(None)
        if config['open']:
            config['open'] = False
            config['start_time'] = 0
        else:
            config['open'] = True
            config['start_time'] = int(time.time())
        self.__write_log(self.__state[config['open']] + '网站防火墙(WAF)');
        self.__write_config(config)
        return public.returnMsg(True, '设置成功!');

    def set_obj_open(self, get):
        config = self.get_config(None)
        if type(config[get.obj]) != bool:
            if config[get.obj]['open']:
                config[get.obj]['open'] = False
            else:
                config[get.obj]['open'] = True
            self.__write_log(self.__state[config[get.obj]['open']] + '【' + get.obj + '】功能');
        else:
            if config[get.obj]:
                config[get.obj] = False
            else:
                config[get.obj] = True
            self.__write_log(self.__state[config[get.obj]] + '【' + get.obj + '】功能');

        self.__write_config(config)
        return public.returnMsg(True, '设置成功!');

    def set_site_obj_open(self, get):
        site_config = self.get_site_config(None)
        if type(site_config[get.siteName][get.obj]) != bool:
            if site_config[get.siteName][get.obj]['open']:
                site_config[get.siteName][get.obj]['open'] = False
            else:
                site_config[get.siteName][get.obj]['open'] = True
            self.__write_log(self.__state[site_config[get.siteName][get.obj][
                'open']] + '网站【' + get.siteName + '】【' + get.obj + '】功能');
        else:
            if site_config[get.siteName][get.obj]:
                site_config[get.siteName][get.obj] = False
            else:
                site_config[get.siteName][get.obj] = True
            self.__write_log(
                self.__state[site_config[get.siteName][get.obj]] + '网站【' + get.siteName + '】【' + get.obj + '】功能');

        if get.obj == 'drop_abroad': self.__auto_sync_cnlist();
        self.__write_site_config(site_config)
        return public.returnMsg(True, '设置成功!');

    def set_obj_status(self, get):
        config = self.get_config(None)
        config[get.obj]['status'] = int(get.statusCode)
        self.__write_config(config)
        return public.returnMsg(True, '设置成功!');

    def set_cc_conf(self, get):
        config = self.get_config(None)
        config['cc']['cycle'] = int(get.cycle)
        config['cc']['limit'] = int(get.limit)
        config['cc']['endtime'] = int(get.endtime)
        config['cc']['increase'] = (get.increase == '1') | False
        self.__write_config(config)
        self.__write_log(
            '设置全局CC配置为:' + get.cycle + ' 秒内累计请求超过 ' + get.limit + ' 次后,封锁 ' + get.endtime + ' 秒' + ',增强:' + get.increase);
        return public.returnMsg(True, '设置成功!');

    def set_site_cc_conf(self, get):
        site_config = self.get_site_config(None)
        site_config[get.siteName]['cc']['cycle'] = int(get.cycle)
        site_config[get.siteName]['cc']['limit'] = int(get.limit)
        site_config[get.siteName]['cc']['endtime'] = int(get.endtime)
        site_config[get.siteName]['cc']['increase'] = (get.increase == '1') | False
        self.__write_site_config(site_config)
        self.__write_log(
            '设置站点【' + get.siteName + '】CC配置为:' + get.cycle + ' 秒内累计请求超过 ' + get.limit + ' 次后,封锁 ' + get.endtime + ' 秒' + ',增强:' + get.increase);
        return public.returnMsg(True, '设置成功!');

    def add_cnip(self, get):
        ipn = [self.__format_ip(get.start_ip), self.__format_ip(get.end_ip)]
        if not ipn[0] or not ipn[1]: return public.returnMsg(False, 'IP段格式不正确');
        if not self.__is_ipn(ipn): return public.returnMsg(False, '起始IP不能大于结束IP');
        iplist = self.__get_rule('cn')
        if ipn in iplist: return public.returnMsg(False, '指定IP段已存在!');
        iplist.insert(0, ipn)
        self.__write_rule('cn', iplist)
        self.__write_log('添加IP段[' + get.start_ip + '-' + get.end_ip + ']到国内IP库');
        return public.returnMsg(True, '添加成功!');

    def remove_cnip(self, get):
        index = int(get.index)
        iplist = self.__get_rule('cn')
        ipn = iplist[index]
        del (iplist[index])
        self.__write_rule('cn', iplist)
        self.__write_log('从国内IP库删除[' + '.'.join(map(str, ipn[0])) + '-' + '.'.join(map(str, ipn[1])) + ']');
        return public.returnMsg(True, '删除成功!');

    def add_ip_white(self, get):
        ipn = [self.__format_ip(get.start_ip), self.__format_ip(get.end_ip)]
        if not ipn[0] or not ipn[1]: return public.returnMsg(False, 'IP段格式不正确');
        if not self.__is_ipn(ipn): return public.returnMsg(False, '起始IP不能大于结束IP');
        iplist = self.__get_rule('ip_white')
        if ipn in iplist: return public.returnMsg(False, '指定IP段已存在!');
        iplist.insert(0, ipn)
        self.__write_rule('ip_white', iplist)
        self.__write_log('添加IP段[' + get.start_ip + '-' + get.end_ip + ']到IP白名单');
        return public.returnMsg(True, '添加成功!');

    def remove_ip_white(self, get):
        index = int(get.index)
        iplist = self.__get_rule('ip_white')
        ipn = iplist[index]
        del (iplist[index])
        self.__write_rule('ip_white', iplist)
        self.__write_log('从IP白名单删除[' + '.'.join(map(str, ipn[0])) + '-' + '.'.join(map(str, ipn[1])) + ']');
        return public.returnMsg(True, '删除成功!');

    def add_ip_black(self, get):
        ipn = [self.__format_ip(get.start_ip), self.__format_ip(get.end_ip)]
        if not ipn[0] or not ipn[1]: return public.returnMsg(False, 'IP段格式不正确');
        if not self.__is_ipn(ipn): return public.returnMsg(False, '起始IP不能大于结束IP');
        iplist = self.__get_rule('ip_black')
        if ipn in iplist: return public.returnMsg(False, '指定IP段已存在!');
        iplist.insert(0, ipn)
        self.__write_rule('ip_black', iplist)
        self.__write_log('添加IP段[' + get.start_ip + '-' + get.end_ip + ']到IP黑名单');
        return public.returnMsg(True, '添加成功!');

    def remove_ip_black(self, get):
        index = int(get.index)
        iplist = self.__get_rule('ip_black')
        ipn = iplist[index]
        del (iplist[index])
        self.__write_rule('ip_black', iplist)
        self.__write_log('从IP黑名单删除[' + '.'.join(map(str, ipn[0])) + '-' + '.'.join(map(str, ipn[1])) + ']');
        return public.returnMsg(True, '删除成功!');

    def add_url_white(self, get):
        url_white = self.__get_rule('url_white')
        url_rule = get.url_rule.strip()
        if get.url_rule in url_white: return public.returnMsg(False, '您添加的URL已存在')
        url_white.insert(0, url_rule)
        self.__write_rule('url_white', url_white)
        self.__write_log('添加url规则[' + url_rule + ']到URL白名单');
        return public.returnMsg(True, '添加成功!');

    def remove_url_white(self, get):
        url_white = self.__get_rule('url_white')
        index = int(get.index)
        url_rule = url_white[index]
        del (url_white[index])
        self.__write_rule('url_white', url_white)
        self.__write_log('从URL白名单删除URL规则[' + url_rule + ']');
        return public.returnMsg(True, '删除成功!');

    def add_url_black(self, get):
        url_white = self.__get_rule('url_black')
        url_rule = get.url_rule.strip()
        if get.url_rule in url_white: return public.returnMsg(False, '您添加的URL已存在')
        url_white.insert(0, url_rule)
        self.__write_rule('url_black', url_white)
        self.__write_log('添加url规则[' + url_rule + ']到URL黑名单');
        return public.returnMsg(True, '添加成功!');

    def remove_url_black(self, get):
        url_white = self.__get_rule('url_black')
        index = int(get.index)
        url_rule = url_white[index]
        del (url_white[index])
        self.__write_rule('url_black', url_white)
        self.__write_log('从URL黑名单删除URL规则[' + url_rule + ']');
        return public.returnMsg(True, '删除成功!');

    def save_scan_rule(self, get):
        scan_rule = {'header': get.header, 'cookie': get.cookie, 'args': get.args}
        self.__write_rule('scan_black', scan_rule)
        self.__write_log('修改扫描器过滤规则');
        return public.returnMsg(True, '设置成功')

    def set_retry(self, get):
        config = self.get_config(None)
        config['retry'] = int(get.retry)
        config['retry_cycle'] = int(get.retry_cycle)
        config['retry_time'] = int(get.retry_time)
        self.__write_config(config)
        self.__write_log('设置非法请求容忍阈值: ' + get.retry_cycle + ' 秒内累计超过 ' + get.retry + ' 次, 封锁 ' + get.retry_time + ' 秒');
        return public.returnMsg(True, '设置成功!');

    def set_site_retry(self, get):
        site_config = self.get_site_config(None)
        site_config[get.siteName]['retry'] = int(get.retry)
        site_config[get.siteName]['retry_cycle'] = int(get.retry_cycle)
        site_config[get.siteName]['retry_time'] = int(get.retry_time)
        self.__write_site_config(site_config)
        self.__write_log(
            '设置网站【' + get.siteName + '】非法请求容忍阈值: ' + get.retry_cycle + ' 秒内累计超过 ' + get.retry + ' 次, 封锁 ' + get.retry_time + ' 秒');
        return public.returnMsg(True, '设置成功!');

    def set_site_cdn_state(self, get):
        site_config = self.get_site_config(None)
        if site_config[get.siteName]['cdn']:
            site_config[get.siteName]['cdn'] = False
        else:
            site_config[get.siteName]['cdn'] = True
        self.__write_site_config(site_config)
        self.__write_log(self.__state[site_config[get.siteName]['cdn']] + '站点【' + get.siteName + '】CDN模式');
        return public.returnMsg(True, '设置成功!');

    def get_site_cdn_header(self, get):
        site_config = self.get_site_config(None)
        return site_config[get.siteName]['cdn_header']

    def add_site_cdn_header(self, get):
        site_config = self.get_site_config(None)
        get.cdn_header = get.cdn_header.strip().lower();
        if get.cdn_header in site_config[get.siteName]['cdn_header']: return public.returnMsg(False, '您添加的请求头已存在!');
        site_config[get.siteName]['cdn_header'].append(get.cdn_header)
        self.__write_site_config(site_config)
        self.__write_log('添加站点【' + get.siteName + '】CDN-Header【' + get.cdn_header + '】');
        return public.returnMsg(True, '添加成功!');

    def remove_site_cdn_header(self, get):
        site_config = self.get_site_config(None)
        get.cdn_header = get.cdn_header.strip().lower();
        if not get.cdn_header in site_config[get.siteName]['cdn_header']: return public.returnMsg(False, '指定请求头不存在!');
        for i in range(len(site_config[get.siteName]['cdn_header'])):
            if get.cdn_header == site_config[get.siteName]['cdn_header'][i]:
                self.__write_log(
                    '删除站点【' + get.siteName + '】CDN-Header【' + site_config[get.siteName]['cdn_header'][i] + '】');
                del (site_config[get.siteName]['cdn_header'][i])
                break;
        self.__write_site_config(site_config)
        return public.returnMsg(True, '删除成功!');

    def get_site_rule(self, get):
        site_config = self.get_site_config(None)
        return site_config[get.siteName][get.ruleName]

    def add_site_rule(self, get):
        site_config = self.get_site_config(None)
        if not get.ruleName in site_config[get.siteName]: return public.returnMsg(False, '指定规则不存在!');
        mt = type(site_config[get.siteName][get.ruleName])
        if mt == bool: return public.returnMsg(False, '指定规则不存在!');
        if mt == str: site_config[get.siteName][get.ruleName] = get.ruleValue
        if mt == list:
            if get.ruleName == 'url_rule' or get.ruleName == 'url_tell':
                for ruleInfo in site_config[get.siteName][get.ruleName]:
                    if ruleInfo[0] == get.ruleUri: return public.returnMsg(False, '指定URI已存在!');
                tmp = []
                tmp.append(get.ruleUri)
                tmp.append(get.ruleValue)
                if get.ruleName == 'url_tell':
                    self.__write_log(
                        '添加站点【' + get.siteName + '】URI【' + get.ruleUri + '】保护规则,参数【' + get.ruleValue + '】,参数值【' + get.rulePass + '】');
                    tmp.append(get.rulePass)
                else:
                    self.__write_log('添加站点【' + get.siteName + '】URI【' + get.ruleUri + '】过滤规则【' + get.ruleValue + '】');
                site_config[get.siteName][get.ruleName].insert(0, tmp)
            else:
                if get.ruleValue in site_config[get.siteName][get.ruleName]: return public.returnMsg(False, '指定规则已存在!');
                site_config[get.siteName][get.ruleName].insert(0, get.ruleValue)
                self.__write_log('添加站点【' + get.siteName + '】【' + get.ruleName + '】过滤规则【' + get.ruleValue + '】');
        self.__write_site_config(site_config)
        return public.returnMsg(True, '添加成功!');

    def remove_site_rule(self, get):
        site_config = self.get_site_config(None)
        index = int(get.index)
        if not get.ruleName in site_config[get.siteName]: return public.returnMsg(False, '指定规则不存在!');
        site_rule = site_config[get.siteName][get.ruleName][index]
        del (site_config[get.siteName][get.ruleName][index])
        self.__write_site_config(site_config)
        self.__write_log('删除站点【' + get.siteName + '】【' + get.ruleName + '】过滤规则【' + json.dumps(site_rule) + '】');
        return public.returnMsg(True, '删除成功!');

    def get_rule(self, get):
        rule = self.__get_rule(get.ruleName)
        if not rule: return [];
        return rule

    def add_rule(self, get):
        rule = self.__get_rule(get.ruleName)
        ruleValue = [1, get.ruleValue.strip(), get.ps, 1]
        for ru in rule:
            if ru[1] == ruleValue[1]: return public.returnMsg(False, '指定规则已存在,请勿重复添加');
        rule.append(ruleValue)
        self.__write_rule(get.ruleName, rule)
        self.__write_log('添加全局规则【' + get.ruleName + '】【' + get.ps + '】');
        return public.returnMsg(True, '添加成功!');

    def remove_rule(self, get):
        rule = self.__get_rule(get.ruleName)
        index = int(get.index)
        ps = rule[index][2]
        del (rule[index])
        self.__write_rule(get.ruleName, rule)
        self.__write_log('删除全局规则【' + get.ruleName + '】【' + ps + '】');
        return public.returnMsg(True, '删除成功!');

    def modify_rule(self, get):
        rule = self.__get_rule(get.ruleName)
        index = int(get.index)
        rule[index][1] = get.ruleBody
        rule[index][2] = get.rulePs
        self.__write_rule(get.ruleName, rule)
        self.__write_log('修改全局规则【' + get.ruleName + '】【' + get.rulePs + '】');
        return public.returnMsg(True, '修改成功!');

    def set_rule_state(self, get):
        rule = self.__get_rule(get.ruleName)
        index = int(get.index)
        if rule[index][0] == 0:
            rule[index][0] = 1;
        else:
            rule[index][0] = 0;
        self.__write_rule(get.ruleName, rule)
        self.__write_log(self.__state[rule[index][0]] + '全局规则【' + get.ruleName + '】【' + rule[index][2] + '】');
        return public.returnMsg(True, '设置成功!');

    def get_site_disable_rule(self, get):
        rule = self.__get_rule(get.ruleName)
        site_config = self.get_site_config(None)
        site_rule = site_config[get.siteName]['disable_rule'][get.ruleName]
        for i in range(len(rule)):
            if rule[i][0] == 0: rule[i][0] = -1;
            if i in site_rule: rule[i][0] = 0;
        return rule;

    def set_site_disable_rule(self, get):
        site_config = self.get_site_config(None)
        index = int(get.index)
        if index in site_config[get.siteName]['disable_rule'][get.ruleName]:
            for i in range(len(site_config[get.siteName]['disable_rule'][get.ruleName])):
                if index == site_config[get.siteName]['disable_rule'][get.ruleName][i]:
                    del (site_config[get.siteName]['disable_rule'][get.ruleName][i])
                    break
        else:
            site_config[get.siteName]['disable_rule'][get.ruleName].append(index)
        self.__write_log('设置站点【' + get.siteName + '】应用规则【' + get.ruleName + '】状态');
        self.__write_site_config(site_config)
        return public.returnMsg(True, '设置成功!');

    def get_safe_logs(self, get):
        try:
            import html
            pythonV = sys.version_info[0]
            if 'drop_ip' in get:
                path = '/www/server/free_waf/drop_ip.log'
                num = 14
            else:
                path = '/www/wwwlogs/free_waf_log/' + get.siteName + '_' + get.toDate + '.log'
                num = 10
            if not os.path.exists(path): return []
            p = 1
            if 'p' in get:
                p = int(get.p)
            start_line = (p - 1) * num
            count = start_line + num
            fp = open(path, 'rb')
            buf = ""
            try:
                fp.seek(-1, 2)
            except:
                return []
            if fp.read(1) == "\n": fp.seek(-1, 2)
            data = []
            b = True
            n = 0
            c = 0
            while c < count:
                while True:
                    newline_pos = str.rfind(buf, "\n")
                    pos = fp.tell()
                    if newline_pos != -1:
                        if n >= start_line:
                            line = buf[newline_pos + 1:]
                            if line:
                                try:
                                    # quote=False:兼容原 cgi.escape 默认行为,避免 " 被转义后 json.loads 失败
                                    tmp_data = json.loads(html.escape(line, quote=False))
                                    for i in range(len(tmp_data)):
                                        if i == 7:
                                            tmp_data[i] = str(tmp_data[i]).replace('&amp;', '&').replace('&lt;',
                                                                                                         '<').replace(
                                                '&gt;', '>')
                                        else:
                                            tmp_data[i] = html.escape(str(tmp_data[i]), quote=True)
                                    data.append(tmp_data)
                                except:
                                    c -= 1
                                    n -= 1
                                    pass
                            else:
                                c -= 1
                                n -= 1
                        buf = buf[:newline_pos]
                        n += 1
                        c += 1
                        break
                    else:
                        if pos == 0:
                            b = False
                            break
                        to_read = min(4096, pos)
                        fp.seek(-to_read, 1)
                        t_buf = fp.read(to_read)
                        if pythonV == 3: t_buf = t_buf.decode('utf-8', errors="ignore")
                        buf = t_buf + buf
                        fp.seek(-to_read, 1)
                        if pos - to_read == 0:
                            buf = "\n" + buf
                if not b: break
            fp.close()
            if 'drop_ip' in get:
                drop_iplist = self.get_waf_drop_ip(None)
                stime = time.time()
                setss = []
                for i in range(len(data)):
                    if (float(stime) - float(data[i][0])) < float(data[i][4]) and not data[i][1] in setss:
                        setss.append(data[i][1])
                        data[i].append(data[i][1] in drop_iplist)
                    else:
                        data[i].append(False)
        except:
            return public.get_error_info()
            data = []
        return data

    def get_logs_list(self, get):
        path = '/www/wwwlogs/free_waf_log/'
        sfind = get.siteName + '_'
        data = []
        for fname in os.listdir(path):
            if fname.find(sfind) != 0: continue;
            tmp = fname.replace(sfind, '').replace('.log', '')
            data.append(tmp)
        return sorted(data, reverse=True);

    def get_waf_drop_ip(self, get):
        try:
            return json.loads(public.httpGet('http://127.0.0.1/get_btwaf_drop_ip'))
        except:
            return [];

    def remove_waf_drop_ip(self, get):
        try:
            data = json.loads(public.httpGet('http://127.0.0.1/remove_btwaf_drop_ip?ip=' + get.ip))
            self.__write_log('从防火墙解封IP【' + get.ip + '】');
            return data
        except:
            return public.returnMsg(False, '获取数据失败');

    def clean_waf_drop_ip(self, get):
        try:
            return json.loads(public.httpGet('http://127.0.0.1/clean_btwaf_drop_ip'))
            self.__write_log('从防火墙解封所有IP');
        except:
            return public.returnMsg(False, '获取数据失败');

    def get_gl_logs(self, get):
        import page
        page = page.Page();
        count = public.M('logs').where('type=?', (u'Nginx免费防火墙',)).count();
        limit = 12;
        info = {}
        info['count'] = count
        info['row'] = limit
        info['p'] = 1
        if hasattr(get, 'p'):
            info['p'] = int(get['p'])
        info['uri'] = get
        info['return_js'] = ''
        if hasattr(get, 'tojs'):
            info['return_js'] = get.tojs

        data = {}

        # 获取分页数据
        data['page'] = page.GetPage(info, '1,2,3,4,5,8');
        data['data'] = public.M('logs').where('type=?', (u'Nginx免费防火墙',)).order('id desc').limit(
            str(page.SHIFT) + ',' + str(page.ROW)).field('log,addtime').select();
        return data;

    def get_total(self, get):
        try:
            total = json.loads(public.readFile(self.__path + 'total.json'))
        except:
            total = {"rules": {"user_agent": 0, "cookie": 0, "post": 0, "args": 0, "url": 0, "cc": 0}, "sites": {},
                     "total": 0}
            self.__write_total(total);
        if type(total['rules']) != dict:
            new_rules = {}
            for rule in total['rules']:
                new_rules[rule['key']] = rule['value'];
            total['rules'] = new_rules;
            self.__write_total(total);
        total['rules'] = self.__format_total(total['rules'])
        return total;

    def __format_total(self, total):
        total['get'] = 0;
        if 'args' in total:
            total['get'] += total['args'];
            del (total['args'])
        if 'url' in total:
            total['get'] += total['url'];
            del (total['url'])
        cnkey = [
            ['post', u'POST渗透'],
            ['get', u'GET渗透'],
            ['cc', u"CC攻击"],
            ['user_agent', u'恶意User-Agent'],
            ['cookie', u'Cookie渗透'],
            ['scan', u'恶意扫描'],
            ['head', u'恶意HEAD请求'],
            ['url_rule', u'URI自定义拦截'],
            ['url_tell', u'URI保护'],
            ['disable_upload_ext', u'恶意文件上传'],
            ['disable_ext', u'禁止的扩展名'],
            ['disable_php_path', u'禁止PHP脚本']
        ]
        data = []
        for ck in cnkey:
            tmp = {}
            tmp['name'] = ck[1]
            tmp['key'] = ck[0]
            tmp['value'] = 0;
            if ck[0] in total: tmp['value'] = total[ck[0]]
            data.append(tmp)
        return data

    def test_check_zhilist(self, get):
        try:
            userInfo = json.loads(public.ReadFile('/www/server/panel/data/userInfo.json'))
            data22 = {"access_key": userInfo['access_key'], "uid": userInfo['uid']}
            url = 'http://www.bt.cn/api/bt_waf/getSpiders'
            data_list = json.loads(public.httpPost(url, data22, timeout=3))
            if data_list:
                for i22 in data_list:
                    try:
                        path = "/www/server/free_waf/%s.json" % i22
                        if os.path.exists(path):
                            ret = json.loads(public.ReadFile(path))
                            localhost_json = list(set(ret).union(data_list[i22]))
                            public.WriteFile(path, json.dumps(localhost_json))
                    except:
                        continue
        except:
            return []


    def get_total_all(self, get):
        self.__check_cjson();
        nginxconf = '/www/server/nginx/conf/nginx.conf';
        if not os.path.exists(nginxconf): return public.returnMsg(False, '只支持nginx服务器');
        #if public.readFile(nginxconf).find('luawaf.conf') == -1: return public.returnMsg(False,'当前nginx不支持防火墙,请重装nginx');
        data = {}
        data['total'] = self.get_total(None)
        del (data['total']['sites'])
        data['drop_ip'] = []
        data['open'] = self.get_config(None)['open']
        conf = self.get_config(None)
        data['safe_day'] = 0
        if 'start_time' in conf:
            if conf['start_time'] != 0: data['safe_day'] = int((time.time() - conf['start_time']) / 86400)
        self.__write_site_domains()
        session_id = self.__get_md5("free_waf_test"+time.strftime('%Y-%m-%d'))
        from BTPanel import session
        if not session_id in session:
            self.test_check_zhilist(None)
        zhizhu_list=['1','2','4','5','6']
        for i in zhizhu_list:
            try:
                if os.path.getsize('/www/server/free_waf/zhizhu'+i+'.json')>10:
                    f=open('/www/server/free_waf/zhizhu'+i+'.json','r')
                    tt=[]
                    for i2 in f:
                        i2=i2.strip()
                        tt.append(i2)
                    f.close()
                    userInfo = json.loads(public.ReadFile('/www/server/panel/data/userInfo.json'))
                    data22 = {"type": i, "ip_list": json.dumps(tt),"access_key": userInfo['access_key'], "uid": userInfo['uid']}
                    url='http://www.bt.cn/api/bt_waf/addSpider'
                    if len(tt)>=1:
                         public.httpPost(url, data22)
                    public.WriteFile('/www/server/free_waf/zhizhu'+i+'.json',"")
            except:continue
        return data

    def __write_site_domains(self):
        sites = public.M('sites').field('name,id').select();
        my_domains = []
        for my_site in sites:
            tmp = {}
            tmp['name'] = my_site['name']
            tmp_domains = public.M('domain').where('pid=?', (my_site['id'],)).field('name').select()
            tmp['domains'] = []
            for domain in tmp_domains:
                tmp['domains'].append(domain['name'])
            binding_domains = public.M('binding').where('pid=?', (my_site['id'],)).field('domain').select()
            for domain in binding_domains:
                tmp['domains'].append(domain['domain'])
            my_domains.append(tmp)
        public.writeFile(self.__path + '/domains.json', json.dumps(my_domains))
        return my_domains

    # 设置自动同步
    def __auto_sync_cnlist(self):
        return True

    def __get_rule(self, ruleName):
        path = self.__path + 'rule/' + ruleName + '.json';
        rules = public.readFile(path)
        if not rules: return False
        return json.loads(rules)

    def __write_rule(self, ruleName, rule):
        path = self.__path + 'rule/' + ruleName + '.json';
        public.writeFile(path, json.dumps(rule))
        public.serviceReload();

    def __check_site(self, site_config):
        sites = public.M('sites').field('name').select();
        siteNames = []
        n = 0
        for siteInfo in sites:
            siteNames.append(siteInfo['name'])
            if siteInfo['name'] in site_config: continue
            site_config[siteInfo['name']] = self.__get_site_conf()
            n += 1
        old_site_config = site_config.copy()
        for sn in site_config.keys():
            if sn in siteNames:
                if not 'cdn_baidu' in site_config[sn]:
                    site_config[sn]['cdn_baidu']=False
                    n += 1
                if not 'retry_cycle' in site_config[sn]:
                    site_config[sn]['retry_cycle'] = 60;
                    n += 1;
                continue
            del (old_site_config[sn])
            self.__remove_log_file(sn)
            n += 1

        if n > 0:
            site_config = old_site_config.copy()
            self.__write_site_config(site_config)

        config = self.get_config(None)
        logList = os.listdir(config['logs_path'])
        mday = time.strftime('%Y-%m-%d', time.localtime());
        for sn in siteNames:
            site_config[sn]['log_size'] = 0;
            day_log = config['logs_path'] + '/' + sn + '_' + mday + '.log';
            if os.path.exists(day_log):
                site_config[sn]['log_size'] = os.path.getsize(day_log)

            tmp = []
            for logName in logList:
                if logName.find(sn + '_') != 0: continue;
                tmp.append(logName)

            length = len(tmp) - config['log_save'];
            if length > 0:
                tmp = sorted(tmp)
                for i in range(length):
                    filename = config['logs_path'] + '/' + tmp[i];
                    if not os.path.exists(filename): continue
                    os.remove(filename)
        return site_config;

    def __is_ipn(self, ipn):
        for i in range(4):
            if ipn[0][i] == ipn[1][i]: continue;
            if ipn[0][i] < ipn[1][i]: break;
            return False
        return True

    def __format_ip(self, ip):
        tmp = ip.split('.')
        if len(tmp) < 4: return False
        tmp[0] = int(tmp[0])
        tmp[1] = int(tmp[1])
        tmp[2] = int(tmp[2])
        tmp[3] = int(tmp[3])
        return tmp;

    def __get_site_conf(self):
        if not self.__config: self.__config = self.get_config(None)
        conf = {
            'open': True,
            'project': '',
            'log': True,
            'cdn': False,
            'cdn_header': ['x-forwarded-for', 'x-real-ip'],
            'retry': self.__config['retry'],
            'retry_cycle': self.__config['retry_cycle'],
            'retry_time': self.__config['retry_time'],
            'disable_php_path': ['^/upload/', '^/static/'],
            'disable_path': [],
            'disable_ext': [],
            'disable_upload_ext': ['php', 'jsp'],
            'url_white': [],
            'url_rule': [],
            'url_tell': [],
            'disable_rule': {
                'url': [],
                'post': [],
                'args': [],
                'cookie': [],
                'user_agent': []
            },
            'cc': {
                'open': self.__config['cc']['open'],
                'cycle': self.__config['cc']['cycle'],
                'limit': self.__config['cc']['limit'],
                'endtime': self.__config['cc']['endtime']
            },
            'get': self.__config['get']['open'],
            'post': self.__config['post']['open'],
            'cookie': self.__config['cookie']['open'],
            'user-agent': self.__config['user-agent']['open'],
            'scan': self.__config['scan']['open'],
            'drop_abroad': False
        }
        return conf

    def sync_cnlist(self, get):
        if not get:
            self.get_config(None)
            self.get_site_config(None)
        rcnlist = public.httpGet(public.get_url() + '/cnlist.json')
        if not rcnlist: return public.returnMsg(False, '连接云端失败')
        cloudList = json.loads(rcnlist)
        cnlist = self.__get_rule('cn')
        n = 0
        for ipd in cloudList:
            if ipd in cnlist: continue;
            cnlist.append(ipd)
            n += 1
        self.__write_rule('cn', cnlist)
        if get: return public.returnMsg(True, '同步成功,本次共增加 ' + str(n) + ' 个IP段')

    def __remove_log_file(self, siteName):
        public.ExecShell('rm -rf /www/wwwlogs/free_waf/' + siteName + '_*.log')
        total = json.loads(public.readFile(self.__path + 'total.json'))
        if siteName in total['sites']:
            del (total['sites'][siteName])
            self.__write_total(total)
        return True

    def __write_total(self, total):
        return public.writeFile(self.__path + 'total.json', json.dumps(total))

    def __write_config(self, config):
        public.writeFile(self.__path + 'config.json', json.dumps(config))
        public.serviceReload();

    def __write_site_config(self, site_config):
        public.writeFile(self.__path + 'site.json', json.dumps(site_config))
        public.serviceReload();

    def __write_log(self, msg):
        public.WriteLog('Nginx免费防火墙', msg)

    def __check_cjson(self):
        cjson = '/usr/local/lib/lua/5.1/cjson.so'
        if os.path.exists(cjson):
            if os.path.exists('/usr/lib64/lua/5.1'):
                if not os.path.exists('/usr/lib64/lua/5.1/cjson.so'):
                    public.ExecShell("ln -sf /usr/local/lib/lua/5.1/cjson.so /usr/lib64/lua/5.1/cjson.so");
            if os.path.exists('/usr/lib/lua/5.1'):
                if not os.path.exists('/usr/lib/lua/5.1/cjson.so'):
                    public.ExecShell("ln -sf /usr/local/lib/lua/5.1/cjson.so /usr/lib/lua/5.1/cjson.so");
            return True

        c = '''wget -O lua-cjson-2.1.0.tar.gz http://download.bt.cn/install/src/lua-cjson-2.1.0.tar.gz -T 20
tar xvf lua-cjson-2.1.0.tar.gz
rm -f lua-cjson-2.1.0.tar.gz
cd lua-cjson-2.1.0
make
make install
cd ..
rm -rf lua-cjson-2.1.0
ln -sf /usr/local/lib/lua/5.1/cjson.so /usr/lib64/lua/5.1/cjson.so
ln -sf /usr/local/lib/lua/5.1/cjson.so /usr/lib/lua/5.1/cjson.so
/etc/init.d/nginx reload
'''
        public.writeFile('/root/install_cjson.sh', c)
        public.ExecShell('cd /root && bash install_cjson.sh')
        return True


1.png2.png

发表评论 / Comment

用心评论~