2010年7月21日星期三

[转]Denial of Service

转载自 http://www.sq120.com/Get/yingjian/232130979.htm


 


拒绝服务,英文为"Distributed Denial of Service",也就是我们常说的DDoS。那什么又是拒绝服务呢?大家可以这样理解,凡是能导致合法用户不能进行正常的网络服务的行为都算是拒绝服务攻击。拒绝服务攻击的目的非常的明确,就是要阻止合法用户对正常网络资源的访问,从而达到攻击者不可告人的目的。www.sq120.com推荐文章


简单的讲,拒绝服务就是用超出被攻击目标处理能力的海量数据包消耗可用系统、带宽资源,致使网络服务瘫痪的一种攻击手段。在早期,拒绝服务攻击主要是针对处理能力比较弱的单机,如个人PC,或是窄带宽连接的网站,对拥有高带宽连接,高性能设备的网站影响不大。


在1999年底,伴随着DDoS的出现,高端网站高枕无忧的局面不复存在。DDoS实现是借助数百,甚至数千台被植入攻击守护进程的攻击主机同时发起的集团作战行为。因此,分布式拒绝服务也被称之为"洪水攻击"。常见的DDoS攻击手法有UDP Flood、SYN Flood、ICMP Flood、TCP Flood、Proxy Foold等等。


曾经案例


在新千年来临之际,全球知名网站雅虎第一个宣告因为遭受分布式拒绝服务攻击而彻底崩溃。UDP Flooder是一款拒绝服务攻击软件,是基于UDP协议对目标服务器进行洪水攻击。


当年雅虎就是遭到来自50台计算机使用此软件攻击而导致瘫痪。紧接着Amazon.com、CNN、ZDNet、Buy.com、Excite和eBay等其他知名网站几乎在同一时间也遭受暴风骤雨般的DDoS攻击,带来了巨大的经济损失。而一些政治团体或非法组织,则通过DDoS攻击对政府门户网站或其它官方网站进行打击,造成巨大的政治影响。


 


简单防御方法


DDoS是一种特殊形式的拒绝服务攻击,所以对付它是一个系统工程,想仅仅依靠某种系统或产品防范DDoS是不现实的。可以肯定的是,要想完全杜绝DDoS目前是不可能的,但是通过适当的措施抵御90%的DDoS攻击是可以做到的。对于此类隐蔽性极好的DDoS攻击的防范,更重要的是用户要加强安全防范意识,提高网络系统的安全性。


(一)通过对系统进行优化,高水平的技术专家,能够根据攻击迅速确定攻击类型,并对各种操作系统核心的配置了如指掌,同时能够根据己方所提供的服务类型和侧重点选择防护和退让策略。


(二)建议网络管理者,在所管辖网络的出入口,配置源路由控制策略,限制非本网的源地址对外访问,这样就能保证至少本网内,不会有发动DoS/DDoS攻击的机器,如果这样做的网络增多了,DoS/DDoS攻击自然减少甚至杜绝。需要得到攻击路径中上级的网络设备服务商支持。


(三)采取退让策略,利用DNS轮循,或者通过负载均衡、Cluster等技术增加响应主机数量,增加系统资源,从而提升抗打击能力。


(四)安装抗DoS能力的防火墙,能够防护一些低规模的拒绝服务攻击,配置和操作相当简单。防火墙作为通用网络安全产品,在防DoS方面不可能达到专业产品的性能和效率,对大规模的DoS攻击是无能为力的,甚至会成为攻击目标。


(五)建立网络安全事件应急响应小组,为所管辖的网络提供应急服务,一旦出现DDoS攻击或者其他的攻击,可以及时启动应急流程,借助有实力安全厂商、CNCERT(国家计算机网络应急技术处理协调中心)的力量,追查攻击的源头。必要时,申请公安部门的协助,诉诸于法律。

2010年7月5日星期一

memcached与iptables

在客户端去连接memcached的服务端,很简单,一个tcp或者udp连接,不需要user和password。貌似很简单,很容易维护,没有了管理帐户的麻烦,但是很不安全。因为如果不加限制,任何一台计算机通过telnet ip port都可以访问memcached服务器,访问和修改数据。这是,就需要iptables对访问加以限制。


什么是iptables(官网)?iptables is the userspace command line program used to configure the Linux 2.4.x and 2.6.x IPv4 packet filtering ruleset. It is targeted towards system administrators. 我认为iptables就是计算机网络数据进出的一个负责人或者说一个门卫,对任何一个数据包,根据数据包的执行阶段(chain)和相关操作(table),执行相关的策略(target),当然需要对数据包进行一定的筛选,可以根据协议(protocal)、端口(port)、地址(ip)、数据包(state)状态等等去判断。


至于这些筛选规则和相关策略都可以设置,但是首先你必须是root。可以通过iptables或者iptables-restore去设置,也可以通过iptables-save或者iptables -L去查看当前的设置。(PS: 如果当前没有设置过规则,使用iptables-save是会报错的,iptables-save v1.4.2: Unable to open /proc/net/ip_tables_names: No such file or directory)


通过这次也终于搞明白了unix中经常提到的bind是何意?一台电脑是可以有多个ip的,比如123.0.0.1和x.x.x.x,在启动memcached的时候,可以bind到其中一个ip。客户端访问时,就必须访问服务器端绑定的ip,不然就会访问出错。


Resoures & References:



  1. netfiler

  2. iptables-save与iptables-restore

  3. iptables指南


 

2010年7月4日星期日

python-memcached连接维护

纵所周知,memcahced client管理了多个与服务器的连接,实现分布式缓存系统。那我们需要担心的是,在执行相关操作时,如果连接断开会出现什么结果?


阅读了python-memcached源码,它是这样处理的:当缓存或者读取数据的时候,连接断开,出现socket.error,client daemon就会标记该连接为dead,在30秒内,不会重新连接。需要注意的是,当出现socket error,除了相关处理外,client daemon并不会抛出错误,仅仅将该次error输出到标准错误,执行相关命名的返回结果也是None或者0(有就是执行失败)。



对于此种处理,我认为是合理的。因为毕竟memcached是缓存,而不是持久化存储,它并不保证你的每次操作(或者说命令)在没有报错的情况下,一定是成功的。

安全有效获取客户端IP

在web开发中,经常需要获取用户的ip地址,以便记录或者添加特定的限制。但是实际生产环境中,web server使用了代理服务器,所以environ中remote_addr的ip地址通常是离web server最近一层代理服务器的ip地址,这样得到的ip就没有太大的意义。但是x-forwarded-for的出现就是想解决这种问题,但是这种方式如何使用以及可靠性如何,都是我们应该考虑的范畴。下面就具体讨论下关于x-forwarded-for和remote_addr的区别。


1. 含义不同


The REMOTE_ADDR variable MUST be set to the network address of the client sending the request to the server.(参考RFC3875 4.1.8 REMOTE_ADDR)


The X-Forwarded-For (XFF) HTTP header is a de facto standard for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or load balancer. This is a non-RFC-standard request header which was introduced by the Squid caching proxy server's developers.(参考)

2. 格式不同


remote_addr的值就是单个ip地址,而x-forwarded-for则是多个ip地址的集合:


The general format of the header is:

X-Forwarded-For: client1, proxy1, proxy2

where the value is a comma+space separated list of IP addresses, the left-most being the farthest downstream client, and each successive proxy that passed the request adding the IP address where it received the request from. In this example, the request passed proxy1, proxy2 and proxy3 (proxy3 appears as remote address of the request).(参考)

3. 来源不同


remote_addr是http server与客户端通过三次握手建立的tcp连接后,根据socket中的ip赋值给remote_addr。而x-forwarded-for则是http server解析http报文头部获取的。


 


总结:通过来源不同,可以看出,environ中的remote_addr是很不容易伪造的,但是x-forwarded-for则相反,极易伪造,伪造一个http 报文头部,轻而易举。所以我们不能简单通过获取x-forwarded-for的ip地址列中的第一项作为客户端ip地址。但是由于代理服务器是我们部署的,这个是可信的,所以x-forwarded-for的内容,我们不能“全信”,也不能“全不信”。至少经过我们的代理服务器,在x-forwarded-for上添加的ip是可信的,所以我们可以通过实际生产环境部署的代理服务器情况,也从x-forwarded-for中选择合适的ip。比如我们只有一层代理服务器,那么x-forwarded-for的最后一个ip就是客户端ip,并且是“可信”的,因为这个ip是代理服务器根据客户端ip加上去的。

memcached学习

许多web应用都是将数据保存在RDBMS中,web apps从RDMS中读取数据并经过渲染后返回给客户端,在浏览器中显示。但是随着数据访问量的增大,访问的集中,RDBMS的负担加重,响应速度变慢、显示延迟等问题都会出现。


但是如果我们采取以下做法:每次从RDBMS读取的数据存放在内存中,并且更新内容时,不仅更改RDBMS并且同时更改内存中的数据。那么等待用户下次读取数据时,先从内存中获取,如果存在直接返回,如果没有再从RDBMS获取返回。


采取这种做法,响应速度就会得到很大改善。因为磁盘I/O的速度跟内存的读写速度不是一个等级的。


memcached是一个实现以上做法的、高性能、分布式的内存对象缓存系统,目的主要是通过减轻数据库负载来使动态web应用程序提速,任意的数据都是采用key-value形式存储的。


1. memcached特点



1.1 协议简单


采用简单的基于文本行的协议,并没有采用二进制协议或者复杂的XML格式。(目前memcahed开发者们正在策划和实现二进制协议)

1.2 基于libevent的事件处理


使用libevent取代了网络服务器所使用的循环检查架构,从而可以省去对网络的处理,达到不错的性能。(参考 对于此点优势,了解还不是特别深入)

1.3 内置内存存储方式


为了提高性能,memcached都将数据保存在内存中。因此如果重启memcahed、重启操作系统或者断电等操作都会造成数据的丢失。所以说memcached不能永久存储数据。

1.4 memcached不互相通信的分布式


memcached虽然是分布式缓存服务器,但是服务端并没有分布式功能,分布式读和写都由客户端采用特定的算法去实现。另外,服务器端并不会通信以达到共享信息。我的看法是:memcached是个缓存系统,并不需要像NoSQL达到“最终一致性”,所以每个服务器保存的内容都是不一样的,都只是部分数据。另外如果一个服务器出现异常,也不影响正常程序,因为我们丢失的仅仅是“缓存”数据。




2 实现memcached的相关理论和方式




2.1 内存存储方式


在使用内存存储数据的时候,就涉及到如何分配内存。如果采用简单的malloc和free,就会导致内存碎片,反而会加重操作系统管理内存的负担。而memcached采用的内存分配机制Slab Allocation,就是为了解决这种问题。具体的工作原理是:按照预先设定的大小,将内存分割成特定长度的块,并且把长度相同的块分成组;这些内存并不会释放,而是重复利用。memcached根据客户端发来数据的大小去选择合适的块去保存,然后将数据缓存其中。


Slab Allocation解决了内存碎片的问题,但是却带来了另外一个问题:由于分配块的大小是固定大小,而客户端发来缓存的数据大小却是随机的,就会造成内存浪费的问题,比如一个100k的数据保存一个128k大小的块中,造成了28k内存空间的浪费。对于这个问题还没有良好的解决方案。但是如果预先知道客户端发送的数据的公用大小,或者仅缓存大小相同的情况下,只要使用适合数据大小的组的列表,就可以减小浪费。

2.2 删除缓存数据方式


内存空间不是无限的,当然有个上限或者一个指定值,所以删除缓存数据是必须的。memcached采用的方式是:LRU(Least Recently Used),也就是说:当memcached内存容量达到指定值后,需要空间来缓存新的数据时,去删除那些近期最少使用的数据,用它的内存空间来保存新的缓存数据。

2.3 客户端分布式算法


memcached分服务端和客户端,服务端仅仅负责存储和读取。至于分布式存储,是客户端需要做的事情。分布式存储,需要保证实现一个特性:良好的伸缩性和扩展性,我对此特性的理解就是:在memcached服务器中,如果新添一台服务器,简简单单通过配置就可以,而不影响其他服务器的正常运行,缓存数据的键也会均匀分散到各个服务器;如果一台memcached服务器因为故障无法连接,也不会影响其他缓存,系统依然能继续运行。


下面就简单介绍下实现该特性比较完善的算法:Consistent Hashing,nosql中需要实现分布的数据库也多采用这种算法。其基本原理是:首先计算各个服务器的哈希值,并将其配置到0 ~ 2^32 的圆上(不是线段哦),然后采用同样的方法求出数据的键的哈希值,映射到圆上,沿着映射的位置顺时针查找,将数据保存在最近的一个服务器上。如果超过2^32任然找不到,就保存到第一台服务器上。读取数据时,也是同样的道理,计算读取数据的键的哈希值,根据哈希值找到对应服务器,读取值。


采取此种算法的好处就是,不管添加服务器或者减少服务器或者服务器故障,都不会影响缓存数据,不影响系统的正常运行。并且,如果我们让一个服务器(物理节点)对应n个虚拟节点,随机分布在0 ~ 2^32的圆上,这样就可以解决分布数据不均匀的问题。


 

2010年7月3日星期六

又遇乱码

身为一个Chinese Web Coder,最烦也最不可避免的就是乱码。在解决乱码之前,必须搞清楚unicode和编码方式的区别,python中执行环境的默认编码。


unicode的诞生就是要显示世界上不同国家、不同地区的文字。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。在我看来,unicode就是一个简单的映射表,key是二进制编码,value就是各个语言的不同字符。而如何高效保存和读取这些二进制编码,就是编码方式所要解决的方法,不同编码方式有着不同的保存读取方式。


在python命令行解释器中:


>>> s = "中国"
>>> s
'\xd6\xd0\xb9\xfa'

我的理解就是,"中"和"国"两个字的unicode码按照python默认的编码方式保存起来了。至于默认编码方式是多少,是sys.getdefaultencoding()或者sys.getfilesystemencoding()。至于这两个的区别:



  • sys.getdefaultencoding(). Return the name of the current default string encoding used by the Unicode implementation. New in version 2.0.

  • sys.getfilesystemencoding(). Return the name of the encoding used to convert Unicode filenames into system file names, or None if the system default encoding is used.


到底用的哪个,我到现在还没有搞清。按照解释,应该是sys.getdefaultencoding(),但是实际测试却是sys.getfilesystemencoding()。


但是在文件中,却有另外一番理论。在文件中,我们需要关心两个编码:文件自身保存的编码以及我们在开始设置的coding。


#! -*- coding: utf-8 -*-

With that declaration, all characters in the source file will be treated as having the encoding encoding, and it will be possible to directly write Unicode string literals in the selected encoding. The list of possible encodings can be found in the Python Library Reference, in the section on codecs.


文件中如果字符串中有汉字,其对应值是以文件保存编码方式编码,因此如果开始设置的coding跟文件保存编码不一样,用设置的coding去解码时,自然会报错。因此让两种编码保证一致,是很有必要的。


Resoucres & References



  1. Using the Python Interpreter

  2. python -> docs-pdf -> library.pdf


 

神秘的__init__.py

Python中的module和package的作用不言而喻:Each module has its own private symbol table, which is used as the global symbol table by all functions defined in the module. Thus, the author of a module can use global variables in the module without worrying about accidental clashes with a user’s global variables.而Packages are a way of structuring Python’s module namespace by using “dotted module names”.


让package称为module那样的访问,__init__ .py 。一个文件夹A如果存在文件__init__.py(哪怕这个文件夹是空的),就表明A不是一个简简单单的文件夹,而是一个包。


另外__init__.py控制着包的导入行为。比如现在存在这样一个pacakge:


Package1/ 


    __init__.py


    Module1.py


    Module2.py


    Package2/ __init__.py


       Module1.py


       Module2.py


如果执行语句:


import Package1
Package1.Module1

肯定会报错


AttributeError: 'module' object has no attribute 'Module1'


为何?因为Module1只是Package1的一个submodule,并不是attribute,通过dir(package)就可以看出。如果想执行正确上面的语句,就必须在__init__.py中执行:


import Module1

另外,语句


from Package1 import *

到底import了那些,完全依靠__init__.py中的__all__属性。


另外,关于from和import需要注意的是:



Note that when using from package import item, the item can be either a submodule (or subpackage) of the package, or some other name defined in the package, like a function, class or variable. The import statement first tests whether the item is defined in the package; if not, it assumes it is a module and attempts to load it. If it fails to find it, an ImportError exception is raised.


Contrarily, when using syntax like import item.subitem.subsubitem, each item except for the last must be a package; the last item can be a module or a package but can’t be a class or function or variable defined in the previous item.



Resouces & References



  1. Modules

  2. Python模块包中__init__.py文件的作用