Unvalidated user data in $site->find()

I’m working on a site that customizes its content based on the U.S. state that the user selects. Each state has its own page under states/xxxxxx, so I’m storing the page id in the session and using that to customize the data shown.

Marketing material is targeted for each state individually, so in addition to the user being able to select their state themselves, all urls for the site can automatically set the state as well. This works by appending a query string on any page: ?state=texas.

So I have two questions.

First, I’m handling the query string with a site.php controller that checks the request object. This works for pages without controllers, but means I have to copy/paste the check into other controllers when a page requires one. Is there a way to check the query string and set the session for all urls regardless of whether they have a controller or not?

Second, before setting the session I verify that the state exists either like this:

$kirby->site()->find('states/' . $stateQueryString)

or like this

$kirby->site()->find($stateQueryString)

Is there a security issue with passing unvalidated user-supplied data to that method? All of the places the site deals with the user’s state work through the page id stored in session, so I think I’m safe from reflection attacks and friends from that direction. I’m not sure about just handing the query string over to find() though?

I don’t think so, the find() method just checks if something exists and should not do any harm even if you pass rubbish to it. It would be different for other methods that actually do something in the file system.

Using user input invalidated is really critical in situations where the user input is used as a method, for example:

$userInput = 'delete';

$page->{$userInput}();

To be 100% on the safe side, you can (and maybe should) always create an array of allowed values before passing your value to the method, though.

Good advice, thanks!

Don’t suppose you have any ideas on my first question? I was thinking a route that always matches might work, but something just seems wrong about that solution because it’s not a route, just some code that needs to run on every request. I also looked at hooks, but there aren’t any for processing requests…