1. Advertising
    y u no do it?

    Advertising (learn more)

    Advertise virtually anything here, with CPM banner ads, CPM email ads and CPC contextual links. You can target relevant areas of the site and show ads based on geographical location of the user if you wish.

    Starts at just $1 per CPM or $0.10 per CPC.

How to disable / circumvent db-access

Discussion in 'PHP' started by PoPSiCLe, Sep 1, 2015.

  1. #1
    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.
     
    PoPSiCLe, Sep 1, 2015 IP
  2. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #2
    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.
     
    Last edited: Sep 1, 2015
    deathshadow, Sep 1, 2015 IP
  3. ThePHPMaster

    ThePHPMaster Well-Known Member

    Messages:
    737
    Likes Received:
    52
    Best Answers:
    33
    Trophy Points:
    150
    #3
    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.
     
    ThePHPMaster, Sep 1, 2015 IP
  4. PoPSiCLe

    PoPSiCLe Illustrious Member

    Messages:
    4,623
    Likes Received:
    725
    Best Answers:
    152
    Trophy Points:
    470
    #4
    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.
     
    PoPSiCLe, Sep 4, 2015 IP
  5. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #5
    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.
     
    deathshadow, Sep 5, 2015 IP