Skip to content

Commit 4d6b45b

Browse files
committed
fix: add missing URI Security and Conig\App::$permittedURIChars
1 parent 7f0cab1 commit 4d6b45b

File tree

8 files changed

+159
-0
lines changed

8 files changed

+159
-0
lines changed

app/Config/App.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,30 @@ class App extends BaseConfig
5959
*/
6060
public string $uriProtocol = 'REQUEST_URI';
6161

62+
/*
63+
|--------------------------------------------------------------------------
64+
| Allowed URL Characters
65+
|--------------------------------------------------------------------------
66+
|
67+
| This lets you specify which characters are permitted within your URLs.
68+
| When someone tries to submit a URL with disallowed characters they will
69+
| get a warning message.
70+
|
71+
| As a security measure you are STRONGLY encouraged to restrict URLs to
72+
| as few characters as possible.
73+
|
74+
| By default, only these are allowed: `a-z 0-9~%.:_-`
75+
|
76+
| Set an empty string to allow all characters -- but only if you are insane.
77+
|
78+
| The configured value is actually a regular expression character group
79+
| and it will be used as: '/\A[<permittedURIChars>]+\z/iu'
80+
|
81+
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
82+
|
83+
*/
84+
public string $permittedURIChars = 'a-z 0-9~%.:_\-';
85+
6286
/**
6387
* --------------------------------------------------------------------------
6488
* Default Locale
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\HTTP\Exceptions;
13+
14+
use CodeIgniter\Exceptions\HTTPExceptionInterface;
15+
use RuntimeException;
16+
17+
/**
18+
* 400 Bad Request
19+
*/
20+
class BadRequestException extends RuntimeException implements HTTPExceptionInterface
21+
{
22+
/**
23+
* HTTP status code for Bad Request
24+
*
25+
* @var int
26+
*/
27+
protected $code = 400; // @phpstan-ignore-line
28+
}

system/Router/Router.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Closure;
1515
use CodeIgniter\Exceptions\PageNotFoundException;
16+
use CodeIgniter\HTTP\Exceptions\BadRequestException;
1617
use CodeIgniter\HTTP\Exceptions\RedirectException;
1718
use CodeIgniter\HTTP\Request;
1819
use CodeIgniter\HTTP\ResponseInterface;
@@ -120,11 +121,23 @@ class Router implements RouterInterface
120121

121122
protected ?AutoRouterInterface $autoRouter = null;
122123

124+
/**
125+
* Permitted URI chars
126+
*
127+
* The default value is `''` (do not check) for backward compatibility.
128+
*/
129+
protected string $permittedURIChars = '';
130+
123131
/**
124132
* Stores a reference to the RouteCollection object.
125133
*/
126134
public function __construct(RouteCollectionInterface $routes, ?Request $request = null)
127135
{
136+
$config = config(App::class);
137+
if (isset($config->permittedURIChars)) {
138+
$this->permittedURIChars = $config->permittedURIChars;
139+
}
140+
128141
$this->collection = $routes;
129142

130143
// These are only for auto-routing
@@ -179,6 +192,8 @@ public function handle(?string $uri = null)
179192
// Decode URL-encoded string
180193
$uri = urldecode($uri);
181194

195+
$this->checkDisallowedChars($uri);
196+
182197
// Restart filterInfo
183198
$this->filterInfo = null;
184199
$this->filtersInfo = [];
@@ -676,4 +691,20 @@ protected function setMatchedRoute(string $route, $handler): void
676691

677692
$this->matchedRouteOptions = $this->collection->getRoutesOptions($route);
678693
}
694+
695+
/**
696+
* Checks disallowed characters
697+
*/
698+
private function checkDisallowedChars(string $uri): void
699+
{
700+
foreach (explode('/', $uri) as $segment) {
701+
if ($segment !== '' && $this->permittedURIChars !== ''
702+
&& preg_match('/\A[' . $this->permittedURIChars . ']+\z/iu', $segment) !== 1
703+
) {
704+
throw new BadRequestException(
705+
'The URI you submitted has disallowed characters: "' . $segment . '"'
706+
);
707+
}
708+
}
709+
}
679710
}

tests/system/Router/RouterTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
use CodeIgniter\Config\Services;
1515
use CodeIgniter\Exceptions\PageNotFoundException;
16+
use CodeIgniter\HTTP\Exceptions\BadRequestException;
1617
use CodeIgniter\HTTP\Exceptions\RedirectException;
1718
use CodeIgniter\HTTP\IncomingRequest;
1819
use CodeIgniter\Router\Exceptions\RouterException;
1920
use CodeIgniter\Test\CIUnitTestCase;
21+
use Config\App;
2022
use Config\Modules;
2123
use Config\Routing;
2224
use Tests\Support\Filters\Customfilter;
@@ -87,6 +89,16 @@ public function testZeroAsURIPath(): void
8789
$router->handle('0');
8890
}
8991

92+
public function testNotPermittedChars(): void
93+
{
94+
$router = new Router($this->collection, $this->request);
95+
96+
$this->expectException(BadRequestException::class);
97+
$this->expectExceptionMessage('The URI you submitted has disallowed characters: "<a>"');
98+
99+
$router->handle('test/%3Ca%3E');
100+
}
101+
90102
public function testURIMapsToController(): void
91103
{
92104
$router = new Router($this->collection, $this->request);
@@ -783,6 +795,9 @@ public function testAutoRouteMethodEmpty(): void
783795
*/
784796
public function testRegularExpressionWithUnicode(): void
785797
{
798+
$config = config(App::class);
799+
$config->permittedURIChars = 'a-z 0-9~%.:_\-\x{0980}-\x{09ff}';
800+
786801
$this->collection->get('news/([a-z0-9\x{0980}-\x{09ff}-]+)', 'News::view/$1');
787802

788803
$router = new Router($this->collection, $this->request);
@@ -802,6 +817,9 @@ public function testRegularExpressionWithUnicode(): void
802817
*/
803818
public function testRegularExpressionPlaceholderWithUnicode(): void
804819
{
820+
$config = config(App::class);
821+
$config->permittedURIChars = 'a-z 0-9~%.:_\-\x{0980}-\x{09ff}';
822+
805823
$this->collection->addPlaceholder('custom', '[a-z0-9\x{0980}-\x{09ff}-]+');
806824
$this->collection->get('news/(:custom)', 'News::view/$1');
807825

user_guide_src/source/changelogs/v4.4.7.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ Release Date: Unreleased
1010
:local:
1111
:depth: 3
1212

13+
********
14+
SECURITY
15+
********
16+
17+
- **URI Security:** The feature to check if URIs do not contain not permitted
18+
strings has been added. This check is equivalent to the URI Security found in
19+
CodeIgniter 3. This is enabled by default, but upgraded users need to add
20+
a setting to enable it. See :ref:`urls-uri-security` for details.
21+
1322
********
1423
BREAKING
1524
********

user_guide_src/source/concepts/security.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ OWASP recommendations
3838
CodeIgniter provisions
3939
======================
4040

41+
- :ref:`urls-uri-security`
4142
- :ref:`invalidchars` filter
4243
- :doc:`../libraries/validation` library
4344
- :doc:`HTTP library <../incoming/incomingrequest>` provides for :ref:`input field filtering <incomingrequest-filtering-input-data>` & content metadata

user_guide_src/source/general/urls.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,39 @@ Route path /blog/news/2022/10 The URI path relative to the Bas
5858
Query page=2
5959
========== ==================================== =========================================
6060

61+
.. _urls-uri-security:
62+
63+
URI Security
64+
============
65+
66+
.. versionadded:: 4.4.7
67+
68+
.. important::
69+
Users upgrading from versions prior to v4.4.7 will need to add the following
70+
to **app/Config/App.php** in order to use this feature::
71+
72+
public string $permittedURIChars = 'a-z 0-9~%.:_\-';
73+
74+
CodeIgniter is fairly restrictive regarding which characters it allows in your
75+
URI strings (Route path) in order to help minimize the possibility that malicious
76+
data can be passed to your application. URIs may only contain the following:
77+
78+
- Alpha-numeric text (latin characters only)
79+
- Tilde: ``~``
80+
- Percent sign: ``%``
81+
- Period: ``.``
82+
- Colon: ``:``
83+
- Underscore: ``_``
84+
- Dash: ``-``
85+
- Space: `` ``
86+
87+
This setting can be changed by ``Config\App::$permittedURIChars``.
88+
89+
.. note::
90+
This check is performed by the ``Router``. The Router takes the URL-encoded
91+
value held by the ``SiteURI`` class, decodes it, and then checks that it
92+
does not contain not permitted strings.
93+
6194
.. _urls-remove-index-php:
6295

6396
Removing the index.php file

user_guide_src/source/installation/upgrade_447.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ Please refer to the upgrade instructions corresponding to your installation meth
1616
Mandatory File Changes
1717
**********************
1818

19+
URI Security
20+
============
21+
22+
The feature to check if URIs do not contain not permitted strings has been added.
23+
This check is equivalent to the URI Security found in CodeIgniter 3.
24+
25+
We recommend you enable this feature. Add the following to **app/Config/App.php**::
26+
27+
public string $permittedURIChars = 'a-z 0-9~%.:_\-';.
28+
29+
See :ref:`urls-uri-security` for details.
30+
1931
Error Files
2032
===========
2133

@@ -66,6 +78,9 @@ and it is recommended that you merge the updated versions with your application:
6678
Config
6779
------
6880

81+
- app/Config/App.php
82+
- The property ``$permittedURIChars`` was added. See :ref:`urls-uri-security`
83+
for details.
6984
- @TODO
7085

7186
All Changes

0 commit comments

Comments
 (0)