I'm building a system where I'm pondering making it possible to build plugins by third parties (this would be parties that will have access to the codebase, since it's not encrypted or "broken" in any way, so they can of course circumvent the circumvention, but just to satisfy my curiosity): Is it possible, in any way, to disable regular db-access via mysql_, mysqli_ and PDO, unless they're using the original DB-class provided in the system? I don't want to use any third party libraries, I'm just wondering if there is a way to set run-time limits on which PHP-functions/classes are available for use in the script. As said, of course it would be possible for someone who has access to the entire code-base to bypass this, but it would make it a little simpler if I'm gonna have to go through everything made by third parties to veto or accept said plugins.
That comes down to restricting the scope of the UN/PW to the object in question, and restricting that object's scope as well. In my own systems to accomplish this, I have a dbSettings.php that does this: <?php define('SQLTYPE', 'mysql'); function dbSettings() { if (!defined('dbSettingsSent')) { define('dbSettingsSent', 1); if ( (count($d = debug_backtrace()) == 3) && isset($d[2]) && ($d[2]['function'] == 'main') && (str_replace( '/dbSettings.php', '/index.php', str_replace('\\', '/', $d[0]['file']) ) == str_replace('\\', '/', $d[2]['file'])) ) return [ // BEGIN USER EDITS 'dbUser' => 'jake', 'dbPass' => 'ekrjtlkjnwkjerbsdoi', 'dbName' => 'demo', // 'dbHost' => 'localHost', 'dbSock' => '/opt/lampp/var/mysql/mysql.sock' ]; } die('Hacking attempt detected for dbSettings.php!'); } ?> Code (markup): The use of debug_backtrace() allows me to be certain that only the main() function inside index.php (which using rewrite rules is the ONLY content generating file users can call) is allowed to ask for the login information. Anything else calls that routine or tries to ask for that information, it fails. I then have main() pass that to my extended PDO object: final class Database extends PDO { public static function exceptionHandler($e) { die($e->getMessage()); } // Database::exceptionHandler public function __construct($settings) { if (defined('DB_CONSTRUCTED')) die( 'Hacking Attempt detected, attempt to connect to database more than once' ); define('DB_CONSTRUCTED',1); if (!in_array(SQLTYPE, PDO::getAvailableDrivers())) die( 'Unable to continue, unsupported SQL type "' . SQLTYPE . '"' ); set_exception_handler([__CLASS__,'exceptionHandler']); parent::__construct( SQLTYPE . ':dbname=' . $settings['dbName'] . ';' . ( isset($settings['dbSock']) ? 'unix_socket= ' . $settings['dbSock'] : 'host=' . $settings['dbHost'] ), $settings['dbUser'], $settings['dbPass'] ); restore_exception_handler(); } Code (markup): that under index.php in main I simply: <?php require_once('dbSettings.php'); require_once('database.php'); function main() { $db = new Database(dbSettings()); // do whatever else your program is doing here } main(); ?> Code (markup): $db being the only valid connection to PDO from that point on. As the passwords are restricted in what they can be passed to, and can only be used ONCE, from that point on only things you pass $db to can access it... so you don't "need" to block PDO, mysqli or mysql - no password so they can't connect should be sufficient. KEEPING the scope isolated on $DB can also be tricky, which is why as sad and pointless as this should be: function safeInclude($filename) { require_once($filename); } Code (markup): You'd want to use that on any libraries or module you load inside main() or by said modules so that main()'s scope is not passed to any files you load after that point; breaking the entire reason for $db to be a local var in the first place. Even so and as you noted if someone uses readfile or fopen, well, even all this does jack **** for hiding that connection info... hence why I still say php should block file operations from loading any file ending in .php, and include/require should block loading any file that does not have the .php extension... and PHP should have the option to load a library WITHOUT passing scope. But no, that would make too much sense; but that's PHP for you! Insecure by design. Another step I'd probably take is to extend PDOStatement to let you add "execute locking" of result sets. That way if you want to pass a result set to a skin it can be set to lock-out a second execute. That's what I do in my system that also does away with static queries and uses named queries instead -- that way arbitrarily run PHP code CANNOT make their own queries without first registering them in the query table by name.
There is one way which is to use the PECL runkit extension and using the method runkit_function_remove, which by default, only userspace functions may be removed, renamed, or modified. In order to override internal functions, you must enable the runkit.internal_override setting in php.ini.
Thanks for the input, folks. @deathshadow: I'll take a look at that as soon as I have time, and @ThePHPMaster I'll look into that as well, although that might be limited by the server this will run on, so I'm not sure if that's a real solution.
That's the problem with PECL and PEAR extensions, if you're writing a commercial package where you don't have dictator level control over what's installed on the server you really can't deploy any of them. If you're self hosting on something like a unmanaged VPS where you have full control of everything, then sure knock yourself out with those, but if you're writing code for others where Christmas only knows what sleazy shared fly-by-night hosting they might try to run it on, you really can't deploy ANY of that in your codebase. It's why even though I have that level of control on my server, I still won't deploy anything from PEAR or PECL in my codebase since, well, you never know. SUCKS.