利用CloudFlare FREE计划硬抗动态站CC、DDOS攻击
作为一名站长,网站安全自然是要放在首位的,要不一但业务做起来之后突然被攻击导致服务中断造成的后果还是很严重的。本文就来谈谈怎么针对WP(wordpress)+CF(CloudFlare)的组合来定制防御方案,每个站点方案不同,仅供参考。
CF免费版本的规则从原来的没有已经到现在的五条规则了,这一点还是奠定了CF在我心中的地位。搜索引擎能找到的千篇一律方案在这里就不加赘述了(比如关闭洋葱路由等等),下面分享一下我的WAF规则以及思路。
1、针对耗费资源的查询定制验证。
众所周知,每次查询都需要耗费大量的服务器资源,而且必须是动态不能缓存,也有一些非法帽子叔叔用这种方法来污染你的SEO结果,那么我们有必要在查询这里进行一个验证。
通过观察得知,WP的搜索都会有【?s=】的字符串,譬如这里我在搜索框中输入【114514】进行查询时,得到的链接就是这样
因此我们可以添加一个规则,只要URL中出现这个字段就进行安全验证,这个是最基础的,也是我觉得每个站点都需要做好的措施。
表达式代码
(http.request.full_uri contains "?s=")
2、针对非法流量的管控
一开始按照网上的WAF规则等等配好了之后发现,免费版针对动态站的抗CC效果十分有限,仍然会有2成左右的非法流量进入源服务器,这点流量就足以让我的小水管炸掉了,于是我就开始反思能不能有一个方法,当我的服务器过载时使用CF的Under Attack 模式自动开盾,负载降下来之后再关掉,还别说,真让我在GitHub里面找到个三年前的项目能够满足我的需求。
配置方法也很简单,在相应目录(这里我是放在了根目录/root)新增一个名字为【autouam.sh】的文件,代码如下:
#!/bin/bash
#
# Usage:
# screen -dmS autouam &&
# screen -x -S autouam -p 0 -X stuff "bash /root/autouam.sh" &&
# screen -x -S autouam -p 0 -X stuff $'\n'
#
mode="cpu"
# 两种模式可选,一:load (负载) 二:cpu
challenge="1"
# 是否同时开启验证码质询 设为1即开启
keeptime="60"
# ≈开盾最小时间,如60 则开盾60秒内负载降低不会关,60秒后关
interval="0.5"
# 检测间隔时间,默认0.5秒
email="换成你的"
# CloudFlare 账号邮箱
api_key="换成你的"
# CloudFlare API KEY
zone_id="换成你的"
# 区域ID 在域名的概述页面获取
default_security_level="high"
# 默认安全等级 关闭UAM时将会把安全等级调整为它
check="80"
#自定义开盾阈值(非必需)
#load模式填负载值 如:8 cpu模式填百分数值 如:90
api_url="https://api.cloudflare.com/client/v4/zones/$zone_id/settings/security_level"
# API的地址
api_url1="https://api.cloudflare.com/client/v4/zones/$zone_id/firewall/rules"
# API的地址之二
api_url2="https://api.cloudflare.com/client/v4/zones/$zone_id/filters"
# API的地址之三
# 安装依赖
if ! which jq &> /dev/null; then
echo "jq not found!"
if [[ -f "/usr/bin/apt-get" ]]; then
apt-get install -y jq
elif [[ -f "/usr/bin/dnf" ]]; then
dnf install -y epel-release
dnf install -y jq
elif [[ -f "/usr/bin/yum" ]]; then
yum install -y epel-release
yum install -y jq
fi
fi
for((;;))
do
if [[ "$mode" == "cpu" ]]; then
check=${check:-90}
#系统空闲时间
TIME_INTERVAL=5
time=$(date "+%Y-%m-%d %H:%M:%S")
LAST_CPU_INFO=$(cat /proc/stat | grep -w cpu | awk '{print $2,$3,$4,$5,$6,$7,$8}')
LAST_SYS_IDLE=$(echo $LAST_CPU_INFO | awk '{print $4}')
LAST_TOTAL_CPU_T=$(echo $LAST_CPU_INFO | awk '{print $1+$2+$3+$4+$5+$6+$7}')
sleep ${TIME_INTERVAL}
NEXT_CPU_INFO=$(cat /proc/stat | grep -w cpu | awk '{print $2,$3,$4,$5,$6,$7,$8}')
NEXT_SYS_IDLE=$(echo $NEXT_CPU_INFO | awk '{print $4}')
NEXT_TOTAL_CPU_T=$(echo $NEXT_CPU_INFO | awk '{print $1+$2+$3+$4+$5+$6+$7}')
#系统空闲时间
SYSTEM_IDLE=`echo ${NEXT_SYS_IDLE} ${LAST_SYS_IDLE} | awk '{print $1-$2}'`
#CPU总时间
TOTAL_TIME=`echo ${NEXT_TOTAL_CPU_T} ${LAST_TOTAL_CPU_T} | awk '{print $1-$2}'`
load=`echo ${SYSTEM_IDLE} ${TOTAL_TIME} | awk '{printf "%.2f", 100-$1/$2*100}'`
else
load=$(cat /proc/loadavg | colrm 5)
check=${check:-$(cat /proc/cpuinfo | grep "processor" | wc -l)}
fi
if [[ ! -f "status.txt" ]]; then
echo "" > status.txt
else
status=$(cat status.txt)
fi
if [[ -f "ruleid.txt" ]]; then
ruleid=$(cat ruleid.txt)
fi
if [[ -f "filterid.txt" ]]; then
filterid=$(cat filterid.txt)
fi
now=$(date +%s)
time=$(date +%s -r status.txt)
echo "当前$mode负载:$load"
if [[ $status -eq 1 ]]; then
echo "UAM ON!"
if [[ "$challenge" -eq 1 ]]; then
echo "Challenge ON!"
fi
else
echo "UAM OFF!"
if [[ "$challenge" -eq 1 ]]; then
echo "Challenge OFF!"
fi
fi
newtime=`expr $now - $time`
closetime=`expr $keeptime - $newtime`
if [[ $(awk 'BEGIN {print ('$load'<'$check') ? 1:0}') -eq 1 ]] && [[ $status -eq 1 ]] && [[ $newtime -gt $keeptime ]]; then echo -e "\n$mode负载低于$check,当前已开盾超过规定时间$newtime秒,尝试调整至默认安全等级($default_security_level)" # Disable Under Attack Mode result=$(curl -X PATCH "$api_url" \ -H "X-Auth-Email: $email" \ -H "X-Auth-Key: $api_key" \ -H "Content-Type: application/json" \ --data "{ \"value\": \"$default_security_level\" }" --silent \ | jq -r '.success') if [[ "$result" = "true" ]]; then echo 0 > status.txt
echo -e "\n成功"
fi
if [[ "$challenge" -eq 1 ]]; then
result=$(curl -X DELETE "$api_url1/$ruleid" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $api_key" \
-H "Content-Type: application/json" \
--silent)
result1=$(curl -X DELETE "$api_url2/$filterid" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $api_key" \
-H "Content-Type: application/json" \
--silent)
if echo $result | jq -e '.success' && echo $result1 | jq -e '.success'; then
echo -e "\n验证码关闭成功"
fi
fi
elif [[ $(awk 'BEGIN {print ('$load'<'$check') ? 1:0}') -eq 1 ]]; then echo -e "\n$mode负载低于$check,不做任何改变,状态持续了$newtime秒" if [[ $status -eq 1 ]]; then echo -e "将于$closetime秒后调整安全等级至$default_security_level" fi elif [[ $(awk 'BEGIN {print ('$load'>'$check') ? 1:0}') -eq 1 ]] && [[ $status -eq 1 ]] && [[ $newtime -gt $keeptime ]]; then
echo -e "\n$mode负载高于$check,当前已开启UAM超过$keeptime秒,UAM无效"
elif [[ $(awk 'BEGIN {print ('$load'>'$check') ? 1:0}') -eq 1 ]] && [[ $status -eq 1 ]]; then
echo -e "\n$mode负载高于$check,当前已开启($newtime秒),请再观察"
elif [[ $(awk 'BEGIN {print ('$load'>'$check') ? 1:0}') -eq 1 ]]; then
echo -e "\n$mode负载高于$check,开启UAM"
# Enable Under Attack Mode
result=$(curl -X PATCH "$api_url" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $api_key" \
-H "Content-Type: application/json" \
--data "{
\"value\": \"under_attack\"
}" --silent \
| jq -r '.success')
if [[ "$result" = "true" ]]; then
echo 1 > status.txt
echo -e "\n成功"
fi
if [[ "$challenge" -eq 1 ]]; then
while :
do
result=$(curl -X POST "$api_url2" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $api_key" \
-H "Content-Type: application/json" \
--data '[{
"expression": "(not cf.client.bot)"
}]' --silent)
if echo $result | jq -e '.success'; then
filterid=$(echo $result | jq -r '.result[].id')
else
filterid=$(echo $result | jq -r '.errors[].meta.id')
for i in $filterid
do
result1=$(curl -X DELETE "$api_url2/$i" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $api_key" \
-H "Content-Type: application/json" --silent)
done
if echo $result1 | jq -e '.success'; then
echo "\n冲突的filter删除成功"
fi
fi
echo $result | jq -e '.success' && break
done
result=$(curl -X POST "$api_url1" \
-H "X-Auth-Email: $email" \
-H "X-Auth-Key: $api_key" \
-H "Content-Type: application/json" \
--data "[{
\"action\": \"challenge\",
\"filter\": {
\"id\": \"$filterid\",
\"expression\": \"(not cf.client.bot)\"
}
}]" --silent)
if echo $result | jq -e '.success'; then
ruleid=$(echo $result | jq -r '.result[].id')
echo "$filterid" > filterid.txt
echo "$ruleid" > ruleid.txt
echo -e "验证码开启成功,规则id:$ruleid"
fi
fi
else
echo 0 > status.txt
fi
sleep $interval
clear
done
这里简单说一下三个需要换成你自己的参数去哪里找。
email就是你注册CloudFlare 账号的邮箱
api_key在右上角头像——我的个人资料——API 令牌——Global API Key处寻找。
zone_id在域名页面寻找
启动脚本(注意路径):
screen -dmS autouam && screen -x -S autouam -p 0 -X stuff "bash /root/autouam.sh" && screen -x -S autouam -p 0 -X stuff $'\n'
查看screen:
screen -r autouam -d
可以看到短短几分钟拦截了10W+次的异常请求。
3、做好缓存
啊有人就要说了,我做的是动态站,怎么能缓存呢?万一涉及到用户登录的场景那不就寄了吗?
别急~得益于CF强大的规则定制,我们可以实现针对未登录用户添加缓存。
我们来到缓存——Cache Rules——创建规则,如图。
表达式
(not http.cookie contains "wp-" and not http.cookie contains "wordpress_" and not http.cookie contains "comment_" and not http.request.uri.path contains "/login.php")
这里注意/login.php换成你的登录地址。
攻击的流量一般不会登录网站,这样就可以让异常流量全部打到缓存的CF服务器上了,不但大幅加快访问速度而且无限抗C,因为压根和你的服务器没关系啊。
今天分享的这个方案来源于:https://zyq.today/archives/178112,大家感兴趣的可以去作者网站查看!