有许多朋友在建立外贸网站时,希望限制国内 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 插件。
一、PHP 代码屏蔽特定 IP
以下是一个简单的 PHP 代码示例,可以用来阻止特定范围内的 IP 访问网站。根据 IP 库的准确度,可以精确到国家、省、市等级的 IP。你只需要将以下代码复制到你的 PHP 文件中即可实现该功能(适用于 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;
- }
IP 库下载:
https://dev.maxmind.com/geoip/legacy/geolite/
2.1 禁止特定国家 IP 访问
使用 ngx_http_geoip_module 模块可以根据访问者的 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,启用 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/;
- }
- }
这是添加网站配置。
最后效果如下:
方法和上面是一样的,先在 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 的用法和 ipset :
1、iptables 包含多个表,每个表由链组成。默认的是 filter 表,最常用的也是 filter 表,另一个常用的是 nat 表。通常在 filter 表的 INPUT 链中添加规则来封锁 IP。
2、在进行规则匹配时,按照规则列表的顺序逐条进行匹配。
3、ipset 提供了一种将 O(n) 的操作转变为 O(1) 的方法:将要处理的 IP 放入一个集合中,并在该集合上设置一条 iptables 规则。与 iptables 类似,IP 集合是 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 就可以生效了。
- https://github.com/iiiiiii1/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 即可。
上面我们实现了一键屏蔽特定国家的 IP 访问,但是有不少人希望让自己的网站仅让某一个国家的 IP 访问,其它的则禁止访问,这时我们就可以使用以下命令了:
- wget https://www.ucblog.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 并允许它们继续访问,可以使用 iptables -I 命令添加新的 iptables 规则,或者手动添加规则。请注意,要将规则放在最顶部,因为 iptables 的执行顺序是从上往下。
手动设置与上述的一键设置方法相同,按照以下命令逐条执行即可。
- 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 插件
https://wordpress.org/plugins/wordfence/
https://wordpress.org/plugins/all-in-one-wp-security-and-firewall/
https://wordpress.org/plugins/ip-geo-block/
快到早上七点钟了,小书童睡不着觉,特花些时间来把这个教程分享给大家,网站屏蔽特定国家 IP 的最简单方法是使用本文介绍的 PHP 代码。该代码引用了淘宝 IP 库,准确度非常高,可以精确到省、市,根据需要进行调整。不过,该方法存在一些不足之处,不支持 Https,并且仅限于 PHP 运行。
实际上,常用的方法是使用 iptables,直接以 Linux 防火墙的方式阻止 IP 访问。这种方法不会消耗额外的资源,可以彻底地阻止访问。另外,Nginx 的 Geo IP 模块也被广泛应用,结合 Nginx,可以根据不同的 IP 用户展示不同的内容。