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.
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
The best option today I think is to largely avoid CRUD & FLS and enforce security via custom permissions aligned with your business processes but that is not really answering your question.
Part of the difficulty here is first understanding where to perform CRUD & FLS checks. For me the best answer to that is on service entry since it allows callers to the service to be given visibility into security questions, such as 'Can I do X on a Y?', that they might need to provide the right UX while also supporting a 'fail fast' model.
You can support this type of handling fairly easily and cheaply with some kind of Permission checking service, really no need to have this integrated into anything else in the stack or make it any more complex than needed for the types of packages you work on.
I think where this answer fails is that a lot of developers would rather not be troubled by thinking about what CRUD & FLS checks are needed and would prefer something more automated. That is likely a mistake as you will probably realise the first time a customer asks what permissions do I need to give a user to make X work and you realise not only that you don't know but you don't have any means to find out either for all the possible 'X' unless you have also done a lot of work on Permission sets.
That said, the next best place to service entry where you could automate CRUD & FLS is likely in a UnitOfWork. There have been some discussion recently on doing exactly that to the FinancialForce version of fflib just to make it easier to get some older code through security review without too much work.
Update:
This is does not fit your constraints but if you allow for some complexity then you get to a nicer place. If you accept the argument that checking CRUD & FLS late creates UX problems then you have to do it early which rules out directly using DMLManager or UnitOfWork style automated handling by requiring checks are at service entry or similar.
The problem with the service entry approach is the manual work needed. To reduce that you can use self-verification where you cross check (in test mode) what permissions were checked at service entry against what were used in DML ops and fail any transaction that causes differences forcing a fix. This just ensures your service entry checks always match which saves you a large chunk of dedicated permissions testing which is a big part of what ‘automated’ checks would help you do anyway.
How you perform the cross check is not that important, just really some Map comparing. A useful abstraction for permission checking in this kind of model is to create a Permission base class for various styles of permissions such as a ReadFieldPermission class and include in the hierarchy combining conditionals such as anyOf, not etc so you can express complex needs. This allows you to pass around permission needs between functions so you don’t break encapsulation while making it straightforward to check if some arbitrary set of permissions are being met by the current user.