有不少的朋友搭建了外貿站的朋友想要限制自己的網站不讓國內的IP訪問,也有一些朋友網站存放的資源可能因為各種原因需要阻止特定的IP訪問,還有一些朋友看到攻擊源IP大多來自國外,想要阻止國外的IP造訪網站。

無論是出於什麼原因,屏蔽和阻止特定地區和國家的IP訪問都是我們日常建站中經常要用到的。如果你用的是PHP,比較簡單的方法就是在PHP檔案加入判斷IP的程式碼,利用IP函式庫進行比對,如果IP為限定存取範圍內,則阻止其繼續存取。

如果網站是Nginx,則可以直接使用Nginx-ngx_http_geoip_module模組,該模組可以精確到國家、省、市等一級的IP,並且全部由Nginx執行識別和阻止訪問,所以相對於PHP來說比較省資源,但Nginx編譯起來比較費事。

如果網站是搭建在VPS或獨立伺服器上,那麼可以直接使用Linux防火牆,利用iptables規則來阻止特定國家和省份的IP存取。當是,Wordpress用戶完全不用擔心Nginx、iptables等設定的問題,因為Wordpress早就有了各種限制IP存取的插件了。

這篇文章就來分享一下網站封鎖和阻止特定地區和國家的IP存取設定四種方法:PHP程式碼、Nginx模組、iptables防火牆和Wordpress外掛程式。如果你在網站建立的過程總是被各種惡意攻擊所困擾,可以試試以下方法:

  1. 五條關於使用免費VPS控制面板的安全建議-不讓駭客有可趁之機
  2. WordPress開啟Nginx fastcgi_cache快取加速方法-Nginx設定實例
  3. 十個你可能不知道的CloudFlare免費CDN加速技巧-SSLDDOSCache

PS:2019年3月25日更新,對於VPS的安全問題,很朋友可能會忽略VPS本身的SSH配置,這裡有強化策略:VPS主機和伺服器安全防護:SSH修改埠,添加白名單,僅限密鑰登入。

PS:2020年3月19日更新,如果你的網站已經被駭了,可以參考以下方法來解決:網站掛木馬和伺服器被黑排查分析-VPS主機和伺服器強化安全方法。

一、PHP程式碼屏蔽特定IP

PHP程式碼比較簡單,直接將以下程式碼丟到你的PHP檔案就可以實現阻止特定範圍內IP訪問網站了,根據IP庫的精準度,可以準確到國家、省、市等一級的IP,程式碼範例如下(這段程式碼可以用來在BA期間使用):

<?php
/**
 *
 * test.php(屏蔽国家IP)
 *
 */

$verification = '美国';//需要屏蔽国家的IP
function get_client_ip() { 
               $ip = $_SERVER['REMOTE_ADDR'];     
         if (isset($_SERVER['HTTP_X_REAL_FORWARDED_FOR']) && preg_match('/^([0-9]{1,3}.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_REAL_FORWARDED_FOR'])) {            
         $ip = $_SERVER['HTTP_X_REAL_FORWARDED_FOR'];       
         }          
         elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match('/^([0-9]{1,3}.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {             
         $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];       
         }          
         elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {           
         $ip = $_SERVER['HTTP_CLIENT_IP'];       
         }          
         return $ip;    
         }
$ip = get_client_ip();//获取访客IP
$antecedents = $_SERVER['HTTP_REFERER'];//访客来路地址
$result = file_get_contents("http://ip.taobao.com/service/getIpInfo.php?ip=".$ip);//IP数据库来自淘宝。
$address = json_decode($result,true);
//判断访客是否属于美国,是否来自百度,是否来自谷歌
if($address['data']['country'] == $verification && strpos($antecedents, 'baidu') === false && strpos($antecedents, 'google') === false){
        sleep(10);//设置一个10秒等待。
        header('HTTP/1.1 503 Service Temporarily Unavailable');
        header('Status: 503 Service Temporarily Unavailable');
        header('Retry-After: 3600000');
        exit;
}


/****** 如果需要阻止某一个省份的IP访问,使用以下代码*********/


<?php
/**
 *
 * test.php(屏蔽地方IP)
 *
 */

$verification = '江西省';//需要屏蔽省份的IP
function get_client_ip() { 
               $ip = $_SERVER['REMOTE_ADDR'];     
         if (isset($_SERVER['HTTP_X_REAL_FORWARDED_FOR']) && preg_match('/^([0-9]{1,3}.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_REAL_FORWARDED_FOR'])) {            
         $ip = $_SERVER['HTTP_X_REAL_FORWARDED_FOR'];       
         }          
         elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match('/^([0-9]{1,3}.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {             
         $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];       
         }          
         elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {           
         $ip = $_SERVER['HTTP_CLIENT_IP'];       
         }          
         return $ip;    
         }
$ip = get_client_ip();//获取访客IP
$antecedents = $_SERVER['HTTP_REFERER'];//访客来路地址
$result = file_get_contents("http://ip.taobao.com/service/getIpInfo.php?ip=".$ip);//IP数据库来自淘宝。
$address = json_decode($result,true);
//判断访客是否属于江西省,是否来自百度,是否来自谷歌
if($address['data']['region'] == $verification && strpos($antecedents, 'baidu') === false && strpos($antecedents, 'google') === false){
  sleep(99999999);//设置一个999999秒的等待。
  Header("HTTP/1.1 204 No Content");
  exit;
}

二、Nginx-ngx_http_geoip_module模組

IP庫下載:

  1. HTTPS://Dev.馬秀敏的.com/Geo IP/legacy/個噢李特/

2.1  禁止特定國家IP訪問

ngx_http_geoip_module模組可以讓Nginx根據來訪者的IP實現不同的需要,這裡我們利用ngx_http_geoip_module模組來阻止特定IP位址存取網站。

首先是將ngx_http_geoip_module編譯到Nginx。如果你用的是寶塔BT面板,可以採用以下指令:

#安装geoip库
yum -y install epel-release
yum -y install geoip-devel


#先查看一下本机的Nginx配置情况
[root@cs ~]# nginx -V
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) 
built with OpenSSL 1.0.2l  25 May 2017
TLS SNI support enabled
configure arguments: --user=www --group=www --prefix=/www/server/nginx --with-openssl=/www/server/nginx/src/openssl --add-module=/www/server/nginx/src/ngx_devel_kit --add-module=/www/server/nginx/src/lua_nginx_module --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --add-module=/www/server/nginx/src/nginx-http-concat --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-stream --with-stream_ssl_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre=pcre-8.40 --with-ld-opt=-ljemalloc

#开始下载Nginx,这里用的是1.15.1,你也可以下载其它的版本
wget http://nginx.org/download/nginx-1.15.1.tar.gz
tar -xzvf nginx-1.15.1.tar.gz
cd nginx-1.15.1

#下面的命令只是在上面的Nginx -v得到的配置详情后加上了--with-http_geoip_module,目的是为了保持原来的配置不变同时又增加新的模块

./configure --user=www --group=www --prefix=/www/server/nginx --with-openssl=/www/server/nginx/src/openssl --add-module=/www/server/nginx/src/ngx_devel_kit --add-module=/www/server/nginx/src/lua_nginx_module --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --add-module=/www/server/nginx/src/nginx-http-concat --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-stream --with-stream_ssl_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre=pcre-8.40 --with-ld-opt=-ljemalloc --with-http_geoip_module

#只编译不安装
make

如果你用的是LNMP腳本或Oneinstack,可以參考這裡:Oneinstack。啟用Nginx-ngx_http_geoip_module模組。先停用Nginx。

然後替換新的Nginx並查看geoip模組是否已經載入。命令如下:

mv /www/server/nginx/sbin/nginx /www/server/nginx/sbin/nginx-lala.im
cp objs/nginx /www/server/nginx/sbin/nginx
ldd /www/server/nginx/sbin/nginx

到你的寶塔面板點擊Nginx,修改設定文件,加入以下程式碼:

geoip_country /usr/share/GeoIP/GeoIP.dat;

操作如下圖:

現在啟動Nginx,你可以在網站的Nginx配置中添加規則了,例如你可以將特定國家的IP訪問返回指定錯誤或導向另一個頁面和網站,程式碼範例:

#返回403 502 404等错误
location / {
default_type text/html;
charset utf-8;
if ($geoip_country_code = CN) {
return 403;
}
}
#导向另一个网站目录
location / {
default_type text/html;
charset utf-8;
if ($geoip_country_code = CN) {
root /home/www/wzfou.com-cn/;
}
}

這是新增網站配置。

最後效果如下:

2.2  僅允許指定國家IP訪問

方法和上面是一樣的,先在Nginx主配置中引入IP庫,然後在在網站的Nginx配置中加入阻止任何國家IP但允許指定國家IP的代碼,示例如下 :

 # 引入IP库
     
  geoip_country /usr/share/GeoIP/GeoIP.dat;
  geoip_city /usr/share/GeoIP/GeoLiteCity.dat;


 map $geoip_country_code $allowed_country {
                default no;
                CN yes;
        }
    
# 在配置中阻止IP
        if ($allowed_country = no) {
                return 403;
        }

三、iptables 防火牆

先熟悉一下iptables用法和ipset :

1、iptables 包含幾個表,每個表由鏈組成。預設的是 filter 表,最常用的也是 filter 表,另一個比較常用的是 nat 表。一般封 IP 就是在 filter 表的 INPUT 鏈新增規則。

2.在進行規則比對時,是從規則清單中從頭到尾一條一條進行比對。

3.ipset 提供了把這個 O(n) 的操作變成 O(1) 的方法:就是把要處理的 IP 放進一個集合,對這個集合設定一條 iptables 規則。像 iptable 一樣,IP sets 是 Linux 核心中的東西,ipset 這個指令是對它進行操作的工具。

iptables只允許指定ip存取本機的指定端口,命令如下:

1、在tcp协议中,禁止所有的ip访问本机的3306端口。

iptables -I INPUT -p tcp –dport 3306 -j DROP

2、允许123.456.789访问本机的3306端口

iptables -I INPUT -s 123.456.789 -p tcp –dport 3306 -j ACCEPT

以此类推…………………………………

封掉一个IP段:
iptables -I INPUT -s 121.0.0.0/8 -j DROP

以上命令的顺序不能错

然后保存iptables
# service iptables save
重启防火墙
#service iptables restart

iptables規則刪除、清空、關閉以及儲存方法:

#CentOS 7请停止firewalld并安装iptables-services
systemctl stop firewalld
systemctl mask firewalld

#安装 iptables-services
yum install iptables-services

################
保存 iptables 规则
service iptables save

重启 iptables
service iptables restart

#################
执行清除命令 iptables -F时可能会断开与服务器的连接,如果想清空的话,先执行
/sbin/iptables -P INPUT ACCEPT
然后执行
/sbin/iptables -F

如果关闭防火墙,执行 
/etc/init.d/iptables stop   
或者是 services iptables stop  

#######################
iptables 规则若重启后消失,请用以下方法
步骤1:备份
iptables-save > /etc/iptables.up.rules.bak

步骤2:删除规则
vim /etc/sysconfig/iptables
或 vim /etc/iptables.up.rules

手动删除即可。

步骤3:导入新规则

iptables-restore < /etc/sysconfig/iptables

最后,重启VPS就可以生效了。

3.1  一鍵屏蔽指定國家IP訪問

  1. HTTPS://GitHub.com/III i III1/block-IPS-from-countries

原理是下載指定國家的IP段,然後將IP段加入到iptables規則當中,直接執行以下指令:

wget https://raw.githubusercontent.com/iiiiiii1/Block-IPs-from-countries/master/block-ips.sh
chmod +x block-ips.sh
./block-ips.sh

然後會要你選擇要封鎖IP還是解封IP。

選擇封鎖IP後會讓你輸入國家代碼,請到這裡查看:http://www.ipdeny.com/ipblocks,例如美國就是輸入us,確定好完成整個US的IP封禁。

如果想要解封的話,再次執行指令,然後選擇2即可。

3.2  一鍵僅允許指定國家IP訪問

上面我們實現了一鍵屏蔽特定國家的IP訪問,但是有不少人希望讓自己的網站僅讓某一個國家的IP訪問,其它的則禁止訪問,這時我們就可以使用以下命令了:

wget https://do.wzfou.net/wzfou/block-any.sh
chmod +x block-ips.sh
./block-ips.sh

上面的程式碼只允許國內的IP訪問,並會在:/etc/rc.d/rc.local寫入規則,每次系統重新啟動後都會重新匯入iptables規則,如果你調整了iptables規則,需要編輯:/ etc/rc.d/rc.local刪除對應的啟動自運行程式碼。 block-any.sh程式碼如下:

#! /bin/bash
#判断是否具有root权限
root_need() {
    if [[ $EUID -ne 0 ]]; then
        echo "Error:This script must be run as root!" 1>&2
        exit 1
    fi
}

#检查系统分支及版本(主要是:分支->>版本>>决定命令格式)
check_release() {
    if uname -a | grep el7  ; then
        release="centos7"
    elif uname -a | grep el6 ; then
        release="centos6"
        yum install ipset -y
    elif cat /etc/issue |grep -i ubuntu ; then
        release="ubuntu"
        apt install ipset -y
    fi
}

#安装必要的软件(wget),并下载中国IP网段文件(最后将局域网地址也放进去)
get_china_ip() {
  #安装必要的软件(wget)
  rpm --help >/dev/null 2>&1 && rpm -qa |grep wget >/dev/null 2>&1 ||yum install -y wget ipset >/dev/null 2>&1 
  dpkg --help >/dev/null 2>&1 && dpkg -l |grep wget >/dev/null 2>&1 ||apt-get install wget ipset -y >/dev/null 2>&1

  #该文件由IPIP维护更新,大约一月一次更新(也可以用我放在国内的存储的版本,2018-9-8日版)
  [ -f china_ip_list.txt ] && mv china_ip_list.txt china_ip_list.txt.old
  wget https://github.com/17mon/china_ip_list/blob/master/china_ip_list.txt
  cat china_ip_list.txt |grep 'js-file-line">' |awk -F'js-file-line">' '{print $2}' |awk -F'<' '{print $1}' >> china_ip.txt
  rm -rf china_ip_list.txt
  #wget https://qiniu.wsfnk.com/china_ip.txt

  #放行局域网地址
  echo "192.168.0.0/18" >> china_ip.txt
  echo "10.0.0.0/8" >> china_ip.txt
  echo "172.16.0.0/12" >> china_ip.txt
}

#只允许国内IP访问
ipset_only_china() {
  echo "ipset create whitelist-china hash:net hashsize 10000 maxelem 1000000" > /etc/ip-black.sh
  for i in $( cat china_ip.txt )
  do
        	echo "ipset add whitelist-china $i" >> /etc/ip-black.sh
  done
  echo "iptables -I INPUT -m set --match-set whitelist-china src -j ACCEPT" >> /etc/ip-black.sh
  #拒绝非国内和内网地址发起的tcp连接请求(tcp syn 包)(注意,只是屏蔽了入向的tcp syn包,该主机主动访问国外资源不用影响)
  echo "iptables  -A INPUT -p tcp --syn -m connlimit --connlimit-above 0 -j DROP" >> /etc/ip-black.sh
  #拒绝非国内和内网发起的ping探测(不影响本机ping外部主机)
  echo "iptables  -A INPUT -p icmp -m icmp --icmp-type 8 -j DROP" >> /etc/ip-black.sh
  #echo "iptables -A INPUT -j DROP" >> /etc/ip-black.sh
  rm -rf china_ip.txt
}

run_setup() {
  chmod +x /etc/rc.local
  sh /etc/ip-black.sh
  rm -rf /etc/ip-black.sh
  #下面这句主要是兼容centos6不能使用"-f"参数
  ipset save whitelist-china -f /etc/ipset.conf || ipset save whitelist-china > /etc/ipset.conf
  [ $release = centos7 ] && echo "ipset restore -f /etc/ipset.conf" >> /etc/rc.local
  [ $release = centos6 ] && echo "ipset restore < /etc/ipset.conf" >> /etc/rc.local
  echo "iptables -I INPUT -m set --match-set whitelist-china src -j ACCEPT" >> /etc/rc.local
  echo "iptables  -A INPUT -p tcp --syn -m connlimit --connlimit-above 0 -j DROP" >> /etc/rc.local
  echo "iptables  -A INPUT -p icmp -m icmp --icmp-type 8 -j DROP" >> /etc/rc.local
  #echo "iptables -A INPUT -j DROP" >> /etc/rc.local
}

main() {
  check_release
  get_china_ip
  ipset_only_china

case "$release" in
centos6)
  run_setup
  ;;
centos7)
  chmod +x /etc/rc.d/rc.local
  run_setup
  ;;
ubuntu)
  sed -i '/exit 0/d' /etc/rc.local
  run_setup
  echo "exit 0" >> /etc/rc.local
  ;;
esac
}
main

如果你要排除一些IP,允許這些IP作為例外繼續訪問,可以使用iptables -I命令新增iptables規則,或者手動添加iptables規則,注意要將規則放在最上面,因為iptables執行順序是從上往下。

3.3  手動設定僅允許特定國家IP訪問

手動設定和上面的一鍵設定方法是一樣的,按照下面的命令一條一條地執行就可以了。

1、安装ipset

#Debian/Ubuntu系统
apt-get -y install ipset

#CentOS系统
yum -y install ipset
CentOS 7还需要关闭firewall防火墙:

systemctl stop firewalld.service
systemctl disable firewalld.service

2、清空之前的规则

#防止设置不生效,建议清空下之前的防火墙规则
iptables -P INPUT ACCEPT
iptables -F

3、创建新规则

#创建一个名为cnip的规则
ipset -N cnip hash:net
#下载国家IP段,这里以中国为例,其它国家IP下载参考:http://www.ipdeny.com/ipblocks/
wget -P . http://www.ipdeny.com/ipblocks/data/countries/cn.zone
#将IP段添加到cnip规则中
for i in $(cat /root/cn.zone ); do ipset -A cnip $i; done

4、设置IP段白名单

#放行IP段
iptables -A INPUT -p tcp -m set --match-set cnip src -j ACCEPT
#关掉所有端口
iptables -P INPUT DROP
这时候就只有指定国家的IP能访问服务器了。

#如果你在国内,网站不允许被国内人访问,建议别关所有端口,这样你的S-S-H会上不去,我们可以只关闭80/443端口。

#关闭指定端口,比如80/443
iptables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -p tcp --dport 443 -j DROP
这时候其他国家的IP是无法访问你服务器的80/443端口,等于无法访问你的网站,其它端口还是可以访问的。

5、删除规则

#将参数里的-A改成-D就是删除规则了,如
iptables -D INPUT -p tcp -m set --match-set cnip src -j ACCEPT
iptables -D INPUT -p tcp --dport 443 -j DROP

四、Wordpress屏蔽特定IP

WordPress外掛

  1. HTTPS://WordPress.org/plugins/word分冊/
  2. HTTPS://WordPress.org/plugins/all-in-one-我怕-security-安定-firewall/
  3. HTTPS://WordPress.org/plugins/IP-個噢-block/

上面三個Wordpress安全插件都帶有屏蔽IP存取的設置,可以根據IP的來源、國家、範圍和網域等進行屏蔽。 (點擊放大)

IP Geo Block稍微專業一些,除了可以封鎖特定的IP位址,還可以對不同的IP位址存取不同的頁面進行封鎖或阻止,功能非常強大。 (點擊放大)

五、總結

網站封鎖特定國家IP最簡單的方法就是本文介紹的PHP程式碼,引用了淘寶IP庫,準確度非常高,而且還可以精確到省、市,自己可以根據需要來調整。不足的地方是不支援Https以及僅限PHP運行。

實際上常用的應該是iptables,直接以Linux防火牆的方式來阻止IP訪問,不消耗資源,阻止得乾淨徹底。 Nginx的Geo IP模組應用範圍比較廣泛,結合Nginx你可以實現對不同的IP用戶展現不同的內容。

發表評論