Cloudflare Tips and Tricks – 51 Security

Cloudflare Tips and Tricks – 51 Security


Cloudflare is designed for easy setup. Anyone with a website and their own domain can use Cloudflare regardless of their platform choice. Cloudflare doesn’t require additional hardware, software, or changes to your code. Cloudflare stops malicious traffic before it reaches your origin web server. Cloudflare analyzes potential threats in visitor requests based on a number of characteristics:

  • visitor’s IP address,
  • resources requested,
  • request payload and frequency, and
  • customer-defined firewall rules.

By default, Cloudflare allows you to create five active firewall rules. In this post, I lists some useful firewall rules for different use cases.

Diagrams

Cloudflare security diagram:

a diagram showing how Cloudflare works to protect your site against malicious traffic


Troubleshooting

https://51sec.org/cdn-cgi/trace

fl=684f51
h=51sec.org
ip=16.3.19.10
ts=1723310073.493
visit_scheme=https
uag=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
colo=YYZ
sliver=none
http=http/3
loc=CA
tls=TLSv1.3
sni=plaintext
warp=off
gateway=off
rbi=off
kex=X25519K1be76811Drat00

Access Cloudflare Dashboard – Firewall Page

Steps:

  1. Login your Cloudflare dashboard
  2. Select the domain name you want to configure Firewall Rules
  3. Click Firewall from the tools at the top
  4. Click Firewall Rules

Firewall Page at Cloudflare dashboard:

Firewall Settings – Security Level Explaination

Threat Score based on Cloudflare Doc as configured by Security Level is based on:

Security Level Threat Scores Description
Essentially off greater than 49 Only challenges IP addresses with the worst reputation.
Low greater than 24 Challenges only the most threatening visitors.
Medium greater than 14 Challenges both moderate threat visitors and the most threatening visitors.
High greater than 0 Challenges all visitors that exhibit threatening behavior within the last 14 days.
I’m Under Attack! N/A Only for use if your website is currently under a DDoS attack.

Cloudflare sets Security Level to Medium by default.  Change the Security Level settings via the Cloudflare Firewall app under the Settings tab.

Challenge Low Risk and Higher IP but Allow Known Bots 

Bad IP:
A request that came from an IP address that is not trusted by Cloudflare based on the Threat Score.

Based on default settings (Medium security level): 

Challenges both moderate threat visitors and the most threatening visitors which threat score is higher than 14.

Here are some common agreed in Cloudflare community when using Threat score to create firewall rule

0 indicates low risk as determined by Cloudflare. Values above 10 may represent spammers or bots, and values above 40 point to bad actors on the Internet. It is rare to see values above 60, so tune your firewall rules to challenge those above 10, and to block those above 50.

My rule setting:

If access request is having Threat Score greater than or equal to 10, and it is not from Known bots, the access request will be challenged by Captcha.

Bypass CloudFlare Cache From Your Browser For Testing

Verify CloudFlare Cache Status

From Google Chrome Browser – F12 or Ctrl+Shift+I (Developer Tools) – Network tab – Filter by Doc 

Check File Name’s Response Header – > cf-cache-status 

It should show either bypass, hit, or DYNAMIC.

Dsiable Cache

Security – WAF – Rate Limiting Rules

Received huge amount of access requests to my workers. Daily requets limitation 100k was reached in less than an hour. 

One way to defend that is to enable rate limiting rule. 

Cloudflare Proxy Another Website

Method 1: Proxy Site

// 替换成你想镜像的站点
const upstream = 'netsec.alwaysdata.net'

// Custom pathname for the upstream website.
const upstream_path = '/'
 
// 如果那个站点有专门的移动适配站点,否则保持和上面一致
const upstream_mobile = 'netsec.alwaysdata.net'
 
// 你希望禁止哪些国家访问
const blocked_region = ['RU']
 
// 禁止自访问
const blocked_ip_address = ['0.0.0.0', '127.0.0.1']
 
// 替换成你想镜像的站点
const replace_dict = {
    '$upstream': '$custom_domain',
    '//netsec.alwaysdata.net': ''
}
 
//以下内容都不用动
addEventListener('fetch', event => {
    event.respondWith(fetchAndApply(event.request));
})
 
async function fetchAndApply(request) {
 
    const region = request.headers.get('cf-ipcountry').toUpperCase();
    const ip_address = request.headers.get('cf-connecting-ip');
    const user_agent = request.headers.get('user-agent');
 
    let response = null;
    let url = new URL(request.url);
    let url_host = url.host;
 
    if (url.protocol == 'http:') {
        url.protocol = 'https:'
        response = Response.redirect(url.href);
        return response;
    }
 
    if (await device_status(user_agent)) {
        upstream_domain = upstream
    } else {
        upstream_domain = upstream_mobile
    }
 
    url.host = upstream_domain;
 
    if (blocked_region.includes(region)) {
        response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
            status: 403
        });
    } else if(blocked_ip_address.includes(ip_address)){
        response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
            status: 403
        });
    } else{
        let method = request.method;
        let request_headers = request.headers;
        let new_request_headers = new Headers(request_headers);
 
        new_request_headers.set('Host', upstream_domain);
        new_request_headers.set('Referer', url.href);
 
        let original_response = await fetch(url.href, {
            method: method,
            headers: new_request_headers
        })
 
        let original_response_clone = original_response.clone();
        let original_text = null;
        let response_headers = original_response.headers;
        let new_response_headers = new Headers(response_headers);
        let status = original_response.status;
 
        new_response_headers.set('access-control-allow-origin', '*');
        new_response_headers.set('access-control-allow-credentials', true);
        new_response_headers.delete('content-security-policy');
        new_response_headers.delete('content-security-policy-report-only');
        new_response_headers.delete('clear-site-data');
 
        const content_type = new_response_headers.get('content-type');
        if (content_type.includes('text/html') && content_type.includes('UTF-8')) {
            original_text = await replace_response_text(original_response_clone, upstream_domain, url_host);
        } else {
            original_text = original_response_clone.body
        }
 
        response = new Response(original_text, {
            status,
            headers: new_response_headers
        })
    }
    return response;
}
 
async function replace_response_text(response, upstream_domain, host_name) {
    let text = await response.text()
 
    var i, j;
    for (i in replace_dict) {
        j = replace_dict[i]
        if (i == '$upstream') {
            i = upstream_domain
        } else if (i == '$custom_domain') {
            i = host_name
        }
 
        if (j == '$upstream') {
            j = upstream_domain
        } else if (j == '$custom_domain') {
            j = host_name
        }
 
        let re = new RegExp(i, 'g')
        text = text.replace(re, j);
    }
    return text;
}
 
async function device_status (user_agent_info) {
    var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    var flag = true;
    for (var v = 0; v < agents.length; v++) { if (user_agent_info.indexOf(agents[v]) > 0) {
            flag = false;
            break;
        }
    }
    return flag;
}

Method 2: Redirect

export default {
  async fetch(request, env) {
    try {
      const { pathname } = new URL(request.url);

      if (pathname.startsWith("/status")) {
        const httpStatusCode = Number(pathname.split("/")[2]);

        return Number.isInteger(httpStatusCode)
          ? fetch("https://http.cat/" + httpStatusCode)
          : new Response("That's not a valid HTTP status code.");
      }
      return fetch("https://blog.51sec.org");
    } catch(e) {
      return new Response(err.stack, { status: 500 })
    }
  }
}

Cloudflare Workers Received Attacks

Received attacks to workers. 

References

Post Comment