To safely build dynamic SOQL queries when object and field names are determined at runtime in Salesforce, follow these comprehensive practices:
1. **Validate Object and Field Names**: Use `Schema.getGlobalDescribe()` to retrieve a map of all objects in the schema. Verify the object exists and is accessible using `getDescribe().isAccessible()`. For fields, use the `fields.getMap()` method on the object's describe result and validate field accessibility with `getDescribe().isAccessible()`.
2. **Use Bind Variables**: Include user-supplied input in the WHERE clause using bind variables to ensure it is safely handled and cannot alter the query structure.
3. **Sanitize Input**: If bind variables are not an option, sanitize input using `String.escapeSingleQuotes()` to escape single quotes. Combine this with other validation methods for comprehensive protection.
4. **Perform CRUD/FLS Checks**: Ensure the user has the necessary permissions to access the object and fields by performing CRUD (Create, Read, Update, Delete) and FLS (Field-Level Security) checks.
5. **Avoid User-Generated Queries in Apex**: Do not execute user-generated queries directly in Apex, as they bypass user permissions. Use the REST or SOAP API for safer execution if dynamic queries are necessary.
6. **Restrict Query Components**: Limit user-supplied input to object names, field names, and WHERE clause inputs. Avoid allowing users to supply other parts of the query.
7. **Use Secure APIs**: For more complex queries, consider using the REST or SOAP API to ensure safe execution.
These steps will help you securely construct dynamic SOQL queries while mitigating risks and preventing information disclosure vulnerabilities.