The file .htaccess (Hypertext Access) is a distributed configuration file that Apache HTTP Server processes at the directory level. Although its original purpose was not security, the policies available — especially through mod_rewrite and mod_authz — allow you to implement effective layers of protection against common web attacks such as SQL injection, Cross-Site Scripting (XSS), CRLF injection and automated scanning.
What is .htaccess and when to use it
The file .htaccess is placed in any directory served by Apache and its directives are applied recursively to that directory and all its subdirectories. Apache reads it in each HTTP request, which implies a performance cost compared to the configuration in httpd.conf or in blocks <VirtualHost>.
Use .htaccess when:
- You do not have access to the main server configuration (shared hosting).
- You need specific rules per directory that can be modified without restarting Apache.
- You want to add an extra layer of defense in depth.
If you have root access to the server, it is preferable to place the rules directly in the VirtualHost configuration for performance. However, the rules that we will see are equally valid in both contexts.
Base configuration and information hiding
The first step is to enable mod_rewrite and disable the exposure of server information:
# Habilitar mod_rewrite
RewriteEngine On
Options +FollowSymLinks
# Ocultar la versión de Apache en páginas de error y cabeceras
ServerSignature Off
# Prevenir listado de directorios
Options -Indexes
# Proteger el propio archivo .htaccess
<Files .htaccess>
Order Allow,Deny
Deny from all
</Files>The directive ServerSignature Off prevents Apache from revealing its version and installed modules on error pages (403, 404, 500). This information is valuable to an attacker looking for known vulnerabilities in specific versions. Complement this with ServerTokens Prod in the main settings if you have access.
Blocking unwanted HTTP methods
Most web applications only need the methods GET and POST. Methods such as TRACE, DELETE or TRACK can be exploited for Cross-Site Tracing (XST) attacks or unauthorized manipulation of resources:
# Bloquear métodos HTTP peligrosos
RewriteCond %{REQUEST_METHOD} ^(HEAD|TRACE|DELETE|TRACK|OPTIONS) [NC]
RewriteRule ^(.*)$ - [F,L]The flags used in the rules are:
[NC]— No Case: The comparison is not case sensitive.[OR]— Chain the condition with the next one using logical OR (default is AND).[F]— Forbidden: returns a 403 error.[L]— Last: Stops processing subsequent rules.
Protection against CRLF Injection
CRLF (Carriage Return Line Feed) injection occurs when an attacker inserts new line characters (%0A, %0D) in the HTTP headers. This can lead to HTTP Response Splitting, cache poisoning, or XSS:
# Bloquear CRLF en la petición completa
RewriteCond %{THE_REQUEST} ^.*(\\r|\\n|%0A|%0D).* [NC]
RewriteRule ^(.*)$ - [F,L]HTTP header sanitization
The headers Referer and Cookie They are frequent injection vectors. An attacker can insert special characters into these headers to attempt reflected XSS or manipulation of the application:
# Bloquear caracteres peligrosos en Referer
RewriteCond %{HTTP_REFERER} ^(.*)(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC,OR]
# Bloquear caracteres peligrosos en Cookie
RewriteCond %{HTTP_COOKIE} ^.*(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC]
RewriteRule ^(.*)$ - [F,L]Blocked characters include: single quotes (%27), HTML tags (%3C, %3E), null bytes (%00) and line breaks (%0A, %0D). None of these should appear in legitimate requests.
URI overflow protection
Requests with extremely long URIs may indicate buffer overflow attempts, especially on servers with vulnerable components such as older versions of Apache Tomcat:
# Bloquear URIs con caracteres sospechosos y longitudes excesivas
RewriteCond %{REQUEST_URI} ^/(,|;|:|<|>|">|"<|/|\\\.\.\\).{0,9999}.* [NC]
RewriteRule ^(.*)$ - [F,L]Blocking malicious User-Agents
Malicious bots, vulnerability scanners and automated tools are often identified with characteristic User-Agents. Blocking them significantly reduces the noise of automated attacks:
# Bloquear peticiones sin User-Agent
RewriteCond %{HTTP_USER_AGENT} ^$ [OR]
# Bloquear herramientas de línea de comandos
RewriteCond %{HTTP_USER_AGENT} ^(java|curl|wget).* [NC,OR]
# Bloquear scrapers y harvesting tools
RewriteCond %{HTTP_USER_AGENT} ^.*(winhttp|HTTrack|clshttp|archiver|loader|email|harvest|extract|grab|miner).* [NC,OR]
# Bloquear escáneres de vulnerabilidades conocidos
RewriteCond %{HTTP_USER_AGENT} ^.*(libwww|curl|wget|python|nikto|scan|sqlmap|nmap|masscan|dirbuster|gobuster).* [NC,OR]
# Bloquear User-Agents con caracteres de inyección
RewriteCond %{HTTP_USER_AGENT} ^.*(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC]
RewriteRule ^(.*)$ - [F,L]Note: Blocking by User-Agent is a superficial measure since any competent attacker can spoof it. However, it is effective against automated bots that don't bother to change their ID.
Protection against SQL Injection and XSS
He QUERY_STRING (the parameters after the ? in the URL) is the primary vector for SQL and XSS injections. These rules block common attack patterns:
# Bloquear palabras clave SQL combinadas con caracteres de inyección
RewriteCond %{QUERY_STRING} ^.*(;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|cast|set|declare|drop|update|md5|benchmark).* [NC,OR]
# Bloquear referencias a localhost (posible SSRF)
RewriteCond %{QUERY_STRING} ^.*(localhost|loopback|127\.0\.0\.1).* [NC,OR]
# Bloquear caracteres de inyección en query string
RewriteCond %{QUERY_STRING} ^.*(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC]
RewriteRule ^(.*)$ - [F,L]These rules detect patterns such as ?id=1' UNION SELECT, ?q=<script> or attempts to access localhost through parameters (Server-Side Request Forgery). Combining special characters with SQL keywords reduces false positives.
Consolidated complete rule
Here is the file .htaccess complete with all rules combined and response action:
RewriteEngine On
Options +FollowSymLinks -Indexes
ServerSignature Off
# Proteger .htaccess
<Files .htaccess>
Order Allow,Deny
Deny from all
</Files>
# --- Métodos HTTP ---
RewriteCond %{REQUEST_METHOD} ^(HEAD|TRACE|DELETE|TRACK) [NC,OR]
# --- CRLF Injection ---
RewriteCond %{THE_REQUEST} ^.*(\\r|\\n|%0A|%0D).* [NC,OR]
# --- Cabeceras ---
RewriteCond %{HTTP_REFERER} ^(.*)(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC,OR]
RewriteCond %{HTTP_COOKIE} ^.*(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC,OR]
# --- URI Overflow ---
RewriteCond %{REQUEST_URI} ^/(,|;|:|<|>|">|"<|/|\\\.\.\\).{0,9999}.* [NC,OR]
# --- User-Agents ---
RewriteCond %{HTTP_USER_AGENT} ^$ [OR]
RewriteCond %{HTTP_USER_AGENT} ^(java|curl|wget).* [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*(winhttp|HTTrack|clshttp|archiver|loader|email|harvest|extract|grab|miner).* [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*(libwww|python|nikto|scan|sqlmap|nmap|masscan|dirbuster|gobuster).* [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC,OR]
# --- SQL Injection / XSS ---
RewriteCond %{QUERY_STRING} ^.*(;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|cast|set|declare|drop|update|md5|benchmark).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(localhost|loopback|127\.0\.0\.1).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(<|>|'|%0A|%0D|%27|%3C|%3E|%00).* [NC]
# Acción: devolver 403 Forbidden
RewriteRule ^(.*)$ - [F,L]The action [F,L] returns an error 403 Forbidden to the client. Alternatively, you can redirect to a logging script to log attack attempts:
# Alternativa: redirigir a un script que registre el intento
RewriteRule ^(.*)$ /security_log.php [L]Additional safety headers
Complement the rewrite rules with security HTTP headers using mod_headers:
<IfModule mod_headers.c>
# Prevenir clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# Activar protección XSS del navegador
Header always set X-XSS-Protection "1; mode=block"
# Prevenir MIME sniffing
Header always set X-Content-Type-Options "nosniff"
# Política de referrer
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Forzar HTTPS
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Content Security Policy básica
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
</IfModule>Limitations and considerations
It is important to understand that the rules .htaccess are one complementary defense layer, not a substitute for application-level security:
- False positives — Rules that are too aggressive can block legitimate users. Monitor Apache error logs after implementing the rules.
- Evasion — An experienced attacker can encode their payloads in ways that avoid regular expressions (double URL encoding, Unicode, etc.).
- User-Agent spoofing — Faking the User-Agent is trivial, so don't rely on it as your only defense.
- Performance — Apache processes
.htaccessin each request. On high traffic sites, move the rules to the VirtualHost configuration. - dedicated WAF — For more robust protection, consider using ModSecurity with the OWASP Core Rule Set (CRS), which offers more sophisticated, community-maintained detection.
The rules presented here are still useful as a quick first line of defense in environments where a full WAF is not available. The key is to combine them with good secure development practices: server-side input validation, parameterized queries, output escaping, and the principle of least privilege.
:wq!
Comments