作为一名站长,网站安全自然是要放在首位的,要不一但业务做起来之后突然被攻击导致服务中断造成的后果还是很严重的。本文就来谈谈怎么针对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
这里简单说一下三个需要换成你自己的参数去哪里找。
启动脚本(注意路径):
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")
好了,今天先记这么多,以后遇到合适的内容再分享吧~
小乔,我的博客信息已经变更,在友链申请页面已经提交修改
你好,已经收到啦!请稍等cdn节点刷新即可看到更改!
ps:新站点越来越简洁了哈哈
从emlog换到了typecho,找了一个更简洁的模板
这个不错,不过视频播放点了播放没反应~~
还是回源问题,现在应该可以了∠( ᐛ 」∠)_