Skip to content

Commit d1e879e

Browse files
[patch] Harden app front-controller routing (#14)
1 parent 282c877 commit d1e879e

9 files changed

Lines changed: 226 additions & 7 deletions

File tree

images/ojs/rootfs/etc/confd/templates/ojs.conf.tmpl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ index index.php;
44
error_log /dev/stderr;
55
access_log /dev/stdout combined;
66

7-
error_log /dev/stderr;
8-
access_log /dev/stdout combined;
9-
107
# API rewrite with redirect
118
location ~ ^/api/v1(.*)$ {
129
return 307 /index.php/api/v1$1;
@@ -18,8 +15,9 @@ location / {
1815
try_files $uri $uri/ /index.php/$uri?$query_string;
1916
}
2017

21-
# PHP processing
22-
location ~ \.php(/|$) {
18+
# Only the public front controller may execute through the web server. OJS
19+
# includes many CLI and handler PHP files beneath the document root.
20+
location ~ ^/index\.php(?:/|$) {
2321
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
2422
if (!-f $document_root$fastcgi_script_name) {
2523
return 404;
@@ -36,6 +34,10 @@ location ~ \.php(/|$) {
3634
include fastcgi_params;
3735
}
3836

37+
location ~ \.php(?:/|$) {
38+
return 404;
39+
}
40+
3941
# Deny access to hidden files
4042
location ~ /\. {
4143
deny all;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
name: ojs-frontcontrollersecurity
3+
services:
4+
ojs:
5+
image: ${OJS:-libops/ojs:local-php83}
6+
environment:
7+
DB_HOST: database
8+
DB_PASSWORD: test-database-password
9+
OJS_SALT: test-salt
10+
OJS_API_KEY_SECRET: test-api-key-secret
11+
OJS_SECRET_KEY: base64:MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=
12+
OJS_ADMIN_PASSWORD: test-admin-password
13+
volumes:
14+
- ./test.sh:/test.sh:ro
15+
command:
16+
- /test.sh
17+
depends_on:
18+
mariadb:
19+
condition: service_healthy
20+
mariadb:
21+
image: ${MARIADB11:-libops/mariadb:local-11}
22+
environment:
23+
DB_ROOT_PASSWORD: test-root-password
24+
DB_NAME: ojs
25+
DB_USER: ojs
26+
DB_PASSWORD: test-database-password
27+
networks:
28+
default:
29+
aliases:
30+
- database
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/command/with-contenv bash
2+
# shellcheck shell=bash
3+
4+
set -euo pipefail
5+
6+
on_terminate() {
7+
exit 0
8+
}
9+
trap 'on_terminate' SIGTERM
10+
11+
for _ in $(seq 1 150); do
12+
if [ -f /installed ]; then
13+
break
14+
fi
15+
sleep 2
16+
done
17+
test -f /installed
18+
19+
curl -fsSL --connect-timeout 5 --max-time 30 \
20+
-o /dev/null http://localhost/index.php/index/login
21+
22+
for protected_path in \
23+
/config.inc.php \
24+
/tools/install.php \
25+
/classes/core/Application.php; do
26+
status="$(curl -sS --connect-timeout 5 --max-time 30 \
27+
-o /dev/null -w '%{http_code}' "http://localhost${protected_path}")"
28+
if [ "$status" != 404 ]; then
29+
echo "Protected OJS path ${protected_path} returned HTTP ${status}" >&2
30+
exit 1
31+
fi
32+
done

images/omeka-classic/rootfs/etc/confd/templates/omeka-classic.conf.tmpl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,27 @@ index index.php;
44
error_log /dev/stderr;
55
access_log /dev/stdout combined;
66

7+
location = /admin {
8+
return 301 /admin/$is_args$args;
9+
}
10+
11+
location /admin/ {
12+
try_files $uri $uri/ /admin/index.php?$args;
13+
}
14+
15+
location = /install {
16+
return 301 /install/$is_args$args;
17+
}
18+
19+
location /install/ {
20+
try_files $uri $uri/ /install/install.php?$args;
21+
}
22+
723
location / {
824
try_files $uri $uri/ /index.php?$args;
925
}
1026

11-
location ~ \.php$ {
27+
location ~ ^/(?:index|admin/index|install/install)\.php$ {
1228
try_files $uri =404;
1329
fastcgi_pass unix:{{ getenv "PHP_FPM_SOCKET" }};
1430
fastcgi_index index.php;
@@ -20,6 +36,14 @@ location ~ \.php$ {
2036
include fastcgi_params;
2137
}
2238

39+
location ~ \.php(?:/|$) {
40+
return 404;
41+
}
42+
43+
location ~* \.ini$ {
44+
deny all;
45+
}
46+
2347
location ~ /\. {
2448
deny all;
2549
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: omeka-classic-adminfrontcontrollerrouting
3+
services:
4+
omeka-classic:
5+
image: ${OMEKA_CLASSIC:-libops/omeka-classic:local-php83}
6+
environment:
7+
DB_PASSWORD: test-database-password
8+
OMEKA_CLASSIC_ADMIN_EMAIL: admin@example.com
9+
OMEKA_CLASSIC_ADMIN_PASSWORD: test-admin-password
10+
volumes:
11+
- ./test.sh:/test.sh:ro
12+
command:
13+
- /test.sh
14+
depends_on:
15+
mariadb:
16+
condition: service_healthy
17+
mariadb:
18+
image: ${MARIADB11:-libops/mariadb:local-11}
19+
environment:
20+
DB_ROOT_PASSWORD: test-root-password
21+
DB_NAME: omeka_classic
22+
DB_USER: omeka_classic
23+
DB_PASSWORD: test-database-password
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/command/with-contenv bash
2+
# shellcheck shell=bash
3+
4+
set -euo pipefail
5+
6+
on_terminate() {
7+
exit 0
8+
}
9+
trap 'on_terminate' SIGTERM
10+
11+
for _ in $(seq 1 150); do
12+
if [ -f /installed ]; then
13+
break
14+
fi
15+
sleep 2
16+
done
17+
test -f /installed
18+
19+
response="$(mktemp)"
20+
trap 'rm -f -- "$response"' EXIT
21+
22+
curl -fsSL --connect-timeout 5 --max-time 30 \
23+
-o "$response" http://localhost/admin/users/login
24+
grep -Eq 'action="[^"]*/admin/users/login"' "$response"
25+
26+
for protected_path in \
27+
/application/config/db.ini \
28+
/application/controllers/UsersController.php; do
29+
status="$(curl -sS --connect-timeout 5 --max-time 30 \
30+
-o /dev/null -w '%{http_code}' "http://localhost${protected_path}")"
31+
case "$status" in
32+
403 | 404) ;;
33+
*)
34+
echo "Protected Omeka Classic path ${protected_path} returned HTTP ${status}" >&2
35+
exit 1
36+
;;
37+
esac
38+
done

images/omeka-s/rootfs/etc/confd/templates/omeka-s.conf.tmpl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ location / {
88
try_files $uri $uri/ /index.php?$args;
99
}
1010

11-
location ~ \.php$ {
11+
location = /index.php {
1212
try_files $uri =404;
1313
fastcgi_pass unix:{{ getenv "PHP_FPM_SOCKET" }};
1414
fastcgi_index index.php;
@@ -20,6 +20,14 @@ location ~ \.php$ {
2020
include fastcgi_params;
2121
}
2222

23+
location ~ \.php(?:/|$) {
24+
return 404;
25+
}
26+
27+
location ~* \.ini$ {
28+
deny all;
29+
}
30+
2331
location ~ /\. {
2432
deny all;
2533
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
name: omeka-s-frontcontrollersecurity
3+
services:
4+
omeka-s:
5+
image: ${OMEKA_S:-libops/omeka-s:local-php83}
6+
environment:
7+
DB_PASSWORD: test-database-password
8+
OMEKA_S_ADMIN_EMAIL: admin@example.com
9+
OMEKA_S_ADMIN_NAME: Administrator
10+
OMEKA_S_ADMIN_PASSWORD: test-admin-password
11+
volumes:
12+
- ./test.sh:/test.sh:ro
13+
command:
14+
- /test.sh
15+
depends_on:
16+
mariadb:
17+
condition: service_healthy
18+
mariadb:
19+
image: ${MARIADB11:-libops/mariadb:local-11}
20+
environment:
21+
DB_ROOT_PASSWORD: test-root-password
22+
DB_NAME: omeka_s
23+
DB_USER: omeka_s
24+
DB_PASSWORD: test-database-password
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/command/with-contenv bash
2+
# shellcheck shell=bash
3+
4+
set -euo pipefail
5+
6+
on_terminate() {
7+
exit 0
8+
}
9+
trap 'on_terminate' SIGTERM
10+
11+
for _ in $(seq 1 150); do
12+
if [ -f /installed ]; then
13+
break
14+
fi
15+
sleep 2
16+
done
17+
test -f /installed
18+
19+
response="$(mktemp)"
20+
trap 'rm -f -- "$response"' EXIT
21+
22+
curl -fsSL --connect-timeout 5 --max-time 30 \
23+
-o "$response" http://localhost/admin
24+
grep -Fq '<h1>Log in</h1>' "$response"
25+
26+
for protected_path in \
27+
/config/database.ini \
28+
/application/src/Controller/IndexController.php; do
29+
status="$(curl -sS --connect-timeout 5 --max-time 30 \
30+
-o /dev/null -w '%{http_code}' "http://localhost${protected_path}")"
31+
case "$status" in
32+
403 | 404) ;;
33+
*)
34+
echo "Protected Omeka S path ${protected_path} returned HTTP ${status}" >&2
35+
exit 1
36+
;;
37+
esac
38+
done

0 commit comments

Comments
 (0)