Skip to content

Commit a0ce6fd

Browse files
docs(security): Prevent direct browser access to logs
1 parent 67ec04b commit a0ce6fd

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,81 @@ Request → CI3 Hook (post_controller_constructor)
152152
The write is a single append with `LOCK_EX`, so it does not block the request
153153
and is safe for concurrent PHP-FPM workers.
154154

155+
## Security
156+
157+
CI3 places `index.php` in the same directory as `application/`, so by default
158+
everything under `application/` — including `application/logs/analysis.log`,
159+
which contains request headers and bodies — is reachable directly from the
160+
browser (`http://host/application/logs/analysis.log`). Apply at least one of
161+
the layers below; using all three is recommended.
162+
163+
### 1. Keep `application/` out of the web root (recommended)
164+
165+
Move the front controller so the public web root only contains `index.php`,
166+
`.htaccess` and assets:
167+
168+
```
169+
project/
170+
├── application/ ← not web-accessible
171+
├── system/
172+
└── public/ ← document root
173+
├── index.php
174+
└── .htaccess
175+
```
176+
177+
Then fix the paths in `public/index.php` (a copy of the original, with
178+
`$system_path`/`$application_folder` updated to `../system` and `../application`)
179+
and set `$config['base_url']` accordingly in `application/config/config.php`.
180+
181+
### 2. Redirect the log directory outside the web root
182+
183+
Even with the default layout, point logs somewhere the browser cannot reach:
184+
185+
```ini
186+
REQUEST_LOG_DIR = "/var/log/myapp/request-analysis"
187+
```
188+
189+
The hook falls back to `application/logs` only when `REQUEST_LOG_DIR` is empty.
190+
191+
### 3. Deny access with the web server
192+
193+
**Apache** — ship an `.htaccess` inside the log directory (and the
194+
`application/` directory) with:
195+
196+
```apache
197+
<IfModule mod_authz_core.c>
198+
Require all denied
199+
</IfModule>
200+
<IfModule !mod_authz_core.c>
201+
Order deny,allow
202+
Deny from all
203+
</IfModule>
204+
```
205+
206+
The sample app already includes `sample/application/logs/.htaccess` and
207+
`sample/application/uploads/.htaccess`. If `AllowOverride` is disabled, deny
208+
the paths in the vhost instead:
209+
210+
```apache
211+
<Directory "/path/to/app/application/logs">
212+
Require all denied
213+
</Directory>
214+
```
215+
216+
**Nginx**:
217+
218+
```nginx
219+
location ~ ^/(application|system)/ {
220+
deny all;
221+
}
222+
location ^~ /application/logs/ {
223+
deny all;
224+
}
225+
```
226+
227+
**Laravel-style check** — as a last line of defense, `application/logs/.htaccess`
228+
also rejects `*.log` / `*.gz` matches even if directory-level rules are ignored.
229+
155230
## Notes
156231

157232
- The hook runs on every request once enabled. To log only specific

0 commit comments

Comments
 (0)