Access Control, Authentication & Hardening with mod_security
Apache In Depth
Chapter 8 · Access Control, Authentication & Hardening with mod_security
Web Servers Fundamentals Chapter 8 named Require/mod_authz_host for IP restriction and .htpasswd-backed Basic Auth without ever showing a full working config, and stopped at the web-server layer entirely. This chapter completes both — the real, composable Require logic and a genuine working Basic Auth setup — then goes one layer deeper than Fundamentals ever went at all: mod_security, a real Web Application Firewall that inspects what's actually being sent, not just who's allowed to send it.
The Modern Require Syntax In Depth
Multiple Require lines at the same level combine with an implicit OR by default — the block above grants access if the request comes from either IP condition, not both. Genuine AND logic needs an explicit <RequireAll> wrapper:
This forces both conditions — the correct internal IP range and the correct authenticated username — rather than accepting either alone. <RequireNone> exists too, for an explicit blacklist condition inside a larger logic group.
A Real Basic Authentication Setup
htpasswd manages the password file itself, hashing each password rather than storing it in plain text. AuthUserFile points Apache at that file, and Require valid-user accepts any correctly-authenticated username in it — swap in Require user admin to restrict to one specific account instead, combinable with the IP logic above via <RequireAll>. Per Chapter 3's own AllowOverride material, these directives fall under the AuthConfig group if placed in a .htaccess file rather than the main config.
mod_ssl material), which is doing the actual protective work here — Basic Auth itself provides no confidentiality of its own.
mod_security: A Real Web Application Firewall
Everything so far in this chapter controls who can reach a path. mod_security is a fundamentally different layer: it inspects what's actually being sent — request parameters, headers, body content — against a rule set, regardless of who's sending it or whether they've authenticated at all, looking for patterns that resemble known attack techniques (SQL injection, XSS payloads, path traversal attempts).
SecRuleEngine has three modes: Off, On (actively blocking matched requests), and DetectionOnly (logging what would have been blocked, without actually blocking anything). A hand-written SecRule follows a fixed shape — variables to inspect (ARGS here, meaning request parameters), an operator (@contains), and actions (an id, what to do on a match, and a log message). In practice, almost nobody hand-writes a full rule set from scratch — the OWASP Core Rule Set (CRS) is the standard, actively-maintained rule set nearly every real mod_security deployment pairs it with, covering the same attack categories the site's own XSS and OWASP Top 10 courses cover conceptually, now enforced directly at the web-server layer.
| Layer | Controls | Example |
|---|---|---|
| Require / mod_authz_host | Who can reach a path (IP) | Require ip 10.0.0.0/8 |
| Basic Auth | Who, via credentials | AuthType Basic + Require valid-user |
| mod_security | What's in the request itself | SecRule ARGS "@contains <script>" ... |
mod_security — especially the full CRS — straight into SecRuleEngine On on an existing production site risks blocking genuine, legitimate traffic that happens to trip an aggressive rule (a search box containing the word "select", for instance). Starting with DetectionOnly and reviewing what the rule set would have blocked, over real production traffic, before ever switching to On is the standard safe rollout — the same "match the machinery to what's actually needed, verify before enforcing" caution this course has already applied to TLS version restriction (Chapter 6) and worker capacity planning (Chapter 2).
mod_security/CRS deployments routinely need per-application rule exclusions tuned over time. Treating it as a one-time install rather than an ongoing tuning responsibility is a common, avoidable source of either false-positive outages or a WAF that's been quietly disabled entirely to make the outages stop.
Hands-On Exercises
Write a <Directory> block that requires a request to come from the IP range 172.16.0.0/12 AND be authenticated as a valid user from an AuthUserFile — both conditions must be true, not either one alone.
📄 View solutionA site has Require ip restricting /admin/ to the office IP range, and separately has mod_security with the OWASP Core Rule Set enabled site-wide. An attacker from outside the office IP range sends a SQL-injection payload to the public /search endpoint (not /admin/). Explain which of these two controls is actually relevant here, and why the other one doesn't apply to this request at all.
📄 View solutionExplain, in your own words, why SecRuleEngine DetectionOnly is the recommended way to first deploy mod_security with the OWASP Core Rule Set on an existing production site, rather than enabling SecRuleEngine On immediately.
📄 View solutionChapter 8 Quick Reference
- Multiple
Requirelines default to OR — use<RequireAll>for genuine AND logic,<RequireNone>for a blacklist condition - Basic Auth —
htpasswdcreates the hashed password file;Require valid-uservs.Require user <name>; base64-encoded, not encrypted — HTTPS-only in practice - mod_security — inspects request content itself, not who's sending it;
SecRuleEngine Off/DetectionOnly/On - The OWASP Core Rule Set (CRS) is the standard rule set almost everyone pairs with
mod_securityrather than hand-writing rules - Start new WAF deployments with
DetectionOnly, and treat rule tuning as ongoing, not a one-time install