Application Security Lab Guides

Combined interactive edition for the four Application Security classes: Cloudflare Setup (AppSec 1), Build and Tune Effective Security Rules (AppSec 2), Bot and Abuse Mitigation (AppSec 3), and Stop Attacks in Real Time (AppSec 4).

⚠ Read this first

These labs run against your assigned Cloudflare-managed lab zone on cftraining.app. Every task is performed in the Cloudflare dashboard or from a terminal against your assigned domain. Fill in your lab slug in the form below before you start, so every command and address in this guide is ready to copy and run.

Typographical Conventions

This guide uses the following conventions to distinguish between user input, system output, and explanatory notes.

ConventionMeaningExample
BoldNames of selectable items in the interfaceClick Save to apply the change.
MonospaceText that you enter and code examplesEnter the command: whoami
Result textStep results, explanations, expected outputOperation completed successfully.
ItalicsContextual notes explaining "why" a task is necessaryThis keeps the service from restarting mid-test.
<in angle brackets>A per-student variable, filled in by the reader.Run dig A <lab-slug>.cftraining.app and press Enter.

How to Use This Guide

This combined guide contains all four Application Security classes: Part 1 (Cloudflare Setup), Part 2 (Build and Tune Effective Security Rules), Part 3 (Bot and Abuse Mitigation), and Part 4 (Stop Attacks in Real Time). Before each class, verify the assigned environment against that class's starting-state checklist. Work the activities in order because later activities use evidence, settings, or rules created earlier in the same class.

About the step numbers and checkboxes. Every instruction has a number (like 4.2.1) and a checkbox next to it. These are optional and are only a visual aid: as you work, you will flip back and forth between this guide and the dashboard, and it is easy to lose your place. Tick a box when you finish a step so you can glance back and instantly see where you were. Checking a box just highlights the step and remembers it in your browser; it does not submit anything, unlock anything, or report anywhere. You do not have to use them at all.
Start here ☛ Fill in your lab slug

This is the most important box in the guide. Enter the slug for your assigned lab domain. Your full domain is <lab-slug>.cftraining.app, so if your domain is happy-balancer.cftraining.app you enter happy-balancer. Every matching placeholder across the guide, including inside the copy blocks, is substituted with your value, so when you click Copy you get a real, ready-to-run command instead of a <placeholder>. This value saves to your browser only; nothing is sent anywhere.

Why These Labs

Why you are doing all of this: These classes follow the same evidence-first job a security engineer does when protecting a web application. First you make sure traffic is going through Cloudflare and is encrypted. Then you investigate attacks, write precise rules, and tune them to protect real customers. You apply that method to bots and API abuse, then use it to respond to an active application incident. Why notes explain the consequential decisions without interrupting every routine click.

AppSec 1 - Cloudflare Setup: From Start to Best Practices

Starting-State Check

Confirm the complete AppSec 1 baseline in your assigned zone. Do not repair an unknown starting state as part of the activity.

Prerequisites

ControlRequired starting state
Apex CNAME recordPoints to the assigned pod origin and is working and Proxied
www CNAME recordPoints to the assigned pod origin and is DNS Only, exposing the origin addresses
SSL/TLS encryption modeFull
Cloudflare Managed RulesetOff
OWASP CoreOff
Test route/.git/config is available for the managed-rules verification request
Baseline check: If any value does not match, stop and ask your instructor to reset the AppSec 1 environment.

Lab 1: DNS Investigation & Configuration

In this lab, you will diagnose and fix a DNS configuration that exposes an origin server and bypasses Cloudflare's security and performance services.

Scenario: You received a service ticket. Users report that www.<lab-slug>.cftraining.app is slow and displays a privacy warning, while the root site works normally.

1.1 Investigate with dig

Open a terminal and run both commands to compare the IPv4 response for www with the response for the root domain. Both names are configured as CNAME records to the pod load balancer, but dig A asks DNS for the resulting IPv4 addresses:

dig A www.<lab-slug>.cftraining.app
dig A <lab-slug>.cftraining.app

The www lookup may show a CNAME first and then return more than one A address. For this comparison, record one returned A address and the query time for each name. You can record every returned A address if you want, but one address is enough.

Record your DNS baseline. Enter at least one A address from each lookup, not the CNAME hostname. If you enter more than one address, separate the addresses with commas.

These values save in this browser and appear again when you compare the post-change results.
Why: The www CNAME is intentionally configured as DNS Only, so it resolves through the load balancer to origin addresses instead of Cloudflare addresses. That is why it is slow and shows a privacy warning: traffic is going straight to the origin and bypassing Cloudflare.
Note: If dig is unavailable, use https://dnschecker.org to compare the responses.

1.2 Fix the Exposed DNS Record

Open the Cloudflare dashboard and select your assigned account.

Go to Domains > Overview, then select your assigned lab domain.

In the domain's left sidebar, select DNS > Records.

Find the www record.

Change Proxy status from DNS Only to Proxied.

Return to the terminal and run the two lookups again. Compare www with your saved baseline of <WWW_INITIAL_IPS> at <WWW_INITIAL_QUERY_TIME>, and compare the root domain with <APEX_INITIAL_IPS> at <APEX_INITIAL_QUERY_TIME>. DNS resolvers across the Internet need time to receive the change, and cached responses can continue returning the previous address until they expire. If www still returns the origin address, wait a few minutes and run the lookup again. Continue after www returns Cloudflare addresses.

Verify: After the change, dig A www.<lab-slug>.cftraining.app returns Cloudflare anycast addresses (the same kind the root domain returns), not the origin IP, and the privacy warning is gone.

Lab 2: SSL/TLS Configuration

In this lab, you will observe how Full (Strict) protects the connection to the origin, then restore the lab's required state.

2.1 Induce the SSL Error

Open the Cloudflare dashboard.

Select your assigned account.

Go to Domains > Overview, then select your assigned lab domain.

In the domain's left sidebar, select SSL/TLS > Overview.

On the SSL/TLS overview, select Configure in the top-right.

On Encryption mode and origin connection settings, select Full (Strict).

Select Save at the bottom of the Encryption mode card.

Leave the dashboard open. In a new browser tab, open https://<lab-slug>.cftraining.app/ to test the site's HTTPS connection with the stricter origin mode.

Record the Cloudflare error code displayed on that page. The published lab currently expects 525: SSL handshake failed.

Why: The exact error depends on the origin's TLS state. A 525 means the TLS handshake failed. A 526 means the origin presented a certificate that Cloudflare could not validate in Full (Strict) mode. This shows that Full (Strict) refuses to trust an origin that cannot prove its identity.
Stop if the expected TLS fixture is missing

If the test does not produce 525 or 526, ask your instructor to verify the origin TLS fixture before continuing to Lab 3.

2.2 Restore the Lab State

Return to the dashboard tab. If needed, select SSL/TLS in the breadcrumb, then select Configure in the top-right.

On Encryption mode and origin connection settings, select Full.

Select Save at the bottom of the Encryption mode card.

Return to https://<lab-slug>.cftraining.app/, refresh the page, and confirm that the site loads.

Verify: The lab site loads again without the Cloudflare SSL error.
Note: This reset is specific to the lab. In production, install a valid origin certificate and retain Full (Strict).

Lab 3: Verifying Traffic in Analytics

3.1 Find the Error in Analytics

From the selected domain's left sidebar, select Analytics > HTTP Traffic.

Add an Edge status code filter for <SSL_ERROR_CODE>, the code you recorded in Lab 2. The published scenario expects 525; use 526 if that is what your lab origin produced.

Confirm that you can find the error generated in Lab 2.

Adjust the time range if the request is not immediately visible.

Verify: The Lab 2 error appears in HTTP Traffic once you filter by edge status code <SSL_ERROR_CODE>.

Lab 4: Enabling the Web Application Firewall (WAF)

4.1 Enable and Verify the Cloudflare Managed Ruleset

From the selected domain's left sidebar, select Security > Settings.

Filter the settings for Web application exploits.

In the Cloudflare managed ruleset row, select the toggle in the Status column. This opens Deploy managed ruleset.

Under Execution configuration, confirm that the execution scope is All incoming requests to your lab domain.

Under Ruleset configuration, leave Ruleset action set to Default and Ruleset status set to Default.

At the bottom of the page, select Deploy. Do not select Save as Draft.

Cloudflare returns you to Security > Security rules. In the Managed rules card, confirm that Cloudflare Managed Ruleset shows Action = Execute and Status = Active.

From a terminal, send a request that the Cloudflare Managed Ruleset is designed to block:

curl -i "https://<lab-slug>.cftraining.app/.git/config"

Confirm that the request returns 403 Forbidden, then record the Cloudflare Ray ID shown near the bottom of the response. This request creates the event you will inspect; you do not need to wait for background scanner traffic.

In the domain's left sidebar, select Security > Analytics, select the Events tab, and set the time range to Last 30 minutes.

Select Add filter, add Service equals Managed rules, add Action equals Block, then apply the filters.

Scroll to Sampled logs. If needed, select Edit columns and add ASN and User agent. Find the fresh GET /.git/config event by its timestamp or Ray ID, then expand it with the caret at the left of the row. Do not use existing Custom rules or Rate limiting rules events as proof that the managed ruleset worked.

Under Matched service, confirm Service = Managed rules, Action taken = Block, Ruleset = Cloudflare Managed Ruleset, and Rule = Version Control - Information Disclosure. Under Request details, confirm Method = GET and Path = /.git/config, then inspect the IP address, country, ASN, user agent, WAF attack scores, and bot score that describe the request.

Verify: A fresh GET /.git/config event shows Cloudflare Managed Ruleset, Version Control - Information Disclosure, and Block. If you cannot find a fresh event, run the command from step 4.1.8 again, wait briefly, and refresh Sampled logs.
Stop if fresh evidence is missing

If the repeated request does not return 403 or produce the matching fresh event, ask your instructor to verify the managed-rules fixture. Do not use an older or unrelated event as evidence.

4.2 Deploy OWASP Safely

In the domain's left sidebar, return to Security > Settings. Under Filter by groups, select Web application exploits.

In the OWASP Core row, select the toggle in the Status column. This opens Deploy managed ruleset for the Cloudflare OWASP Core Ruleset.

Under Execution configuration, confirm that the execution scope is All incoming requests to your lab domain.

Under Cloudflare OWASP Core Ruleset configuration, leave OWASP Anomaly Score Threshold set to Medium - 40 and higher and leave OWASP Paranoia Level set to PL1.

Change OWASP Action from Block to Log.

At the bottom of the page, select Deploy. Do not select Save as Draft.

Why: Starting in Log lets you inspect matches before enforcing a broader ruleset against production traffic. OWASP is score-based, so logging first shows you what it would catch before it can block a real customer.

4.3 Test the Logging Rule

Open a Linux terminal and run this request against your assigned domain:

curl --http1.0 -i -X DELETE -H "Accept;" -H "Content-Type: text/invalid" -H "Connection: keep-alive, close" "https://<lab-slug>.cftraining.app/owasp-test?file=http%3A%2F%2F127.0.0.1%2Fshell.php%3F&PHPSESSID=test&x=%0D%0ASet-Cookie%3Atest"

This request deliberately combines an unsupported method, conflicting and unusual headers, remote-file-inclusion input, header-injection input, and a session identifier. Nine PL1 OWASP rules contribute to a cumulative score of 40, which reaches the configured Medium - 40 and higher threshold. The Cloudflare Managed Ruleset above OWASP does not terminate this request first.

Confirm that curl shows 405 Method Not Allowed. That response comes from the lab origin because Log allows the request to continue. It does not mean the OWASP test failed.

Return to the dashboard. From the selected domain's left sidebar, select Security > Analytics, then select the Events tab. Select Clear all to remove the filters from section 4.1. Select Add filter, add Service equals Managed rules, add Action equals Log, then apply the filters.

Set the time range to Last 30 minutes.

In Sampled logs, expand the fresh event for path /owasp-test. Under Matched service, confirm Ruleset = Cloudflare OWASP Core Ruleset, Action taken = Log, the rule name includes Inbound Anomaly Score Exceeded, and OWASP score = 40.

Select Additional logs (9) to inspect the individual PL1 rules that contributed to the score. Under Request details, confirm Method = DELETE and Path = /owasp-test.

Why this test works: An individual OWASP signature normally adds only a few points. Cloudflare applies the configured OWASP action only after the cumulative score reaches the threshold, so a single SQL-injection signature did not produce the Log event this test requires.
Verify: The fresh DELETE /owasp-test event shows Cloudflare OWASP Core Ruleset, Inbound Anomaly Score Exceeded, OWASP score = 40, and Action taken = Log. Leave the OWASP action set to Log.
Stop if the OWASP evidence is incomplete

If the origin does not return 405 or the fresh event does not show all four required values, ask your instructor to verify the test fixture. Do not change OWASP to Block.

Final Retained State

ControlRequired retained state
Apex CNAME recordPoints to the assigned pod origin and is Proxied
www CNAME recordPoints to the assigned pod origin and is Proxied
SSL/TLS encryption modeFull for this lab environment
Cloudflare Managed RulesetActive, action Execute
OWASP CoreActive, PL1, threshold Medium - 40 and higher, action Log
Required evidenceFresh managed-rules Block event and fresh OWASP score-40 Log event
Class handoff: Keep this state for the AppSec 1 recap. Other AppSec classes start from independent baselines, so do not assume that these controls carry into another class.

Appendix: Lab Command Reference

The dig commands used in this class, for quick reference:

dig A www.<lab-slug>.cftraining.app
dig A <lab-slug>.cftraining.app

AppSec 2 - Build and Tune Effective Security Rules

You are a security engineer for Single Origin. In this lab, you will investigate scraping, build and test a precise mitigation, tune false positives, repair inherited rule hygiene, and protect the product API without disrupting legitimate users.

Use the Assess, Build, Test, Tune cycle throughout the lab.

ActivityModeDuration
1. Investigate scraping and correct a Skip-order conflictSolo18 minutes
2. Tune two false positivesSolo16 minutes
3. Repair inherited rule hygieneSynchronous6 minutes
4. Add product-API rate limitingSolo10 minutes

Starting-State Check

The required configuration is listed below. Confirm it in your assigned environment before starting Activity 1; the guide does not assume that the environment was reset correctly.
Where to verify: Open the dashboard, select your assigned account, go to Domains > Overview, and select <lab-slug>.cftraining.app. Use Security > Security rules for managed, Custom, and rate-limit rules; Security > Analytics > Traffic for recent scraper traffic; and account navigation Manage account > Configurations > Lists for site_monitor_ips.
Required configuration: The assigned domain has a proxied apex CNAME that points to the pod's shared load balancer. No managed ruleset is enabled. The account-level $site_monitor_ips list contains the trusted monitor address. Both inherited Skip rules have Log matching requests enabled. No scraper rule or product-API rate-limit rule exists.

The class starts with exactly these five inherited Custom rules in this order:

PositionRuleExpressionAction
1BYPASS - Site Monitor IPip.src in $site_monitor_ipsSkip all remaining Custom rules; Log matching requests enabled
2BLOCK - DE or Android 5ip.src.country eq "DE" or http.user_agent contains "Android 5"Block
3BLOCK - Site Monitor curlhttp.user_agent contains "curl"Block
4BYPASS - Main Customer Geoip.src.country eq "US"Skip all remaining Custom rules; Log matching requests enabled
5BLOCK - bot UAhttp.user_agent contains "bot"Block
Starting state: Recent scraper traffic is visible in Security Analytics, and no rate-limit rule targets /api/v1/products.
Required routes: GET /products, GET /api/v1/products, GET /checkout, and GET /health return 200 before a Cloudflare mitigation.
SignalLifecycle and rateRequired identity and result
SEC2-HUMANContinuous; four GET /products and two GET /api/v1/products requests per minuteCurrent browser user agent; US residential or equivalent browser source; 200
SEC2-SCRAPERContinuous; forty GET /api/v1/products requests per minutepython-requests/2.31.0; US source; static source IP and ASN
SEC2-VPNContinuous; four GET /checkout requests per minuteCurrent browser user agent; same ASN as the scraper from a different IP; 200 after tuning
SEC2-MONITORContinuous; two GET /api/v1/products requests per minutepython-requests/2.31.0; same ASN as the scraper; static allowlisted IP; 200 through the first Skip
SEC2-API-BURSTRecurring preconfigured schedule; fifteen GET /api/v1/products requests inside 60 seconds per runCurrent browser user agent; non-US source and ASN; crosses the learner-created threshold
Traffic lifecycle: The four continuous signals establish history before validation and keep running throughout class. SEC2-API-BURST runs on a non-overlapping preconfigured schedule that keeps recent evidence available. It has no classroom trigger; optional replay is non-gating.
Baseline check: If any required value or traffic signal is missing, stop and ask your instructor to reset the AppSec 2 environment.

Activity 1 (SEC2-1): Investigate scraping and correct a Skip-order conflict

Required outcome: Identify the scraper and correct a rule-order conflict so the new observation rule evaluates. Mode and duration: Solo, 18 minutes.
Recent scraper traffic targets /api/v1/products, and the five inherited Custom rules are in their baseline order.

1.1 Investigate Scraping

Open the Cloudflare dashboard and select your assigned account.

Go to Domains > Overview, then select <lab-slug>.cftraining.app.

In the domain's left sidebar, select Security > Analytics, select the Traffic tab, and set the time range to Last 30 minutes.

Select Add filter, create a filter where Path equals /api/v1/products, apply it, and find a fresh scraper request by its timestamp, source ASN, rate, and python-requests/2.31.0 user agent.

In Sampled logs, select the timestamp for that scraper request. Under Request details, record the value next to ASN as <SCRAPER_ASN>.

Why: The path, rate, network, and user agent give you several signals to compare, so you do not have to treat one broad attribute as proof by itself.

1.2 Create and Place the Log Rule

Select Security > Security rules.

Select Create rule, then select Custom rules. Enter LOG - Scraping ASN as the rule name, select Edit expression, and paste the copied expression:

ip.src.asnum eq <SCRAPER_ASN>

Under Then take action, set Choose action to Log. Under Execution order, leave Select order set to Last. At the bottom of the page, select Deploy, then select Deploy in the Deploy rule to production? confirmation dialog.

On the Custom rules list, select the linked event count in the Events column for BYPASS - Main Customer Geo.

In the filtered Events view, set the time range to Last 30 minutes.

In Sampled logs, start with the first row and select its timestamp. In the expanded Request details, check for Path /api/v1/products, User agent python-requests/2.31.0, and ASN AS<SCRAPER_ASN>. If that row does not have all three values, select the next timestamp and check again.

In the matching event's Matched service section, confirm that Action taken is Skip. The Rule ID filter came from BYPASS - Main Customer Geo, so this shows that the higher Skip handles the scraper request before the new last-place Log rule can evaluate it.

Select Security > Security rules. In the Custom rules table, select LOG - Scraping ASN to edit it.

Under Execution order, set Select order to Custom. Set Select which rule this will fire after to BLOCK - Site Monitor curl; this places the Log rule immediately above BYPASS - Main Customer Geo. Select Save.

Why: The geographic Skip stops evaluation before a lower rule can run. Moving the observation rule just above that Skip lets you collect the intended evidence without disturbing the trusted-service exception.

Verify

Select Security > Analytics, then select the Events tab and set Last 30 minutes.

Select Add filter for each filter, create and apply Action equals Log, then Service equals Custom rules, then Path equals /api/v1/products.

In Sampled logs, select the timestamp of a fresh event. Under Matched service, confirm that Action taken is Log. Confirm that the event timestamp is after you saved the rule's new position.

Stop if the observation rule has no fresh event

Keep the rule in Log and check the numeric ASN and placement with your instructor before Activity 2.

Activity 2 (SEC2-2): Tune two false positives

Required outcome: Block the scraper while preserving a legitimate VPN user and an allowlisted monitor that share some scraper attributes. Mode and duration: Solo, 16 minutes.
LOG - Scraping ASN is above BYPASS - Main Customer Geo. The scraper and legitimate VPN traffic share <SCRAPER_ASN>, while the scraper and monitor share python-requests/2.31.0.

2.1 Test the Broad Block

Select Security > Security rules.

Open Show all rule types and select Custom rules.

In the Custom rules table, select LOG - Scraping ASN to edit it.

Change its action to Block, then select Save.

In the domain's left sidebar, select Security > Analytics, select the Events tab, set Last 30 minutes, and apply Action equals Block, Service equals Custom rules, and Path equals /checkout.

In Sampled logs, select the timestamp of a fresh blocked legitimate VPN request.

Under Request details, confirm that ASN is AS<SCRAPER_ASN>, then enter User agent as <VPN_USER_AGENT> in the field below.

Why: The first Block proves that ASN alone is too broad because it catches the scraper and a legitimate VPN user on the same network.

2.2 Refine and Verify the Rule

Select Security > Security rules.

Open Show all rule types and select Custom rules.

In the Custom rules table, select LOG - Scraping ASN to edit it.

In the expression editor, replace the current expression by pasting the copied expression:

ip.src.asnum eq <SCRAPER_ASN> and http.user_agent eq "python-requests/2.31.0"

Rename the scraper rule BLOCK - Scraping ASN+UA, then select Save.

Scroll to the bottom of the page, and on the left select Manage account > Configurations, open the Lists tab, select site_monitor_ips, and enter its listed monitor IP as <SITE_MONITOR_IP> in the field below.

Return to Overview, then select Security > Security rules.

Open Show all rule types and select Custom rules.

In the Custom rules table, confirm that BYPASS - Site Monitor IP remains the first rule.

Select Security > Analytics, then select the Events tab.

Set Last 30 minutes, then apply Source IP equals <SITE_MONITOR_IP>.

Under Top events by source > Custom rules, confirm that BYPASS - Site Monitor IP appears.

In Sampled logs, select the timestamp of a fresh event from the monitor IP.

Under Matched service, confirm that Action taken is Skip.

Under Request details, confirm that Source IP matches the monitor IP, ASN is AS<SCRAPER_ASN>, and User agent is exactly python-requests/2.31.0.

At the top of the domain sidebar, select Back to Domains, then in the account sidebar expand Investigate under the Observe heading, select Trace, and select Begin Trace.

In URL, enter https://<lab-slug>.cftraining.app/checkout.

Confirm that HTTP Method is set to GET.

Under User Agent and Request Headers, select Add setting.

Enter User-Agent for Header key, then paste the copied value into Value:

<VPN_USER_AGENT>

Select Send Trace to simulate how the active Cloudflare configuration handles the tuned VPN checkout request.

In Trace Results, confirm that Match type is All configurations so rules that did not match remain visible.

Expand WAF Custom Rules.

Find BLOCK - Scraping ASN+UA and confirm that it shows No match.

Select View rule detail, confirm that "matched" is false, and confirm that the expression checks for http.user_agent eq "python-requests/2.31.0".

Close Rule detail, then under Request JSON confirm that User-Agent matches <VPN_USER_AGENT>.

Under TRACE END, confirm that HTTP Status Code is 200 OK.

Why: Trace cannot reproduce the generated VPN source ASN, so combine this simulation with the live blocked Event that confirms the shared ASN and the fresh 200 response after tuning.

Verify

Select Overview, then select Security > Analytics.

Select the Events tab and set the time range to Last 30 minutes.

Apply Action equals Block, Service equals Custom rules, and Path equals /api/v1/products.

Under Top events by source > Custom rules, confirm that BLOCK - Scraping ASN+UA appears, then in Sampled logs select a fresh event and confirm that Action taken is Block, ASN is AS<SCRAPER_ASN>, and User agent is python-requests/2.31.0.

Select the Traffic tab and set the time range to Last 30 minutes.

Apply Source IP equals <SITE_MONITOR_IP> and Edge status code equals 200, then select a fresh request and confirm that Path is /api/v1/products, Source IP is <SITE_MONITOR_IP>, and User agent is python-requests/2.31.0.

Remove all filters, apply Path equals /products and Edge status code equals 200, then select a fresh request and confirm that Path is /products.

Remove all filters, apply Path equals /checkout, Source ASN equals <SCRAPER_ASN>, Source user agent equals <VPN_USER_AGENT>, and Edge status code equals 200, then select a fresh request and confirm that its details match all four filters.

Select the Events tab, remove all filters, apply Source IP equals <SITE_MONITOR_IP>, confirm under Top events by source > Custom rules that BYPASS - Site Monitor IP appears, then select a fresh event and confirm that Action taken is Skip.

Confirm that the blocked VPN Event showed ASN AS<SCRAPER_ASN> and User agent <VPN_USER_AGENT>, the fresh /checkout Traffic request with ASN AS<SCRAPER_ASN> and User agent <VPN_USER_AGENT> returned 200, Trace showed BLOCK - Scraping ASN+UA as No match for <VPN_USER_AGENT>, the blocked scraper Event showed ASN AS<SCRAPER_ASN> and User agent python-requests/2.31.0, and the monitor Event from <SITE_MONITOR_IP> showed Action taken Skip; this confirms that the tuned rule blocks the scraper signature while preserving the VPN and monitor.

Stop if legitimate traffic does not recover

If scraper traffic is not blocked or VPN, monitor, or human traffic does not return 200, keep the rule available for correction before continuing.

Activity 3 (SEC2-3): Repair inherited rule hygiene

Required outcome: Repair faulty Boolean logic, remove an obsolete rule, and verify that rules run from the narrowest exception to the broadest control. Mode and duration: Synchronous, 6 minutes.
Delivery dependency: validation traffic

This activity depends on delivery-provided DE Android 5, DE without Android 5, and non-DE Android 5 requests. The guide does not provide or invent a trigger. Confirm that your instructor has identified fresh validation traffic before changing the rule. If it is unavailable, stop and leave the inherited rules unchanged.

3.1 Repair the Inherited Rules

Select Security > Security rules, open Show all rule types, select Custom rules, and in the Custom rules table select BLOCK - DE or Android 5 to edit it.

Select Edit expression, then replace the current expression by pasting the copied expression:

ip.src.country eq "DE" and http.user_agent contains "Android 5"

In Rule name, enter this exact value:

BLOCK - DE Android 5

Select Save, then on the Custom rules list select BLOCK - Site Monitor curl to edit it, under Status select Disabled, select Save, and confirm that the rule shows Disabled on the list.

On the Custom rules list, confirm that the rules appear in this exact order with these statuses:

PositionRuleStatus
1BYPASS - Site Monitor IPActive
2BLOCK - DE Android 5Active
3BLOCK - Site Monitor curlDisabled
4BLOCK - Scraping ASN+UAActive
5BYPASS - Main Customer GeoActive
6BLOCK - bot UAActive

Select Security > Analytics.

Select the Traffic tab and set the time range to Last 30 minutes.

Select Clear all if any filters remain from an earlier task.

Select Show more top statistics, then under Source user agents select the four-arrow Expand to see more and search button, enter Android 5.0.2 in Search, hover over the single result, and select the Filter icon at the right.

Under Countries, hover over Germany, then select Filter.

Above, under Number of requests mitigated and served by Cloudflare, confirm that all filtered requests were blocked or challenged by Cloudflare's security tools by confirming that Mitigated by Cloudflare equals Total.

Under Edge status codes, confirm that all filtered requests are 403 Forbidden.

In Sampled logs, select a fresh timestamp, then under Edge response confirm that Mitigation is Block by Custom rules and Edge status code is 403 - Forbidden.

Under Request details, confirm that Country is Germany and User agent contains Android 5.0.2.

Scroll up and select Clear all under the filters, then under Source user agents replace any current Search value with Chrome/133.0.0.0, hover over the single result, and select the Filter icon at the right.

Under Countries, hover over Germany, then select Filter.

Above, under Number of requests mitigated and served by Cloudflare, confirm that all filtered requests were served by the origin by confirming that Served by origin equals Total.

Under Edge status codes, confirm that all filtered requests are 200 OK.

In Sampled logs, select a fresh timestamp, then confirm that Mitigation is Not mitigated, Edge status code is 200 - OK, Country is Germany, and User agent contains Chrome/133.0.0.0.

Select Clear all, then under Source user agents replace any current Search value with Android 5.0.2, hover over the single result, and select the Filter icon at the right.

Under Countries, hover over United States, then select Filter.

Above, under Number of requests mitigated and served by Cloudflare, confirm that all filtered requests were served by the origin by confirming that Served by origin equals Total.

Under Edge status codes, confirm that all filtered requests are 200 OK.

In Sampled logs, select a fresh timestamp, then confirm that Mitigation is Not mitigated, Edge status code is 200 - OK, Country is United States, and User agent contains Android 5.0.2.

Why: The AND expression blocks only the intended overlap, and disabling the obsolete curl rule prevents an old control from conflicting with the allowlisted monitor.
Verify: The three filtered samples prove that the corrected AND expression blocks DE Android 5 traffic while allowing DE traffic without Android 5 and non-DE Android 5 traffic.

Activity 4 (SEC2-4): Add product-API rate limiting

Required outcome: Rate-limit bursts to the products API without disrupting ordinary, VPN, monitor, or already-blocked scraper traffic. Mode and duration: Solo, 10 minutes.

4.1 Rate-Limit the Product API

Select Security > Security rules.

On the Rate limiting rules card, select Create rule, enter RATE-LIMIT - Product API as the rule name, select Edit expression, and paste the copied expression:

http.request.uri.path eq "/api/v1/products"

Under With the same characteristics, leave Field set to IP so Cloudflare counts requests separately for each source IP.

Under When rate exceeds, enter 10 for Requests and select 2 minutes for Period.

Under Then take action, select Non-Interactive Challenge.

Under With the following behavior, leave Non-Interactive Challenge for the selected duration selected and choose 5 minutes for Duration.

Under Status, leave Active selected.

Select Deploy, select Deploy in the Deploy rule to production? confirmation dialog, then wait for the next scheduled SEC2-API-BURST run against https://<lab-slug>.cftraining.app/api/v1/products while the ordinary, VPN, monitor, and scraper traffic continues.

Return to Security > Analytics, select the Events tab, set Last 30 minutes, and apply Service equals Rate limiting rules and Path equals /api/v1/products.

In Sampled logs, select the timestamp of a fresh event from the latest scheduled burst, under Matched service confirm that Service is Rate limiting rules and Action taken is Non-Interactive Challenge, and under Request details confirm that Path is /api/v1/products, which distinguishes this event from existing Custom-rule Block events.

Why: The exact path limits this control to the product API, and the threshold catches a burst without treating every API request as abuse.
Verify: The next scheduled burst crosses the threshold; human, VPN, and monitor traffic remains below it; scraper traffic remains blocked by BLOCK - Scraping ASN+UA; and Security Events distinguishes the Custom-rule Block from the rate-limit action. Keep the learner-created rules active for the class recap.

Final Retained State

Class handoff: Confirm that BLOCK - Scraping ASN+UA, the corrected BLOCK - DE Android 5, and RATE-LIMIT - Product API remain active; BYPASS - Site Monitor IP remains first; and BLOCK - Site Monitor curl is disabled. Keep the learner-created rules through recap. The AppSec 2 class reset restores the five inherited rules in their original order, $site_monitor_ips membership, route data, and absence of learner-created rules. It preserves labeled traffic visibility, all four continuous actors, and the recurring SEC2-API-BURST schedule without depending on an actor restart.

AppSec 3 - Bot and Abuse Mitigation

You are protecting Single Origin's specialty-coffee services from automated abuse. In this lab, you will establish bot protections, manage AI crawler treatment, protect a browser login, investigate credential stuffing, and add behavior-based API defenses without blocking legitimate clients.

ActivityModeDuration
1. Establish Baseline Bot ProtectionsSynchronous6 minutes
2. Manage AI Crawler TreatmentSynchronous8 minutes
3. Apply Maximize Security to the Login PageSynchronous5 minutes
4. Investigate, Mitigate, and Tune Credential StuffingSynchronous27 minutes
5. Rate-Limit Leaked Credentials by JA4Synchronous10 minutes
6. Protect the Orders APISynchronous9 minutes
Evidence source: The approved continuous external traffic generator may supply every sampled request and security event used in this lab.
Continuous signals: SEC3-STUFFER: Six form-encoded POST /admin and six JSON POST /api/mobile/login requests per minute with CF_EXPOSED_USERNAME and CF_EXPOSED_PASSWORD. SEC3-MOBILE-LOGIN: Two JSON mobile logins per minute. SEC3-MOBILE-ORDERS: Two GET /api/v1/orders requests per minute. SEC3-ORDER-SCRAPER: Twenty GET /api/v1/orders requests per minute.

Activity 1 (SEC3-1): Establish Baseline Bot Protections

Required outcome: Inspect the existing generator signals, confirm Leaked credentials detection, and enable Precursor in Minimize Friction. Mode and duration: Synchronous, 6 minutes.

1.1 Inspect the Generator Signals

Open the Cloudflare dashboard and select your assigned account.

Go to Domains > Overview, then select <lab-slug>.cftraining.app.

Select Security > Analytics, select the Traffic tab, and set the time range to Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter, set Field to Path, set Operator to equals, enter /admin for Value, and apply the filter.

In Sampled logs, select a fresh timestamp and expand the row.

Under Request analyses, confirm that the request has a bot score and a JA4 fingerprint.

Under Request details, confirm that Method is POST, Path is /admin, and Edge status code is 401.

Select Clear all, then select Add filter and apply Path equals /api/v1/orders.

Under Source user agents and Edge status codes, compare the relative request volume and response outcomes for the mobile-order and order-scraper traffic.

In Sampled logs, expand a fresh row for each client and confirm that the scraper has bot score 1, the mobile client has bot score 7, and both have the same JA4 while their user agents and edge status codes differ.

1.2 Enable Precursor

Select Security > Settings, then select Fraud under Filter by groups.

Confirm that Leaked credentials detection is On.

Select Precursor under Filter by groups.

Enable Precursor and select Minimize Friction.

Confirm that the Precursor row reports On and Minimize Friction.

Why: The continuous generator already provides the traffic comparison, so this exercise does not wait for manually generated traffic or analytics propagation.
Verify: Leaked credentials detection remains On, Precursor is On in Minimize Friction, and the existing generator signals were inspected. If an expected signal or setting is absent, record the observed mismatch, continue to the next exercise, and include it in the end-of-lab results.

Activity 2 (SEC3-2): Manage AI Crawler Treatment

Required outcome: Apply Single Origin's crawler policy by allowing PerplexityBot for AI search and discovery and blocking GPTBot because model-training collection is not essential to the business. Mode and duration: Synchronous, 8 minutes.

2.1 Allow and Block AI Crawlers

In the selected domain's left sidebar, select AI Crawl Control > Security.

Leave Show inactive crawlers selected so catalog entries with zero request history remain visible.

Find the PerplexityBot row even if its request history is zero.

In the Block Crawler column for PerplexityBot, turn the switch off if it is on.

Confirm that the Block Crawler switch for PerplexityBot is off, which leaves this AI Search crawler allowed.

Find the GPTBot row even if its request history is zero.

In the Block Crawler column for GPTBot, turn the switch on.

Confirm that the Block Crawler switch for GPTBot is on.

Confirm that unrelated crawler actions remain unchanged.

Why: Single Origin values PerplexityBot's search and discovery traffic but does not need GPTBot's model-training collection, and zero request history does not prevent changing either crawler's catalog treatment.
Verify: The Block Crawler switch is off for PerplexityBot and on for GPTBot. If either crawler or switch is unavailable, record the observed mismatch, continue to the next exercise, and include it in the end-of-lab results.

Activity 3 (SEC3-3): Apply Maximize Security to the Login Page

Required outcome: Apply Maximize Security to the browser-facing /login path without affecting the API-only login or orders paths. Mode and duration: Synchronous, 5 minutes.

3.1 Create the Maximize Login Rule

Select Security > Settings.

Select Precursor under Filter by groups.

Select Precursor Rules, then select Create rule.

Enter MAXIMIZE - Login as the rule name.

Select Edit expression and paste the copied expression:

http.request.uri.path eq "/login"

Select Maximize Security as the Precursor mode.

Under Status, leave Active selected.

Select Deploy. If the dashboard displays the Deploy rule to production? confirmation dialog, select Deploy again.

Leave the dashboard open, open https://<lab-slug>.cftraining.app/login in a fresh private browser session without a cf_clearance cookie, then return to the dashboard.

Select Security > Analytics, select the Events tab, and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Path equals /login.

Select Add filter and apply Action equals Precursor Interstitial Page Issued.

In Sampled logs, select a fresh timestamp and expand the row.

Under Matched service, confirm that the action is Precursor Interstitial Page Issued.

Under Request details, confirm that Path is /login.

Why: The exact browser-login path receives the stronger mode while /api/mobile/login and /api/v1/orders remain outside the rule.
Verify: MAXIMIZE - Login is active for exact /login, and a fresh event shows Precursor Interstitial Page Issued when matching traffic is available. If the event is absent, record the observed mismatch, continue to the next exercise, and include it in the end-of-lab results.

Activity 4 (SEC3-4): Investigate, Mitigate, and Tune Credential Stuffing

Required outcome: Identify credential stuffing from multiple signals, diagnose rule order, compare the broad rule's actual effect on abusive and legitimate API traffic, and narrow the interactive challenge to the admin route. Mode and duration: Synchronous, 27 minutes.

4.1 Investigate Credential Stuffing

Select Security > Analytics, select the Traffic tab, and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Path equals /admin.

Select Add filter and apply Edge status code equals 401.

Use the request chart and filtered total to confirm that repeated failed /admin traffic is higher-volume than the legitimate two-request-per-minute mobile-login fixture declared at the start of the lab.

In Sampled logs, select a fresh timestamp and expand the row.

Under Request analyses, confirm that Bot score is 1, record the JA4 fingerprint, and record the leaked-credentials detection result. Do not claim that the field matched if the dashboard reports None.

Under Request details, confirm that Method is POST, Path is /admin, and record the source IP and timestamp.

Expand another fresh /admin row and confirm under Request analyses and Request details that the JA4 is stable while the source IP changes.

Why: Within one data-center counter scope, the stable JA4 connects the requests even while the attacker rotates source IPs. Traffic reaching another data center gets a separate count, and the other signals show that the pattern is credential stuffing rather than one unusual login.

4.2 Mitigate and Test

Select Security > Security rules.

Select Create rule, then select Custom rules.

Enter CHALLENGE - Low Bot Score Login as the rule name.

Select Edit expression and paste the copied expression:

cf.bot_management.score eq 1 and http.request.uri.path in {"/admin" "/api/mobile/login"}

Under Then take action, select Managed Challenge.

Under Execution order, leave Select order set to Last.

Under Status, leave Active selected.

Select Deploy. If the dashboard displays the Deploy rule to production? confirmation dialog, select Deploy again.

Select Security > Analytics, select the Events tab, and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Action equals Skip.

Select Add filter and apply Service equals Custom rules.

Select Add filter and apply Path equals /admin.

In Sampled logs, select a fresh timestamp and expand the row.

Under Matched service, confirm that Service is Custom rules, Action taken is Skip, and Rule is BYPASS - Main Customer Geo.

Under Request analyses, confirm that Bot score is 1 and note the JA4 fingerprint.

Under Request details, confirm that Path is /admin and note the source IP.

Select Security > Security rules, open Show all rule types, and select Custom rules.

Select CHALLENGE - Low Bot Score Login to edit the existing rule.

Under Execution order, set Select order to Custom.

Set Select which rule this will fire after to BYPASS - Site Monitor IP.

Select Save.

Select Security > Analytics, select the Events tab, and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Path equals /api/mobile/login.

Select Add filter and apply Service equals Custom rules.

Select Add filter and apply Action equals Managed Challenge.

In Sampled logs, select a fresh timestamp and expand the row.

Under Matched service, confirm that Service is Custom rules, Action taken is Managed Challenge, and Rule is CHALLENGE - Low Bot Score Login.

Under Request analyses, confirm that Bot score is 1 and note the credential stuffer's JA4 fingerprint.

Under Request details, confirm that Path is /api/mobile/login and User agent is curl/8.10.1. This event is abusive API traffic, not the legitimate mobile client.

Select the Traffic tab, keep Path equals /api/mobile/login, add Source user agent equals SingleOrigin-Mobile/3.1.0 (iOS 18.2; iPhone15,3) and Edge status code equals 200, then expand a fresh row and confirm that its bot score is 7.

4.3 Tune the Rule

Select Security > Security rules, open Show all rule types, and select Custom rules.

Select CHALLENGE - Low Bot Score Login to edit the existing rule.

Select Edit expression and replace the expression with the copied expression:

cf.bot_management.score eq 1 and http.request.uri.path eq "/admin"

Rename the rule CHALLENGE - Admin Credential Stuffing.

Select Save.

Select Security > Analytics, select the Events tab, and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Path equals /admin.

Select Add filter and apply Service equals Custom rules.

Select Add filter and apply Action equals Managed Challenge.

In Sampled logs, expand a fresh row and confirm under Matched service that Rule is CHALLENGE - Admin Credential Stuffing and Action taken is Managed Challenge.

Select the Traffic tab and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Path equals /api/mobile/login.

Select Add filter and apply Edge status code equals 200.

In Sampled logs, expand a fresh row and confirm under Request details that User agent begins with SingleOrigin-Mobile/3.1.0 and Path is /api/mobile/login.

Why: Managed Challenge is an interactive browser control, so narrowing the rule to the attacked admin route avoids applying it to the API login. The current legitimate mobile client already falls outside the score-1 condition, but route scope makes the policy boundary explicit and leaves API abuse for the rate-limit exercise.
Verify: Events prove that CHALLENGE - Admin Credential Stuffing challenges /admin, the earlier broad-rule event on /api/mobile/login belongs to curl/8.10.1, and Traffic shows the score-7 generated mobile login returning 200. If any result is absent, record the observed mismatch, continue to the next exercise, and include it in the end-of-lab results.

Activity 5 (SEC3-5): Rate-Limit Leaked Credentials by JA4

Required outcome: Block repeated leaked-credential attempts from one client even when its source IP changes. Mode and duration: Synchronous, 10 minutes.

5.1 Rate-Limit Leaked Credential Attempts

Select Security > Security rules.

Select Create rule, then select Rate limiting rules.

Enter ARL - Leaked Credential Attempts as the rule name.

Select Edit expression and paste the copied expression:

(cf.waf.credential_check.username_and_password_leaked)

Under With the same characteristics, set Field to JA4.

Under When rate exceeds, enter 3 for Requests.

Under When rate exceeds, select 1 minute for Period.

Under Then take action, select Block.

Under With the following behavior, select Throttle requests over the maximum configured rate.

Under Status, leave Active selected.

Select Deploy. If the dashboard displays the Deploy rule to production? confirmation dialog, select Deploy again.

Why: Cloudflare implicitly includes cf.colo.id in rate-limit counting, so the JA4 counters are independent per data center even though cf.colo.id is not added as a characteristic.

5.2 Verify the Leaked-Credential Rate Limit

Select Security > Analytics, select the Traffic tab, and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Path equals /api/mobile/login.

In Sampled logs, expand two fresh credential-stuffer rows.

Under Request analyses, confirm that both rows have the same JA4 fingerprint.

Under Request analyses, record the leaked-credentials detection result. If it reports None, record an evidence mismatch because the rate-limit expression will not match that request.

Under Request details, confirm that the rows have different source IPs and Path /api/mobile/login.

Select the Events tab and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Service equals Rate limiting rules.

Select Add filter and apply Action equals Block.

Select Add filter and apply Path equals /api/mobile/login.

In Sampled logs, select a fresh timestamp and expand the row.

Under Matched service, confirm that Service is Rate limiting rules, Action taken is Block, and Rule is ARL - Leaked Credential Attempts.

Under Request analyses, confirm that the JA4 fingerprint matches the changing-IP traffic sample.

Under Request details, confirm that Method is POST and Path is /api/mobile/login.

Select the Traffic tab, set Last 30 minutes, and select Clear all if any filters are present.

Select Add filter and apply Path equals /api/mobile/login.

Select Add filter and apply Source user agent equals SingleOrigin-Mobile/3.1.0 (iOS 18.2; iPhone15,3).

Select Add filter and apply Edge status code equals 200.

In Sampled logs, expand a fresh row and confirm that its path, full user agent, and edge status code match all three filters.

Verify: Changing source IPs share the stable JA4, a fresh Rate limiting rules event shows Block for ARL - Leaked Credential Attempts, and Traffic shows the legitimate mobile-login client still returning 200. If this evidence is absent, record the observed mismatch, continue to the next exercise, and include it in the end-of-lab results.

Activity 6 (SEC3-6): Protect the Orders API

Required outcome: Block high-rate automated scraping without blocking a low-rate legitimate API client that shares its JA4 but has a different bot score and user agent. Mode and duration: Synchronous, 9 minutes.

6.1 Rate-Limit API Scraping

Select Security > Security rules.

Select Create rule, then select Rate limiting rules.

Enter ARL - API Scraping as the rule name.

Select Edit expression and paste the copied expression:

(http.request.uri.path eq "/api/v1/orders" and cf.bot_management.score eq 1 and http.user_agent eq "curl/8.10.1")

Under With the same characteristics, set Field to JA4.

Under When rate exceeds, enter 10 for Requests.

Under When rate exceeds, select 1 minute for Period.

Under Then take action, select Block.

Under With the following behavior, select Throttle requests over the maximum configured rate.

Under Status, leave Active selected.

Select Deploy. If the dashboard displays the Deploy rule to production? confirmation dialog, select Deploy again.

Why: The exact path, score-1 condition, and scraper user agent select the observed high-volume failed scraper. The legitimate mobile client shares its JA4 but scores 7 and uses a different user agent, while the implicit cf.colo.id scope keeps counters independent per data center.

6.2 Verify the API-Scraping Rate Limit

Select Security > Analytics, select the Traffic tab, and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Path equals /api/v1/orders.

Under Source user agents and Edge status codes, compare the relative request volume for SingleOrigin-Mobile/3.1.0 (iOS 18.2; iPhone15,3) returning 200 and curl/8.10.1 returning 401.

In Sampled logs, expand a fresh mobile-order row and a fresh order-scraper row.

Under Request analyses, confirm that the scraper has Bot score 1, the mobile client has Bot score 7, and both rows have the same JA4 fingerprint.

Under Request details, confirm that the mobile row uses SingleOrigin-Mobile/3.1.0 (iOS 18.2; iPhone15,3) and returns 200.

Under Request details, confirm that the scraper row uses curl/8.10.1 and returns 401.

Select the Events tab and set Last 30 minutes.

Select Clear all if any filters are present.

Select Add filter and apply Service equals Rate limiting rules.

Select Add filter and apply Action equals Block.

Select Add filter and apply Path equals /api/v1/orders.

In Sampled logs, select a fresh timestamp and expand the row.

Under Matched service, confirm that Service is Rate limiting rules, Action taken is Block, and Rule is ARL - API Scraping.

Under Request analyses, confirm that Bot score is 1 and the JA4 fingerprint matches the high-rate scraper.

Under Request details, confirm that Method is GET, Path is /api/v1/orders, and User agent is curl/8.10.1.

Verify: Events prove that ARL - API Scraping blocks the high-rate score-1 curl/8.10.1 scraper. Traffic shows the score-7 full mobile user agent returning 200 even though it shares the scraper's JA4. If either result is absent, record the observed mismatch and include it in the end-of-lab results.

Final Retained State

Class handoff: Leaked credentials detection remains On; Precursor remains On in Minimize Friction; PerplexityBot remains Allow; GPTBot remains Block; and MAXIMIZE - Login, CHALLENGE - Admin Credential Stuffing, ARL - Leaked Credential Attempts, and ARL - API Scraping remain Active.

7. End-of-Lab Results

Record each activity as Success when its final verification matched the guide or as Mismatch observed when it did not.

For every mismatch, record the time range, filters, expected value, observed value, and affected rule or setting.

Include missing sampled evidence as a result rather than treating it as proof that the configuration failed.

Submit one end-of-lab results summary covering all six activities and retain the final settings and rules for the class recap.

AppSec 4 - Stop Attacks in Real Time

Single Origin's search service is under application-layer pressure. You will evaluate a broad emergency control, diagnose why an inherited rate limit acts too late, derive a safer response from measured traffic, and prove that the application and legitimate clients recover.
Estimated activity time: 52 minutes.
ActivityModeGoalTime
1. Evaluate Under Attack ModeInstructor-driven, optional follow-alongObserve the reach of a broad emergency control and return it to Off4 minutes
2. Investigate the Search IncidentSoloCorrelate attack traffic, rate-limit responses, and application 503 responses18 minutes
3. Derive and Deploy a Safer Rate LimitSoloRecord a decision, then update the inherited rule18 minutes
4. Evaluate the Mitigation and CloseSynchronousProve activation, recovery, attack reduction, and legitimate-client safety12 minutes

Starting-State Check

Prerequisites

Use only the supplied scenario

Do not create extra traffic or add another security rule. The supplied traffic and the inherited rule are part of the incident evidence.

Search route and pressure model

RequestRequired behavior
GET /search?q=<term>&complex=0Shopper, monitor, and threshold-test traffic; does not consume a pressure slot
GET /search?q=<term>&complex=1Attack traffic; holds one of four per-environment work slots for 750 milliseconds
A fifth simultaneous complex requestReturns 503 immediately; capacity stays isolated between learner environments

Dashboard orientation

  1. Open the Cloudflare dashboard.
  2. Select your assigned account.
  3. Select Domains, select Overview, and select <lab-slug>.cftraining.app.
  4. Use the selected domain's left navigation for the rest of the lab.
DestinationNavigationPurpose
Domain OverviewOverviewBriefly evaluate Under Attack Mode
Security AnalyticsSecurity > Analytics > TrafficCompare request paths, rates, sources, and response codes
Security EventsSecurity > Analytics > EventsConfirm which rule and action handled a request
Request rate analysisSecurity > Analytics > Traffic, then Request rate analysisCompare legitimate and attack source-IP rates
Advanced rate limitingSecurity > Security rules > Advanced rate limiting rulesInspect and edit the inherited rule

Required starting state

Control or trafficRequired starting state
ZoneThe assigned SEC-attacks environment
Apex DNS recordCNAME to the pod's shared load balancer; Proxied
Advanced Rate Limiting, Request rate analysis, Security Analytics, and EventsAvailable
Bot Fight ModeOff
Under Attack ModeOff
Inherited ruleOne enabled Advanced Rate Limiting rule named Block high rate of requests
Rule match and counterExact /search, counted by source IP with Cloudflare's implicit per-data-center scope
Starting trigger200 requests per 60 seconds
Starting action and timeoutBlock with 60-second mitigation
SEC4-SHOPPERContinuous GET /search?q=espresso&complex=0 returning 200; 30 to 35 total requests per minute from shared egress, paced at six per minute per ordinary client
SEC4-MONITORContinuous one GET /search?q=healthcheck&complex=0 per minute from SingleOrigin-HealthMonitor/1.0 (+https://singleorigin.example/monitoring), from a stable source separate from the attacker, returning 200
SEC4-ATTACKContinuous eight GET /search?q=connect-load&complex=1 requests per second from a stable environment-specific source, with a pre-change mix of 200, rate-limit, and tenant-scoped 503 responses
SEC4-TESTRecurring preconfigured schedule; 70 numbered GET /search?q=threshold-test&complex=0 requests inside 60 seconds per run from one controlled client
Traffic lifecycle: Shopper, attack, and monitor traffic must have at least 24 hours of usable history before instructor validation and remain active through delivery. The SEC4-TEST schedule prevents overlap and keeps a recent numbered run available. It has no classroom trigger; optional replay is non-gating.
Baseline gate: If any item does not match, stop and ask the instructor to restore the AppSec 4 starting state. Do not repair the environment yourself.
Evidence rules: Traffic is sampled and shows patterns. Events proves the Cloudflare rule and action. The lab-provided complete application-response output proves the 60-second recovery window. Managed Challenge has no universal response status, and counter updates may delay activation until after request 61.

Activity 1 (SEC4-1): Evaluate Under Attack Mode

Mode: Instructor-driven with optional follow-along. Time: 4 minutes. Required outcome: Observe the visitor effect and return Under Attack Mode to Off.
  1. From the selected domain, stay on Overview.
  2. Find the Under Attack Mode card and turn its switch On.
  3. In a private window, open https://<lab-slug>.cftraining.app/search.
  4. Record the visitor effect you actually receive. Do not assume every client receives the same screen or status.
  5. Return to Overview and turn Under Attack Mode Off.
  6. Refresh the private browser and confirm the broad visitor effect no longer applies.
Why: Under Attack Mode acts across the zone rather than only on the expensive search behavior. The short test demonstrates that tradeoff without making it the permanent response.
Return gate: Under Attack Mode is Off. Stop if you cannot confirm that state.

Activity 2 (SEC4-2): Investigate the Search Incident

Mode: Solo. Time: 18 minutes. Required outcome: Connect the attack, rate-limit responses, and 503 responses to the inherited rule's late trigger and short mitigation.

2.1 Isolate the affected route

  1. Go to Security > Analytics and select Traffic.
  2. Set the time range to include current lab traffic.
  3. Select Add filter and set Path equals /search.
  4. Compare Source IPs, Source user agents, Edge status codes, and request timing.
  5. Confirm shoppers and the monitor send simple requests at low rates.
  6. Identify the concentrated complex traffic at approximately eight searches per second.
  7. Confirm the pre-change mix includes 200, rate-limit, and tenant-scoped 503 responses.
Why: The shared path identifies where the incident happens, while request behavior, rate, and response codes separate ordinary use from application pressure.

2.2 Inspect the inherited rule

  1. Go to Security > Security rules.
  2. Find Advanced rate limiting rules and open Block high rate of requests.
  3. Confirm exact /search, source-IP counting, 200 requests per 60 seconds, Block, and 60-second mitigation. Do not change the rule.
  4. Review the supplied readiness evidence and confirm Cloudflare's implicit per-data-center counter scope; this scope is not shown as a learner-editable rule characteristic.
  5. Return to Security > Analytics > Events.
  6. Filter to exact path /search and find fresh events for Block high rate of requests.
  7. Confirm the inherited rule really activates and uses Block.
Why: The rule waits long enough for expensive requests to consume the application's limited work slots, then stops mitigation after 60 seconds and lets the pressure return.
Return gate: You can explain why a firing rule can coexist with recurring 503 responses. No configuration changed.

Activity 3 (SEC4-3): Derive and Deploy a Safer Rate Limit

Mode: Solo. Time: 18 minutes. Required outcome: Record your own decision before revealing and deploying the scenario target.

3.1 Record your decision first

  1. Go to Security > Analytics > Traffic and keep Path equals /search.
  2. Select Request rate analysis.
  3. Compare the attack outlier with legitimate source-IP buckets.
  4. Confirm the busiest legitimate bucket peaks no higher than 35 requests per minute.
  5. Record the threshold, action, and mitigation timeout you would choose and why. Stop before the supplied target until all three choices are recorded.

3.2 Compare with the scenario target

SettingTarget
Match and counterKeep exact /search, source IP, and implicit per-data-center scope
Threshold60 requests per 60 seconds
ActionManaged Challenge
Mitigation timeout600 seconds

3.3 Edit the existing rule

  1. Go to Security > Security rules > Advanced rate limiting rules and open Block high rate of requests.
  2. Keep exact /search, source-IP counting, the 60-second period, and existing position.
  3. Change Requests from 200 to 60.
  4. Change Action from Block to Managed Challenge.
  5. Change mitigation from 60 seconds to 600 seconds.
  6. Select Deploy.
  7. Reopen the rule and confirm every saved value.
Why: This is one measured correction to a known rule, not another overlapping control.
Return gate: The enabled rule keeps exact /search and source-IP counting, with 60 requests per 60 seconds, Managed Challenge, and 600-second mitigation.

Activity 4 (SEC4-4): Evaluate the Mitigation and Close

Mode: Synchronous. Time: 12 minutes. Required outcome: Close the enforcement, recovery, and legitimate-client gates.

4.1 Prove activation

  1. Inspect the next scheduled SEC4-TEST run and let all 70 numbered simple searches finish inside its 60-second window. You don't need a classroom trigger. An optional replay can help troubleshoot, but it doesn't replace the required scheduled-run evidence.
  2. Record the first numbered result where mitigation actually appears. Do not force the answer to request 61.
  1. Go to Security > Analytics > Events.
  2. Filter exact path /search and rule Block high rate of requests.
  3. Expand a fresh threshold-test event and confirm Managed Challenge, not Block.
  4. Do not require one specific response status from the controlled client.

4.2 Prove application recovery

  1. Use the lab-provided complete application-response output for the recovery window.
  2. Observe a full 60-second window and confirm that no complete application response returns 503 during that window.
  3. In Security > Analytics > Traffic, compare the sampled attack trend before and after deployment.
  4. Confirm the concentrated attack traffic drops after mitigation.

4.3 Protect legitimate clients

Exclude the known attack and SEC4-TEST clients, then verify each legitimate client separately.

ClientRequired result
ShopperFresh simple /search request returns 200
Health monitorFresh request from SingleOrigin-HealthMonitor/1.0 (+https://singleorigin.example/monitoring) returns 200
Ordinary browserhttps://<lab-slug>.cftraining.app/search works normally
Why: A challenge Event proves enforcement, but it does not prove recovery or legitimate-client safety. Each result closes a different incident-response question.
Final activity gate: Record the actual activation request, matching Managed Challenge Event, 60 seconds without a complete 503 response, reduced sampled attack trend, shopper 200, monitor 200, and successful ordinary-browser search. If any check fails, investigate that checkpoint. Do not add another rule.

Final Retained State

ItemRequired final state
Under Attack ModeOff
Bot Fight ModeOff
RuleBlock high rate of requests, enabled
Match and counterExact /search, source IP, implicit per-data-center scope
Threshold60 requests per 60 seconds
ActionManaged Challenge
Mitigation timeout600 seconds
Class handoff: Keep the adjusted rule active through recap. The class reset restores Under Attack Mode Off, the original enabled rule with its 200-request threshold, Block action, and 60-second mitigation timeout, resets tenant pressure, clears learner-created SEC 4 rules, and preserves the shopper, attack, monitor, and recurring SEC4-TEST schedules.

Troubleshooting

SymptomWhat to check
No /search TrafficConfirm exact lowercase path, broaden the time range, and wait for supplied traffic
No EventsConfirm the inherited rule is enabled and inspect fresh requests
Request rate analysis is missingConfirm you are on Security > Analytics > Traffic in the assigned Enterprise zone
Starting values differStop and ask the instructor to restore the baseline
Mitigation does not appear on request 61Let all 70 requests finish and record the actual activation result
Managed Challenge returns an unexpected statusUse the Event action and client evidence; do not assume a universal status
Old 503 rows remain visibleCompare timestamps and use complete application responses for the 60-second proof
A legitimate client failsStop at that gate and inspect the matching request before changing the rule

Resources

Reference material for all four classes. External links open Cloudflare documentation in a new tab.

End of Lab Guide.