Louis_XI
Louis_XI

Reputation: 53

phpMyAdmin token authentification system

I recently had the idea to create a login system to PhpMyAdmin with a token created from login credentials stored in a database. The url would look like this :

pma.example.com/index.php?token=WCxuFWj1QtGGapZhfSPS9Eo1Q9q666sR

The token contains 32 randomly generated alphanumeric characters.

So I have an API made in Python with Flask which communicates locally with PhpMyadmin and the different applications with which has two routes:

  1. 127.0.0.1:4040/api/v1/create_token [POST] This request has as JSON argument database_host_id for the MySQL host ID to connect defined in PhpMyAdmin configuration file, database_username which is as written the database user to connect and finally database_host which is the database password. The response have an token argument for the client to receive the new generated token. The JSON sent and received should look like this :
JSON received by the API :
{
  "database_host_id": 1,
  "database_username": "pma",
  "database_password: "4wAyS8",
}

JSON sent to the client:
{
  "token": "WCxuFWj1QtGGapZhfSPS9Eo1Q9q666sR"
}
  1. 127.0.0.1:4040/api/v1/get_database [POST] This request has a JSON argument token to know from which token to retrieve the connection information. So the response will have all this information along with database_host_id, database_username and database_password. The JSON sent and received should look like this :
JSON received by the API :
{
  "token": "WCxuFWj1QtGGapZhfSPS9Eo1Q9q666sR"
}

JSON sent to the client :
{
  "database_host_id": 1,
  "database_username": "pma",
  "database_password: "4wAyS8",
}

It's nice to have an API but if you don't use it, it's useless, so I searched in the source code of PhpMyAdmin how the authentication system worked.

I could understand that :

$auth_plugin = Plugins::getAuthPlugin();
$auth_plugin->authenticate();

So now I had to modify the AuthenticationCookie.php script so that when token was contained in the URL, it could retrieve the information from the API and then use it to connect to the database associated with that token.

And that's where I get stuck because despite my PHP basics, I can't get what I'm doing to work. I could notice despite my attempts that when I retrieve the token from the URL, it was not at all what was defined, it was as if it was encrypted.

So here are my questions:

  1. Should I encrypt the password even though the API only communicates locally?
  2. Shouldn't I just use the SignOn connection method for my MySQL host?
  3. Is it safe to do what I am trying to do?

Information:

I would appreciate any kind of help in my reasoning to do or for any answers to my questions

Upvotes: 1

Views: 2106

Answers (1)

Louis_XI
Louis_XI

Reputation: 53

I finally ended up making a SignOn script associated with my server that gets the token from the URL, asks the API for the token information and then connects with it. It works perfectly.

<?php
/**
 * Single signon for phpMyAdmin
 *
 * This is just example how to use session based single signon with
 * phpMyAdmin, it is not intended to be perfect code and look, only
 * shows how you can integrate this functionality in your application.
 */

declare(strict_types=1);

/* Use cookies for session */

ini_set('session.use_cookies', 'true');
/* Change this to true if using phpMyAdmin over https */
$secure_cookie = false;
/* Need to have cookie visible from parent directory */
session_set_cookie_params(0, '/', '', $secure_cookie, true);
/* Create signon session */
$session_name = 'SignonSession';
session_name($session_name);
// Uncomment and change the following line to match your $cfg['SessionSavePath']
//session_save_path('/foobar');
@session_start();

/* Was data posted? */
if (isset($_REQUEST['token'])) {
    $data = array(
        'token'=> $_REQUEST['token']
    );
    $payload = json_encode($data);
    $curl = curl_init('http://127.0.0.1:4040/api/v1/get_database');
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    $response = curl_exec($curl);

    curl_close($curl);

    $response = json_decode($response, true);
    
    /* Store there credentials */
    $_SESSION['PMA_single_signon_user'] = $response->database_username;
    $_SESSION['PMA_single_signon_password'] = $response->database_password;
    $_SESSION['PMA_single_signon_host'] = $_POST['host'];
    $_SESSION['PMA_single_signon_port'] = $_POST['port'];
    /* Update another field of server configuration */
    $_SESSION['PMA_single_signon_cfgupdate'] = ['verbose' => 'Signon test'];
    $_SESSION['PMA_single_signon_HMAC_secret'] = hash('sha1', uniqid(strval(rand()), true));
    $id = session_id();
    /* Close that session */
    @session_write_close();
    /* Redirect to phpMyAdmin (should use absolute URL here!) */
    header('Location: ../index.php');
} else {
    /* Show simple form */
    header('Content-Type: text/html; charset=utf-8');

    echo '<?xml version="1.0" encoding="utf-8"?>' . "\n";
    echo '<!DOCTYPE HTML>
<html lang="en" dir="ltr">
<head>
<link rel="icon" href="../favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="../favicon.ico" type="image/x-icon">
<meta charset="utf-8">
<title>Loading...</title>
</head>
<body>';

    if (isset($_SESSION['PMA_single_signon_error_message'])) {
        echo '<p class="error">';
        echo $_SESSION['PMA_single_signon_error_message'];
        echo '</p>';
    }

    echo '
<h1>Loading...</h1>
</body>
</html>';
}

I know it's not finished but I plan to improve it. So this is the solution I found.

Upvotes: 2

Related Questions