知乎专栏 |
应用防火墙用于保护应用及服务不受恶意访问和攻击。
应用防火墙有别于网络防火墙,防火墙防火墙偏重对IP地址和端口端访问控制。
应用防火墙有有别于7层防火墙,7层防火墙虽然能实现拆包,根据协议,做出访问控制。
应用防火墙的核心功能除了局别7层防火墙的特点,颗粒度可以做到更细。
例如开发过程中我们有很多需求,直接在功能模块中实现。所谓应用防火墙就是将这些功能做成一个独立模块,实现共享和复用。
计数器的需求很常见,功能简单,就是记录访问数量,计数器也是水军主要战场。
计数器需求:
对于网防火墙可以通过IP访问策略进行封杀,但是我国由于IP地址有限,主要的上网方式是NAT(网络地址转换),例如一个公司的办公室内所有电脑都是通过一个IP地址出去的。封杀IP地址容易误伤。
使用应用防火墙就容易很多,可以使用用户+COOKIE+IP地址的方案。
应用防火墙我提供了一个思路,不便提供代码。
下面的代码是10年前写的,没有100%实现,因为该代码不会影响竞业,供大家参考。
<?php /* * ===================================== * Website: http://netkiller.github.com * Author: neo <netkiller@msn.com> * Email: netkiller@msn.com * ===================================== */ class Logging { protected $file; public function __construct($logfile = "/tmp/debug.log"){ $this->file = fopen($logfile,"a+"); } public function __destruct() { //fclose($this->file); } public function close() { fclose($this->file); } private function write($msg){ fwrite($this->file,date('Y-m-d H:i:s').' '.$msg."\r\n"); } public function info($msg){ $this->write(__FUNCTION__.' '.$msg); } public function warning($msg){ $this->write(__FUNCTION__.' '.$msg); } public function error($msg){ $this->write(__FUNCTION__.' '.$msg); } public function debug($msg){ $this->write(__FUNCTION__.' '.$msg); } } class Permission{ protected $_PERMISSION = array(); public function __construct($login){ $test = array( 'neo' => array( 'News'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ), 'RSS'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ) ), 'jam' => array( 'News'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ), 'RSS'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ) ) ); //print_r($test); $this->load($test[$login]); } public function load($arr){ $this->_PERMISSION = $arr; } public function is_allowed($class, $fun){ $class = trim($class); $fun = trim($fun); //echo $class, $fun; //print_r($this->_PERMISSION); if(array_key_exists($class,$this->_PERMISSION)){ if(array_key_exists($fun,$this->_PERMISSION[$class])){ if($this->_PERMISSION[$class][$fun] == 'Y') return true; //return in_array("Y",$this->_PERMISSION[$class][$fun]); } } return false; } public function is_denied($class, $fun){ return (!$this->is_allowed($class, $fun)); } public function scan(){ return true; } } class News extends Permission{ private $logging; public function __construct(){ parent::__construct('neo'); $this->logging = new Logging('/tmp/news.log'); } public function __destruct() { $this->logging->debug('news->get permission denied!!!'); $this->logging->close(); } public function add(){ if(!$this->is_allowed(__CLASS__,__FUNCTION__)) return; print("Allowed!!! \r\n"); $this->logging->info('news->add ok'); } public function get(){ if( $this->is_denied(__CLASS__,__FUNCTION__)) { print("Denied!!! \r\n"); $this->logging->warning('news->get permission denied!!!'); } } } $news = new News(); $news->add(); $news->get();
权限来自下面数组数据,这里仅仅提供一个例子,管理权限你可以单独实现一个class,实现供权限管理功能,最终后转化为下面的数据结构即可。例如你可以将权限写入数据库,最终拼装如下数字让Permission顺利load即可。
array( 'neo' => array( 'News'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ), 'RSS'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ) ), 'jam' => array( 'News'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ), 'RSS'=> array( 'add' => 'Y', 'remove' => 'N', 'update' => 'Y' ) ) );
public function is_allowed($class, $fun) 用户判断权限是否合法。
这里提供了一个 News 类,用于演示怎样控制每个function的权限。
同时还提供了一个简单的 Logging 类用于记录程序运行日志。
有了上面的例子就可以将News应用于SOAP一类Web Service上,用来控制每个方法的权限
上面仅仅对于方法控制权限,接下来我们为程序增加7层防火墙功能
<?php /* * ===================================== * Website: http://netkiller.github.com * Author: neo <netkiller@msn.com> * Email: netkiller@msn.com * ===================================== */ class Firewall{ protected $status; protected $policy; protected $chain; protected $rule; protected $match; private $debug; //$get,$post,$cookie,$server; public function __construct() { $this->name = "Firewall"; } public function __destruct() { //print "Destroying " . $this->name . "\n"; } public function enable(){ $this->status = true; } public function disable(){ $this->status = false; } public function get(){ if($this->status){ $this->chain = $_GET; return($this); }else{ return($this->status); } } public function post(){ if($this->status){ $this->chain = $_GET; return($this); }else{ return($this->status); } $this->chain = $_POST; } public function cookie() { if($this->status){ $this->chain = $_COOKIE; return($this); }else{ return($this->status); } } public function server(){ if($this->status){ $this->chain = $_SERVER; return($this); }else{ return($this->status); } } public function match($key, $value){ if($this->debug) print_r($this->chain); $this->match = false; if(!array_key_exists($this->chain, $key)){ if($this->chain[$key] == $value){ $this->match = true; } } return($this); } public function policy($p){ $this->policy = $p; } public function counter($tm, $cnt){ return($this); } public function allow($fun = null){ if($this->status && $this->match){ if($fun){ $fun(); } } $this->destroy(); return($this->status); } public function deny($fun = null){ if($this->status && $this->match){ if($fun){ $fun(); } } $this->destroy(); return($this->status); } public function debug($tmp){ $this->debug = $tmp; } public function ip($ipaddr){ return $this->server()->match('REMOTE_ADDR', $ipaddr); } public function destroy(){ $this->chain = array(); $this->match = false; } }; #include_once('firewall.php') $fw = new Firewall(); $fw->debug(true); $fw->debug(false); $fw->enable(); //$fw->disable(); function test(){ echo 'OK'; }; function allow(){ echo 'allow'; }; function deny(){ echo 'deny'; }; //$fw->policy('blacklist'); $fw->ip('192.168.3.17')->allow('allow'); $fw->ip('192.168.3.17')->deny('deny'); $fw->counter('1m',5)->match('id','1000')->deny('test'); /* $fw->ip('172.16.0.0/24')->allow(); $fw->ip('172.16.0.0','255.255.255.0')->allow(); $fw->header(array('User-Agent' => 'MSIE5'))->deny() */ $fw->get()->match('id','1000')->deny('test'); $fw->get()->match('name','chen')->allow('test'); //$fw->get()->match(array('id' => '1000'))->deny(); /* $fw->post()->data(array('action'=>'/login.php'))->allow() $fw->cookie()->data(array('userid'=>'test'))->deny() */ $fw->server()->match('HTTP_REFERER', 'http://www.mydomain.com/index.html')->allow('test'); $fw->server()->match('REQUEST_METHOD', 'GET')->deny('test'); $fw->disable(); //$fw->destroy();
这里仅仅给你一个思路,我并没有写完程序。例如控制IP请求次数可以如下实现,请自行改善程序
<?php /* * ===================================== * Website: http://netkiller.github.com * Author: neo <netkiller@msn.com> * Email: netkiller@msn.com * ===================================== */ require 'SharedConfigurations.php'; $single_server = array( 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0 ); $multiple_servers = array( array( 'host' => '127.0.0.1', 'port' => 6379, 'database' => 15, 'alias' => 'first', ), array( 'host' => '127.0.0.1', 'port' => 6380, 'database' => 15, 'alias' => 'second', ), ); $client = new Predis\Client($single_server, array('prefix' => 'fw:')); $key=$_SERVER['REMOTE_ADDR']; if(!$client->exists($key)){ $client->setex($key, 20, 1); }else{ $client->incrby($key,1); } $counter = $client->get($key); if($counter > 10){ echo 'Deny'; } print_r($client->get($key)); //var_dump($client->keys('*'));