标签谢选骏全集

2021年1月31日星期日

谢选骏:防火墙下的开放



《郑永年:中国要更开放,单边开放也可以》(2020年12月11日 新华网)报道:


11月28日,由新华网主办,新华网思客、新华网浙江分公司承办的“2020新华网思客年会”在杭州举行,本次年会聚焦“中国经济的下一程:新理念、新征程、新蓝图”。香港中文大学(深圳)全球与当代中国高等研究院院长郑永年发表题为《中美关系、有限全球化与中国经济的未来》的主旨演讲。


以下为演讲摘编:


面对百年不遇之大变局,中国要单边开放,即使美国或者西方向中国封锁,中国也应当向他们开放,不应当把自己封闭起来。历史上看,哪个国家最开放,哪个国家最获利;哪个国家最封闭,再强大也会衰落下去。我们现在提“双循环”,以国内循环为主是指中国的内部消费对中国经济增长的贡献越来越大,并不是说外循环不重要。


我一直主张长三角、珠三角还是要以外循环为主。这些沿海经济区都是在改革开放中、在全球化的背景下成长起来的,脱离不了国际市场,它本身就是国际市场的一部分。当下的外循环并不仅是资本的问题,现在主要是技术的问题,而技术进步的本质就是开放。哪个国家如果封闭了,技术肯定会衰落。我们必须承认这样一个现实,从近代以来,大部分的原创性的技术来自西方,这是个事实。所以开放就是为了保证技术。所以我觉得我们的思想一定要进入国际大循环。我现在担心的是,我们一些人里面,世界观里面越来越没有世界,很多人认为我就是世界,这肯定会衰弱。


那么我们自己的优势是什么呢?一个是开放,另一个是市场。开放是什么概念呢?中国很多地方就是开放得不够,开放的深度不够,广度也不够。这两年我们实行金融开放,尽管有中美贸易冲突,但根据美国一家智库的估计,过去两年有六千亿美金流入中国金融市场,今年英国The Economist(《经济学人》)估计前十个月有两千亿美金流入中国市场。这表明中国的开放开一个口、开一块新地方,就可以改变世界资本流向的格局。


为什么会这样呢?就是因为中国的市场。我们以中产的阶层规模来衡量中国市场,现在中国中产的比例比美国小,美国的中产人数占总人口的50%,中国是30%,但我们有4亿中产,这个数量相当于美国的人口总数,而美国只有2亿多中产,中国的4亿中产消费能力不比美国的2亿低,因为我们的收入差异大。所以我跟美国驻中国的商会、美国驻香港的商会沟通,没有多少企业乐意离开中国。


讲未来,我们就要讲怎么开放的问题。最近中国加入了RCEP(《区域全面经济伙伴关系协定》),我们对TPP(跨太平洋伙伴关系协定)也是持开放的态度。但RCEP跟TPP是两码事,RCEP主要是传统的关税,TPP则是标准。我们需要考虑中国如何在国际市场上通过开放影响国际标准,这是非常关键的。如果之前的开放是传统的贸易投资类的开放,那下一步的开放一定要思考贸易投资中国际规则的问题。如何制定规则?我觉得我们要有世界观、国际观,不能太自私,我们是第二大经济体,是一个大国,所以要考虑到其他。


在贸易依存度方面,总体上来说因为中国现在市场大、经济量大,所以其他国家依赖中国多。但是要依赖多少才合适?像这次医疗物资,根据美方的估计,85%以上的美国医疗物资靠中国供应,美国97%的抗生素靠中国供应,这对美国来说会很难接受。所以我觉得中美之间,或者中国跟西方国家之间有些领域的贸易依存度下降一点,对各方都有好处。我们不能太民族主义地看问题,如果太民族主义地看问题就看不清这个世界是怎么运作的,就看不清我们以后怎么在世界上扮演一个领导角色。


现在中美关系的局势,我想十年二十年三十年不会变,中美之间的问题不应是谁打败谁的问题,现在很多人整天想如何打败美国,美国也想如何打败中国,这完全是错误的。这两个大国,不会互相打败,这两个大国的问题就是和平共存的问题。所以我是觉得我们还是需要在这个时代,学会如何跟美国打交道。我们不能太骄傲,还是要谦虚谦虚再谦虚,开放开放再开放。


谢选骏指出:所谓单边开放,其实就是“防火墙下的开放”;不能太骄傲,所以防火墙不能少——


网文《防火墙》报道:


防火墙(英语:Firewall)在计算机科学领域中是一个架设在互联网与企业内网之间的信息安全系统,根据企业预定的策略来监控往来的传输。防火墙可能是一台专属的网络设备或是运行于主机上来检查各个网络接口上的网络传输。它是目前最重要的一种网络防护设备,从专业角度来说,防火墙是位于两个(或多个)网络间,实行网络间访问或控制的一组组件集合之硬件或软件。


功能

防火墙最基本的功能就是隔离网络,透过将网络划分成不同的区域(通常情况下称为ZONE),制定出不同区域之间的访问控制策略来控制不同信任程度区域间传送的数据流。例如互联网是不可信任的区域,而内部网络是高度信任的区域。以避免安全策略中禁止的一些通信。它有控制信息基本的任务在不同信任的区域。典型信任的区域包括互联网(一个没有信任的区域) 和一个内部网络(一个高信任的区域) 。最终目标是:根据最小特权原则,在不同水平的信任区域,透过连通安全政策的运行,提供受控制的连通性。例如:TCP/IP Port 135~139是 Microsoft Windows 的【网上邻居】所使用的。如果电脑有使用【网上邻居】的【共享文件夹】,又没使用任何防火墙相关的防护措施的话,就等于把自己的【共享文件夹】公开到Internet,使得任何人都有机会浏览目录内的文件。且早期版本的Windows有【网上邻居】系统溢出的无密码保护的漏洞(这里是指【共享文件夹】有设密码,但可经由此系统漏洞,达到无须密码便能浏览文件夹的需求)。防火墙的本义,是指古代构筑和使用木制结构房屋时,为防止火灾发生及蔓延,人们将坚固石块堆砌在房屋周围做为屏障,这种防护结构建筑就被称为防火墙。现代网络时代引用此喻意,指隔离本地网络与外界网络或是局域网间与互联网或互联网的一道防御系统,借由控制过滤限制消息来保护内部网资料的安全。


防火墙类型

针对普通用户的个人防火墙,通常是在一部电脑上具有数据包过滤功能的软件,如ZoneAlarm及Windows XP SP2后内置的防火墙程序。而专业的防火墙通常为网络设备,或是拥有2个以上网络接口的电脑。以作用的TCP/IP堆栈区分,主要分为网络层防火墙和应用层防火墙两种,但也有些防火墙是同时运作于网络层和应用层。


网络层(数据包过滤型)防火墙运作于TCP/IP协议堆栈上。管理者会先根据企业/组织的策略预先设置好数据包通过的规则或采用内置规则,只允许符合规则的数据包通过。


一般而言,由于来源IP不易阻挡,通常借由阻挡非80/443的端口,以阻挡来自非80/443的端口的不需要的流量。


网络层防火墙可分为:状态感知(stateful)与无状态感知(stateless)。


状态感知(stateful)


状态感知防火墙会针对活动中的连线维护前后传输的脉络,并使用这些状态信息来加速数据包过滤处理。


根据需求,现行的网络连线由各种性质描述,包括:来源端IP位置,目的端IP位置、UDP或TCP端口口号码,以及连线所处的状态阶段(连线初始化、交握中,资料传输中、或完成连线)。


如果有数据包与现存连线不符,防火墙会根据规则来评估此数据包是否该属于另外一个新连线。如果数据包符合现存连线,防火墙会根据自己所创建的状态表完成比对,该数据包就不必额外处理,即可通过两端网络。


无状态感知(stateless)


无状态感知防火墙所需较少的存储器,针对于通过的数据包,作比较简易与快速的过滤。如此,相较于查询对话工作期间(session),无状态感知防火墙所耗的时间也较少。


这种防火墙可处理无状态网络通信协议,这种协议并没有对话工作期间(session)的概念。


反之,这种防火墙无法根据沟通的两端所处的状态阶段作出复杂的决策。


我们也能以另一种较宽松的角度来制定防火墙规则,只要数据包不符合任何一项“否定规则”就予以放行。现在的操作系统及网络设备大多已内置防火墙功能。


较新的防火墙能利用数据包的多样属性来进行过滤,例如:来源 IP 地址、来源端口号、目的 IP 地址或端口号、服务类型(如 HTTP 或是 FTP)。也能经由通信协议、TTL 值、来源的域名或网段……等属性来进行过滤。


应用层防火墙


防火墙的视察软件接口示例,纪录IP进出的情况与对应事件

应用层防火墙是在TCP/IP堆栈的“应用层”上运作,使用浏览器时所产生的资料流或是使用 FTP 时的资料流都是属于这一层。应用层防火墙可以拦截进出某应用程序的所有数据包,并且屏蔽其他的数据包(通常是直接将数据包丢弃)。理论上,这一类的防火墙可以完全阻绝外部的资料流进受保护的机器里。


防火墙借由监测所有的数据包并找出不符规则的内容,可以防范电脑蠕虫或是木马程序的快速蔓延。实际上,这个方法繁复(因软件种类极多),所以大部分防火墙都不会考虑以这种方法设计。


截至2012年,所谓的下一代防火墙(NGFW)都只是“拓宽”并“深化”了在应用栈检查的能力。例如,现有支持深度分组检测的现代防火墙均可扩展成入侵预防系统(IPS),用户身份集成(用户ID与IP或MAC地址绑定),和Web应用防火墙(WAF)。


代理服务

代理(Proxy)服务器(可以是一台专属的网络设备,或是在一般电脑上的一套软件)采用应用程序的运作方式,回应其所收到的数据包(例:连线要求)来实现防火墙的功能,而屏蔽/抛弃其他数据包。代理服务器用来连接一个网络(例:互联网)到另一个特定子网(例:企业内网)的转送者。


代理会使从外部网络窜改一个内部系统更加困难,且只要对于代理有良好的设置,即使内部系统出现问题也不一定会造成安全上的漏洞。相反地,入侵者也许劫持一个公开可及的系统和使用它作为代理人为他们自己的目的;代理人伪装作为那个系统对其它内部机器。当对内部地址空间的用途增加安全,破坏者也许仍然使用方法譬如IP欺骗(IP spoofing)试图通过数据包对目标网络。


防火墙经常有网络地址转换(NAT) 的功能,并且主机被保护在防火墙之后共同地使用所谓的“私人地址空间”,定义在RFC 1918。


防火墙的适当的配置要求技巧和智能,它要求管理员对网络协议和电脑安全有深入的了解,因小差错可使防火墙不能作为安全工具。


防火墙架构

Server-firewall.svg

主机型防火墙

此防火墙需有两张网卡,一张与互联网连接,另一张与内部网连接,如此互联网与内部网的通道无法直接接通,所有数据包都需要透过主机发送。


双闸型防火墙

此防火墙除了主机型防火墙的两张网卡外,另安装应用服务转送器的软件,所有网络数据包都须经过此软件检查,此软件将过滤掉不被系统所允许的数据包。


屏障单机型防火墙

此防火墙的硬件设备除需要主机外,还需要一个路由器,路由器需具有数据包过滤的功能,主机则负责过滤及处理网络服务要求的数据包,当互联网的数据包进入屏障单机型防火墙时,路由器会先检查此数据包是否满足过滤规则,再将过滤成功的数据包,转送到主机进行网络服务层的检查与发送。


屏障双闸型防火墙

将屏障单机型防火墙的主机换成双闸型防火墙。


屏障子网域型防火墙

此防火墙借由多台主机与两个路由器组成,电脑分成两个区块,屏障子网域与内部网,数据包经由以下路径,第一个路由器——>屏障子网域——>第二路由器——>内部网,此设计因有阶段式的过滤功能,因此两个路由器可以有不同的过滤规则,让网络数据包更有效率。若一数据包通过第一过滤器数据包,会先在屏障子网域进行服务处理,若要进行更深入内部网的服务,则要通过第二路由器过滤。


缺点

正常状况下,所有互联网的数据包软件都应经过防火墙的过滤,这将造成网络交通的瓶颈。例如在攻击性数据包出现时,攻击者会不时寄出数据包,让防火墙疲于过滤数据包,而使一些合法数据包软件亦无法正常进出防火墙。


谢选骏指出:除了防火墙,还需要“敏感词过滤”——


《如何优雅地过滤敏感词》(码农沉思录 2018年08月11日)报道:


敏感词过滤功能在很多地方都会用到,理论上在Web应用中,只要涉及用户输入的地方,都需要进行文本校验,如:XSS校验、SQL注入检验、敏感词过滤等。今天着重讲讲如何优雅高效地实现敏感词过滤。


敏感词过滤方案一


先讲讲笔者在上家公司是如何实现敏感词过滤的。当时毕竟还年轻,所以使用的是最简单的过滤方案。简单来说就是对于要进行检测的文本,遍历所有敏感词,逐个检测输入的文本中是否含有指定的敏感词。这种方式是最简单的敏感词过滤方案了,实现起来不难,示例代码如下:


   @Test

    public void test1(){

        Set<String>  sensitiveWords=new HashSet<>();

        sensitiveWords.add("shit");

        sensitiveWords.add("傻逼");

        sensitiveWords.add("笨蛋");

        String text="你是傻逼啊";

        for(String sensitiveWord:sensitiveWords){

            if(text.contains(sensitiveWord)){

                System.out.println("输入的文本存在敏感词。——"+sensitiveWord);

                break;

            }

        }

    }

复制代码

代码十分简单,也确实能够满足要求。但是这个方案有一个很大的问题是,随着敏感词数量的增多,敏感词检测的时间会呈线性增长。由于之前的项目的敏感词数量只有几十个,所以使用这种方案不会存在太大的性能问题。但是如果项目中有成千上万个敏感词,使用这种方案就会很耗CPU了。


敏感词过滤方案二


在网上查了下敏感词过滤方案,找到了一种名为DFA的算法,即Deterministic Finite Automaton算法,翻译成中文就是确定有穷自动机算法。它的基本思想是基于状态转移来检索敏感词,只需要扫描一次待检测文本,就能对所有敏感词进行检测,所以效率比方案一高不少。


假设我们有以下5个敏感词需要检测:傻逼、傻子、傻大个、坏蛋、坏人。那么我们可以先把敏感词中有相同前缀的词组合成一个树形结构,不同前缀的词分属不同树形分支,以上述5个敏感词为例,可以初始化成如下2棵树……


把敏感词组成成树形结构有什么好处呢?最大的好处就是可以减少检索次数,我们只需要遍历一次待检测文本,然后在敏感词库中检索出有没有该字符对应的子树就行了,如果没有相应的子树,说明当前检测的字符不在敏感词库中,则直接跳过继续检测下一个字符;如果有相应的子树,则接着检查下一个字符是不是前一个字符对应的子树的子节点,这样迭代下去,就能找出待检测文本中是否包含敏感词了。


我们以文本“你是不是傻逼”为例,我们依次检测每个字符,因为前4个字符都不在敏感词库里,找不到相应的子树,所以直接跳过。当检测到“傻”字时,发现敏感词库中有相应的子树,我们把他记为tree-1,接着再搜索下一个字符“逼”是不是子树tree-1的子节点,发现恰好是,接下来再判断“逼”这个字符是不是叶子节点,如果是,则说明匹配到了一个敏感词了,在这里“逼”这个字符刚好是tree-1的叶子节点,所以成功检索到了敏感词:“傻逼”。大家发现了没有,在我们的搜索过程中,我们只需要扫描一次被检测文本就行了,而且对于被检测文本中不存在的敏感词,如这个例子中的“坏蛋”和“坏人”,我们完全不会扫描到,因此相比方案一效率大大提升了。


在Java中,我们可以用HashMap来存储上述的树形结构,还是以上述敏感词为例,我们把每个敏感词字符串拆散成字符,再存储到HashMap中,可以这样存:


{

    "傻": {

        "逼": {

            "isEnd": "Y"

        },

        "子": {

            "isEnd": "Y"

        },

        "大": {

            "个": {

                "isEnd": "Y"

            }

        }

    }

}

复制代码

首先将每个词的第一个字符作为key,value则是另一个HashMap,value对应的HashMap的key为第二个字符,如果还有第三个字符,则存储到以第二个字符为key的value中,当然这个value还是一个HashMap,以此类推下去,直到最后一个字符,当然最后一个字符对应的value也是HashMap,只不过这个HashMap只需要存储一个结束标志就行了,像上述的例子中,我们就存了一个{"isEnd","Y"}的HashMap,来表示这个value对应的key是敏感词的最后一个字符。


同理,“坏人”和“坏蛋”这2个敏感词也是按这样的方式存储起来,这里就不罗列出来了。


用HashMap存储有什么好处呢?我们知道HashMap在理想情况下可以以O(1)的时间复杂度进行查询,所以我们在遍历待检测字符串的过程中,可以以O(1)的时间复杂度检索出当前字符是否在敏感词库中,效率比方案一提升太多了。


接下来上代码。


首先是初始化敏感词库:


   private Map<Object,Object> sensitiveWordsMap;


    private static final String END_FLAG="end";


    private void initSensitiveWordsMap(Set<String> sensitiveWords){

        if(sensitiveWords==null||sensitiveWords.isEmpty()){

            throw new IllegalArgumentException("Senditive words must not be empty!");

        }

        sensitiveWordsMap=new HashMap<>(sensitiveWords.size());

        String currentWord;

        Map<Object,Object> currentMap;

        Map<Object,Object> subMap;

        Iterator<String> iterator = sensitiveWords.iterator();

        while (iterator.hasNext()){

            currentWord=iterator.next();

            if(currentWord==null||currentWord.trim().length()<2){  //敏感词长度必须大于等于2

                continue;

            }

            currentMap=sensitiveWordsMap;

            for(int i=0;i<currentWord.length();i++){

                char c=currentWord.charAt(i);

                subMap=(Map<Object, Object>) currentMap.get(c);

                if(subMap==null){

                    subMap=new HashMap<>();

                    currentMap.put(c,subMap);

                    currentMap=subMap;

                }else {

                    currentMap= subMap;

                }

                if(i==currentWord.length()-1){

                    //如果是最后一个字符,则put一个结束标志,这里只需要保存key就行了,value为null可以节省空间。

                    //如果不是最后一个字符,则不需要存这个结束标志,同样也是为了节省空间。

                    currentMap.put(END_FLAG,null);

                }

            }

        }

    }

复制代码

代码的逻辑上面已经说过了,就是循环敏感词集合,将他们放到HashMap中,这里不再赘述。


接下来是敏感词的扫描:


public enum MatchType {


    MIN_MATCH("最小匹配规则"),MAX_MATCH("最大匹配规则");


    String desc;


    MatchType(String desc) {

        this.desc = desc;

    }

}


    public Set<String> getSensitiveWords(String text,MatchType matchType){

        if(text==null||text.trim().length()==0){

            throw new IllegalArgumentException("The input text must not be empty.");

        }

        Set<String> sensitiveWords=new HashSet<>();

        for(int i=0;i<text.length();i++){

            int sensitiveWordLength = getSensitiveWordLength(text, i, matchType);

            if(sensitiveWordLength>0){

                String sensitiveWord = text.substring(i, i + sensitiveWordLength);

                sensitiveWords.add(sensitiveWord);

                if(matchType==MatchType.MIN_MATCH){

                    break;

                }

                i=i+sensitiveWordLength-1;

            }

        }

        return sensitiveWords;

    }


    public int getSensitiveWordLength(String text,int startIndex,MatchType matchType){

        if(text==null||text.trim().length()==0){

            throw new IllegalArgumentException("The input text must not be empty.");

        }

        char currentChar;

        Map<Object,Object> currentMap=sensitiveWordsMap;

        int wordLength=0;

        boolean endFlag=false;

        for(int i=startIndex;i<text.length();i++){

            currentChar=text.charAt(i);

            Map<Object,Object> subMap=(Map<Object,Object>) currentMap.get(currentChar);

            if(subMap==null){

                break;

            }else {

                wordLength++;

                if(subMap.containsKey(END_FLAG)){

                    endFlag=true;

                    if(matchType==MatchType.MIN_MATCH){

                        break;

                    }else {

                        currentMap=subMap;

                    }

                }else {

                    currentMap=subMap;

                }

            }

        }

        if(!endFlag){

            wordLength=0;

        }

        return wordLength;

    }

复制代码

其中,MatchType表示匹配规则,有时候我们只需要找到一个敏感词就可以了,有时候则需要知道待检测文本中到底包含多少个敏感词,前者对应的是最小匹配原则,后者则是最大匹配原则。


getSensitiveWordLength方法的作用是根据给定的待检测文本及起始下标,还有匹配规则,计算出待检测文本中的敏感词长度,如果不存在,则返回0,存在则返回匹配到的敏感词长度。


getSensitiveWords方法则是扫描一遍待检测文本,逐个检测每个字符是否在敏感词库中,然后将检测到的敏感词截取出来放到集合中返回给客户端。


最后写个测试用例测一下:


   public static void main(String[] args) {

        Set<String> sensitiveWords=new HashSet<>();

        sensitiveWords.add("你是傻逼");

        sensitiveWords.add("你是傻逼啊");

        sensitiveWords.add("你是坏蛋");

        sensitiveWords.add("你个大笨蛋");

        sensitiveWords.add("我去年买了个表");

        sensitiveWords.add("shit");


        TextFilter textFilter=new TextFilter();

        textFilter.initSensitiveWordsMap(sensitiveWords);

        String text="你你你你是傻逼啊你,说你呢,你个大笨蛋。";

        System.out.println(textFilter.getSensitiveWords(text,MatchType.MAX_MATCH));

    }

复制代码

结果输出如下:


可以看到,我们成功地过滤出了敏感词。


敏感词过滤方案三


方案二在性能上已经可以满足需求了,但是却很容易被破解,比如说,我在待检测文本中的敏感词中间加个空格,就可以成功绕过了。要解决这个问题也不难,有一个简单的方法是初始化一个无效字符库,比如:空格、*、#、@等字符,然后在检测文本前,先将待检测文本中的无效字符去除,这样的话被检测字符就不存在这些无效字符了,因此还是可以继续用方案二进行过滤。只要被检测文本不要太长,那么我们只要在方案二的基础上再多扫描一次被检测文本去除无效字符就行了,这个性能损耗也还是可以接受的。


如果敏感词是英文,则还要考虑大小写的问题。有一个比较简单的解决方案是在初始化敏感词时,将敏感词都以小写形式存储。同时,在检测文本时,也统一将待检测文本转化为小写,这样就能解决大小写的问题了。


比较棘手的是中文跟拼音混合的情况,比如“傻逼”这个敏感词,可以通过“sha逼”这种中文跟拼音混合的方式轻松绕过,对于这种情况我目前还没想到比较好的解决方案,有想法的读者可以在文末留言。


完整代码:github.com/hzjjames/Te…


谢选骏指出:如法炮制,就可以优雅地进行统治、永久地掌握权力了。

没有评论:

发表评论

谢选骏:病夫治国创造历史

网文《美国历史上病夫治国误过哪些事》报道: 在法国记者皮埃尔·阿考斯和瑞士医学博士皮埃尔·朗契尼克1976年所著的《病夫治国》一书中,20世纪那些鼎鼎大名的政治人物,多经受着病痛折磨,他们一边患病,一边做着决策,有时甚至在神智不清状态下,仍孜孜于国务,让人捏了一把汗。 美国和苏联...