You can query the SetupEntityAccess object via Apex Code. This provides three fields: ParentId, SetupEntityId, and SetupEntityType.
Basically, get the user's profile Id, then query the PermissionSet object that represents that profile:
Id permssionSetId = [SELECT Id FROM PermissionSet WHERE ProfileId = :UserInfo.getProfileId()].Id;
Finally, query the SetupEntityAccess table for all records matching:
SELECT ParentId, SetupEntityId FROM SetupEntityAccess
WHERE ParentId = :permissionSetId AND SetupEntityType = 'ApexClass'
For each row that matches, they have access; if there is no corresponding row, then they do not have access. SetupEntityId is the ApexClass ID, so you'll probably also want to query all ApexClass records where NamespacePrefix is null.
You don't need to check every field that you query. The issue really boils down to who manages security for the field -- your app or the admin. With standard objects, the answer is always the admin. With custom objects, it depends on whether the field is user visible and is used by users or whether it is internal and used only by your app. Things like wizard state, Auth state, etc.
The standard example would be something like an app that manages contracts. The text of the contract would be managed by the admin, but something like contract state would be managed by your app.
Because this is fundamentally ambiguous, you can make a false positive document outlining which fields are managed by your app and which are not. The worst thing to do is nothing and let the tester try to guess, as they might not have much time to go through your app and may not understand your app well enough to correctly classify this on their own. So a lot of this is up to you in making the case that you own some objects and not the admin.
Once you have the list of fields you need to check perms for, it doesn't matter whether they are used in WHERE clauses or directly accessed -- you need to make sure that the perms are checked. You can use the ESAPI API which contains helper functions such as updateAsUser(), you can write your own helper functions, or you can do checks centrally in your controller.
Triggers run after other code or the UI already allows an operation, so the trigger wouldn't fire if the user didn't have permission already, assuming all the upstream checks are made when necessary. Therefore triggers that only update the object being modified generally don't need a check, the issue is if the the trigger modifies some other object, in which case you do need to do the check if the object is managed by the admin.
For background/async/scheduled jobs, there is absolutely no difference in security requirements for async apex versus synchronous apex. Whether you do an operation synchronously or not has nothing to do with whether the admin's policies need to be respected. The same concerns apply as above -- do the check if you don't own the object and don't do it if you do.
One thing to keep in mind here is that unlike sharing, you generally control the profiles and permission sets of your users as you can package these with your app. So package the permissions so that your checks always return true. Then, if your checks return false, it means that the admin assigned a different perm set or changed the perm set assignment for a user of your app, which is something that should generally result in a failure message. There may be situations in which the app should not work for a user -- say the admin removed some perms from that user, but due to not having deep knowledge of how your app works, did not remove that user from your app. Making these checks is exactly for this type of conflict situation, and generally you want to stop processing with an error message and let the admin resolve the inconsistency at the perm level, rather than trying to guess what the admin would want to do and then override the org perms. This is because if every app overrode the org perms, then the admin wouldn't be able to manage permissions centrally anymore.
Best Answer
You should check that the current user has isAccessible() permission on the fields in the WHERE clause as well as any fields in the SELECT clause being returned to the logged in user. The reason why you need to do this check is that Apex will not throw a DML error when accessing fields that the user cannot access, so it is up to you to enforce the platform's permission model.
There is some confusion if your intuition comes from working in the developer console or via the API. The developer console runs in an execute anonymous context in which permissions are enforced. Permissions are enforced in the API as well. Permissions are not enforced in apex code running in your org, so you as the developer need to enforce them. You will not get permissions exceptions.
Also, be aware of the difference between profile perms, CRUD/FLS perms, and record sharing. Profile perms are always enforced but are only refreshed across logins. CRUD/FLS are never enforced which is why you need to make all those .isAccessible(), and isCreatable() calls before accessing, updating, deleting or inserting records. Record sharing is enforced if your class is 'with sharing'.
In terms of implementation, it's up to you as the dev to enforce programmatically whatever isn't enforced automatically by the platform, where appropriate. In some cases you may want to set a field as private/private for sharing and then ignore sharing when accessing the field. This should only be done for custom fields. In the case of sites and guest user access, you may also need to ignore CRUD/FLS, but again this should only be done for custom fields that are in your namespace. For standard fields you must always enforce CRUD/FLS (no exceptions), and you must always enforce sharing in the entry-points to your app (controllers and global classes). Hide the without sharing code in library functions invoked downstream from your entry points.