I have a page where users can view jokes based on categories and sub-categories they are allowed to view. Then based on what category they pick, they can choose sub-categories and based on that, a list of jokes will appear.
I may be mistaken, but I get the impression that the question was focused on server side authentication (making sure the user actualy has access to the requested categories) rather than sanitization, which is always advisable. For this, I think a lot would depend on your actual system. Are you working within a commercial system, or something you are authoring yourself? What architectures does the system use: mvc, ddd, linear includes? It is possible that you have the components required already present.
Typically, in any system that makes use of a database as a storage medium, there are relational fields you can use to associate tables of data. So a simple SQL statement, with a few joins, could tell you right away if a particular user id has access to any given category id. This can be made easier with a good ORM or API, depending on your architecture.
Regardless, the client side script could still be tweaked to send a known combination of user id and category id that works. The way most people handle this is with an anti-forgery token. The way it works is when a page is requested for display (HTTP_GET) a one-time code is generated and associated with some client info (browser type and version, IP address, a generation timestamp, session id, etc). This is kept server side and is valid only for a short period. The submitted form must have this value present in order to validate the response. While the user might be able to guess a valid user id - category id combination, the chances of guessing this anti-forgery code is extremely unlikely. It is possible to output your page so that your JS also knows this value. By the time a user re-writes a pages JS, the code would be invalid (unless it was an automated process).
There is no sure-fire way to protect yourself, but these are some of the things we all face and should give you a starting point for coming up with your own solution.
Correct, it's more of a user authorization thing and not a sanitization thing (I'm using PDO anyway).
I'm not using any complex architechtures, and no frameworks. I just got done reading the 5th edition of Kevin Yank's book and was putting together some stuff I learnt from the book. I'm using a modified version of his user authentication. Basically, I just don't know how to authorize someone in the way I described in the OP. It's true, I could do a few SQL queries to make sure the data requested is authorized but I wasn't sure if this was the proper way or not. I didn't want to use slow SQL queries if I didn't have to.
If you don't want to use an SQL query you could store the IDs the user is allowed to view in a session variable, then have your script check that list before returning the sub-category list.
But it'd also be worth checking the user is allowed to view the results once they've selected a sub-category. I don't know how your page works but as an example, once a user selects a category and a sub-category, if they are then taken to a page with those IDs you should then check they have access to it, otherwise they could manipulate the IDs. Which kinda goes back to what SpacePhoenix said - you should validate user input at all steps of the process.
I've decided just to authorize by doing the multiple SQL queries required for each call. It sucks that there has to be so much overhead just to ensure that a user isn't accessing restricted content but I don't see any better way to do it and I don't really want to abuse session variables but loading the IDs into an array.
I don't really think it's much of an overhead to do an extra SQL call (well at least it shouldn't be - something simple like that shouldn't take long). Most sites and apps will make numerous SQL queries per page load.
Out of interest though, why do you say storing an array of IDs in the session is abusing it?
I agree with martbean. The $_SESSION variable is perfect for something like this. When a user logs in, store the allowed IDs in the session, and it's a quick matter of checking for the requested ID in the session variable rather than having to query the DB again.