PHP: Connect to a MS SQL Server with Dynamic Port Allocation - php

I'm trying to browse a customer's Microsoft SQL server database with PHP but port 1433 is closed. Digging around I found out MSSQL can run in Dynamic Port Allocation mode, that means it will choose a random listen port at first execution, and will likely remain the same accross startup. I know I can find out the current port, but since likely is not always and I'd like to avoid searching for it again, is there any way to remotely discover the port to connect to?
From what I could understand by my searches this job is usually accomplished by SQLBrowser(.exe ?), but how to do this on Linux?
Update on the solution
While #Chris' answer was correct I was missing a simple but essential bit: on every change of odbc.ini you need to run:
odbcinst -i -s -f /etc/odbc.ini
to update system's DSN.
After that I could connect using
isql -v DSN_NAME username password
Troubleshooting
To check server instance:
tsql -H HOSTNAME_OR_IP -L
this will print server information, including instance names and port to which you should be able to connect using standard telnet or mssql client.

Given that your answer was correct, I had to do minor changes to make it work. I decided to write them here. Steps are basically the same. On Ubuntu/Debian:
apt-get install php5-sybase unixodbc tdsodbc
Edit /etc/odbcinst.ini and add driver details
[TDS]
Description = FreeTDS Driver
Driver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
Setup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so
Edit /etc/odbc.ini and enter connection details
[SQLSRV01]
Description = SQL Server test
Driver = TDS
Trace = No
Server = SERVER_IP\INSTANCE_NAME
TDS_Version = 9.0
#Database = DataBaseName
#ReadOnly = Yes
The last two parameters are optional. Driver must match what we wrote in odbcinst.ini. The Server directive must be in that syntax (of course SERVER_IP can be an hostname too).
According to UnixODBC the next step should not be necessary, but this is what made my installation work. Run the following command (every time odbc.ini is changed)
odbcinst -i -s -f /etc/odbc.ini
After this you should be able to connect using:
isql -v SQLSRV01 nome_utente password
Or via PHP:
$db = new PDO("dblib:host=SQLSRV01;dbname=DBNAME","USERNAME","PASSWORD");

Short answer:
ODBC drivers know to contact SQL server on port 1434 to find which dynamic port is associated with a named instance. user SERVERNAME\INSTANCENAME to connect.
Long answer:
I started here which led here and here.
Eventually I found this:
If you are using mssql with multiple instances and dynamic port
allocation you can use the following:
[SQLServer2008]
Description = Production Server
Driver = TDS
Trace = No
Server = servername\instance_name
TDS_Version = 8.0
Which seems to be echoed in a similar IBM Doc:
Question
SQLServer is setup to dynamically assign ports. In the .odbc.ini file,
the Address parameter is usually set to hostname colon port number
(Address=HostName:1433), but the port may change. How should we handle
this?
Answer
For the Address parameter value, instead of entering the hostname
colon port, enter the hostname a backslash and the server instance
name.
For example, in Unix/Linux, use the IBM SQLServer Wire Protocol driver
and enter the following in the .odbc.ini file in the DSN definition
for the connection to the SQLServer data source:
Address=HostName\Server_Instance_Name
For Windows, use the ODBC Data Sources Administrator to configure a
System DSN for the data source using the IBM SQLServer Wire Protocol
driver.
Note: The parameter is Server

Related

Connect PHP/phpMyAdmin to MySQL/MariaDB via named-pipe when skip-networking is enabled on Windows

Apache 2.4, PHP and MySQL or MariaDB Server are all running under Windows 10. phpMyAdmin is used in this environment.
my.ini has the configuration options skip-networking and enable-named-pipe set. So there is no way to connect via network.
HeidiSQL is connecting well using this configuration using . as hostname.
What options may be used for phpMyAdmin, to make him connect? I tried '.', 'localhost', '' and null. I also tried the options of my related posts.
How is this done using mysql::real_connect in PHP itself (which phpMyAdmin uses)? I think the docu is unclear for the socket parameter.
Related on stackoverflow:
PhpMyAdmin connect protocol PIPE
MySQL: Trying to connect via Named Pipe but getting "open_basedir restriction in effect"
My configuration (edit):
For mysql 80 you need to enter following named pipe for windows
$cfg['Servers'][$i]['socket'] = '\\.\pipe\MySQL80'
you should also check the phpmyadmn manual for connection and the mysql manual for more information on named pipes
To extent the answer:
The MySQL driver will always try to use sockets(linux) or named pipes(windows), when you add localhost as host, only when it can't use Sockets/named pipes, it will try to connect via TCP/IP, so under normal circumstances you don't need the line i wrote, only if you have a different name for the socket/named pipe you need to enter the socket/named pipe at that place.
so if you don't allow TCP/IP communnications for MySQL and the named pipe can't be established the connection would fail.

PHP connect to MS SQL with SSL

What I mean to achieve is very simple. I want to connect to an external MS SQL database from a PHP script over a secure connection. This has however proven problematic and, with three hours put in to research so far, I am at a loss.
The platform for the client is Ubuntu, which means I can not use SQLSRV.
The secure connection has been tested with different clients and it works fine.
I am currently using PDO and DBlib to connect to the database, which also works fine.
I was not able to find any method to force a secure connection. I have tried multiple other drivers, to no avail.
What are my options?
Edit: I am left with the following FreeTDS logs...
config.c:543: Got a match.
config.c:565: host = 'XXXXXXXXXX'
config.c:595: Found host entry XXXXXXXXXX.
config.c:599: IP addr is XXXXXXXXXX.
config.c:565: port = '1433'
config.c:565: encryption = 'require'
config.c:565: check certificate hostname = 'no'
config.c:629: UNRECOGNIZED option 'check certificate hostname' ... ignoring.
config.c:565: ca file = 'XXXXXXXXXX.pem'
config.c:629: UNRECOGNIZED option 'ca file' ... ignoring.
If you want to use PDO, you could set up PDO ODBC. You will need to setup the configuration files /etc/odbc.ini, /etc/odbcinst.ini and /etc/freetds/freetds.conf.
You'll also have to install unixodbc and freetds: apt-get install unixodbc tdsodbc.
You can see more info here: Connect PHP to MSSQL via PDO ODBC
EDIT: To enforce SSL in ODBC, add the Encrypt keyword and set it to true in your connection string. And setup your SQL Server to use SSL: https://support.microsoft.com/en-us/kb/316898
EDIT 2: According to the OP, adding encryption=require and check certificate hostname to freetds.config as per the following specification: http://www.freetds.org/userguide/freetdsconf.htm along with the above steps will fix the problem
Have you configured mssql_connect to use a secure connection? Look in your php.ini and verify the mssql.secure_connection parameter is set to on
[MSSQL]
mssql.secure_connection = On

PDO Error "Adaptive Server is unavailable" when connecting to MS SQL Database

I am trying to connect to a SQL server database that is running on a windows server. I am running this PHP code on a linux server (centos 7).
I am using the following pdo connection string to connect to the database.
$db = new PDO ("dblib:192.168.2.1;dbname=TestDB","username",'pass');
When i run the code i get the following exception. 'PDOException' with message 'SQLSTATE[HY000] Unable to connect: Adaptive Server is unavailable or does not exist (severity 9)'
I have tried to test the connection using tsql and i am able to connect to the database without any error. The following code gave me a list of all the tables in TestDB. It wouldnt work if i didng type use TestDB first.
tsql -S 192.168.2.1 -U username -P 'pass' -L TestDB
use TestDB
GO
select * FROM sys.Tables
GO
My freetds.conf file contains the following
[Server1]
host = 192.168.2.1
port = 1433
tds version = 8.0
I cannot figure out how i am able to connect using tsql, but cannot do the same when connecting with php.
The dblib driver is definitely installed.
print_r(PDO::getAvailableDrivers());
Array ( [0] => dblib [1] => mysql [2] => sqlite )
Answer
Found the cause of the problem. Turned out to be SELinux. The following commands fixed the issue.
setsebool -P httpd_can_network_connect 1
setsebool -P httpd_can_network_connect_db 1
You have the datasource name, you should make use of it:
$db = new PDO ("dblib:host=Server1;dbname=TestDB","username",'pass');
You are running linux right? I recommend giving odbc a shot.
Three things to check for you.
First try your connection using your defined port.
Instead of:
$db = new PDO ("dblib:192.168.2.1;dbname=TestDB","username",'pass');
try using this:
$db = new PDO("dblib:host=192.168.2.1:1433;dbname=TestDB","username",'pass');
Second, you should be sure if your SQL Server is configured to hear on port 1433. You can check this using the SQL Server Configuration Manager.
The third (if you run it on windows) thing you can check is one thing I find in the PHP docs. Inside a comment, another one mentioned the same error. Here is the answer which seems to work:
For PDO MSSQL connection issues, ensure that you have the updated version of ntwdblib.dll (currently 8.00.194 as of this post). Overwrite the existing (old) file or place it in the Windows system32 folder. The version that ships with PHP 5.2.X does not work. This is a well known issue apparently, but I had a really hard time finding information on this issue until I was able to lock in a file name. I hope that this helps someone.
Another possible issue could be SELinux if it's enabled. I've gotten some errors which are something familiar with this on my Ruby on Rails installation. You can give it a try by disabling SELinux and try it again.
When connecting using PDO, if you leave out the host= it uses the Unix domain sockets which could be causing your problem. Putting in the host= will connect to the database via TCP/IP.
So try change your line of code to:
$db = new PDO ("dblib:host=192.168.2.1;dbname=TestDB","username",'pass');
Or with the port aswell:
$db = new PDO ("dblib:host=192.168.2.1:1433;dbname=TestDB","username",'pass');
For me, the problem was using the IP address.
I changed it to "MyServerName/SQL2017DEV" and it connected

Connect to Oracle using PHP

I am trying to connect to Oracle database which is not on my PC. I have installed Xampp, PHP and Oracle instant Client. I have added a windows environmental path to C:\instantclient_11_1. The Oci8 is enabled too when I check from phpinfo(). I have added the extension extension_dir = C:\php-5.4.0\ext to the php.ini and also enabled extension=php_oci8_11g.dll. Then when I try to connect to the database using the code below:
<?php
$conn = oci_connect('username', 'password');
$query = 'select table_name from user_tables';
?>
It says Warning: oci_connect(): ORA-12560: TNS:protocol adapter error. Could anyone help?
The oci_connect call has an optional third parameter for the connection_string. As you aren't specifying TWO_TASK or LOCAL in your environment, as mentioned in the documentation, you have to provide that connection string so that PHP knows how to find and connect to your database.
The general pattern for easy connect syntax is:
$conn = oci_connect('username', 'password', '//hostname:port/service_name');
Where hostname is the name or IP address of the server the database is on - which will not be localhost since you said it is not on your PC; port is the listener port, which defaults to 1521 but could be something else (you can do lsnrctl status on the server to check); and service_name is the database service name, which may or may not be the same as the SID (you can do lsnrctl services to see the valid values on your server).
If you have an SQL Developer connection to the same database, the hostname and port will be shown in the connection settings. It may also show the service name, or may show the SID depending on how it was configured. The service name and SID might be the same. (If you can't run lsnrctl but have sufficient privileges you can try select value from v$parameter where name = 'service_names', but it's likely you won't be able to see that view).

Connect to a MySQL server over SSH in PHP

I have my database on remote Linux machine, and I want to connect using SSH and PHP functions (I am currently using ssh2 library for that). I tried using mysql_connect, but it gives me can't access (although I have granted permission)
when I tried using this function:
$connection = ssh2_connect('SERVER IP', 22);
ssh2_auth_password($connection, 'username', 'password');
$tunnel = ssh2_tunnel($connection, 'DESTINATION IP', 3307);
$db = mysqli_connect('127.0.0.1', 'DB_USERNAME', 'DB_PASSWORD',
'dbname', 3307, $tunnel)
or die ('Fail: '.mysql_error());
I got error "mysqli_connect() expects parameter 6 to be string, resource given". How can I resolve this?
SSH Tunnel Solution
Set up an SSH tunnel to your MySQL database server (through a Jumpbox proxy for security).
(A) GUI Tools
Depending on your requirements, you can use a GUI MySQL client with SSH Tunnelling support built-in such as Visual Studio Code Forwarding a port / creating SSH tunnel, TablePlus or use PuTTY to setup local port forwarding.
On macOS, I like Secure Pipes or TablePlus.
(B) Command Line
Step 1.
ssh -fNg -L 3307:10.3.1.55:3306 username#ssh-jumpbox.com
The key here is the '-L' switch which tells ssh we're requesting local port forwarding.
I've chosen to use port 3307 above. All traffic on my local machine directed to this port will now be 'port-forwarded' via my ssh client to the ssh server running on the host at address ssh-jumpbox.com.
The Jumpbox ssh proxy server will decrypt the traffic and establish a network connection to your MySQL database server on your behalf, 10.3.1.55:3306, in this case. The MySQL database server sees the connection coming in from your Jumpbox' internal network address.
Local Port Forwarding Syntax
The syntax is a little tricky but can be seen as:
<local_workstation_port>:<database_server_addr_remote_end_of_tunnel>:<database_server_port_remote_end> username#ssh_proxy_host.com
If you're interested in the other switches, they are:
-f (go to background)
-N (do not execute a remote command)
-g (allow remote hosts to connect to local forwarded ports)
Private Key Authentication, add (-i) switch to above:
-i /path/to/private-key
Step 2.
Tell your local MySQL client to connect through your SSH tunnel via the local port 3307 on your machine (-h 127.0.0.1) which now forwards all traffic sent to it through the SSH tunnel you established in step 1.
mysql -h 127.0.0.1 -P 3307 -u dbuser -p passphrase
Data exchange between client and server is now sent over the encrypted SSH connection and is secure.
Security note
Don’t tunnel directly to your database server. Having a database server directly accessible from the internet is a huge security liability. Make the tunnel target address the internet address of your Jumpbox/Bastion Host (see example in step 1) and your database target the internal IP address of your database server on the remote network. SSH will do the rest.
Step 3.
Now connect up your PHP application with:
<?php
$smysql = mysql_connect( "127.0.0.1:3307", "dbuser", "passphrase" );
mysql_select_db( "db", $smysql );
?>
Credit to Chris Snyder's great article detailing ssh command line tunnelling for MySQL connectivity.
Unfortunately, the ssh2 tunnel offered by php doesn't seem able to handle a remote mysql connection as you cannot specify the local port to tunnel (it only works with port 22 or whatever ssh port your remote server is running on). My solution to this is to just open the tunnel via exec() operator and connect as usual from there:
exec('ssh -f -L 3307:127.0.0.1:3306 user#example.com sleep 10 > /dev/null');
$mysqli = new mysqli('127.0.0.1', 'user', 'password', 'database', '3307');
I was looking for the same thing, but I prefer not to need external commands and manage external processes. So at some point I thought, how hard can it be to write a pure PHP MySQL client which can operate on any PHP stream? It took me about half a day, based on the MySQL protocol documentation.
https://gist.github.com/UCIS/4e509915ed221660e58f5169267da004
You can use this with the SSH2 library or any other stream:
$ssh = ssh2_connect('ssh.host.com');
ssh2_auth_password($ssh, 'username', 'password');
$stream = ssh2_tunnel($ssh, 'localhost', 3306);
$link = new MysqlStreamDriver($stream, 'SQLusername', 'SQLpassword', 'database');
$link->query('SELECT * FROM ...')->fetch_assoc();
It does not implement the complete mysqli API, but it should work with all plain-text queries. Please be careful if you decide to use this, I haven't thoroughly tested the code yet and the string escaping code has not been reviewed.
According to the docs, that last parameter is supposed to be a socket or pipe name, something like '/var/run/mysql/mysql.sock'. Since you're not connecting using a UNIX socket, that doesn't apply to you... so try just leaving it out.
I believe that the reason I (and I suppose most people) have a problem getting this to work is because the user in the mysql server is set to only allow from "localhost" and not 127.0.0.1, the IP address of localhost.
I got this to work by doing the following steps:
Step 1: Allow 127.0.0.1 host for target user
SSH normally into your server, and log in with the mysql root user, then issue the command:
GRANT ALL ON yourdbname.* TO yourdbuser#127.0.0.1 IDENTIFIED BY 'yourdbpassword';
The key of course, is specifying 127.0.0.1 above.
Step 2: Start local SSH tunnel to MySQL
You can now start your local SSH tunnel to the remote MySQL server, like so:
ssh -vNg -L 33306:127.0.0.1:3306 sshuser#remotehost.com
-v makes ssh operate in verbose mode, which kind of helps to see what's happening. For example, you'll see debugging output like this in your terminal console when you attempt a connection:
debug1: client_input_global_request: rtype hostkeys-00#openssh.com want_reply 0
debug1: Connection to port 33306 forwarding to 127.0.0.1 port 3306 requested.
and output like this when you close the connection:
debug2: channel 2: is dead
debug2: channel 2: garbage collecting
debug1: channel 2: free: direct-tcpip: listening port 33306 for 127.0.0.1 port 3306, connect from 127.0.0.1 port 52112 to 127.0.0.1 port 33306, nchannels 3
-N makes ssh issue no commands, and just wait instead after establishing connection.
-g allows remote hosts to connect to local forwarded ports. Not completely sure if this is necessary but it might be useful for multiplexing multiple connections through the same SSH tunnel.
-L This is the main parameter that specifies the local port 33306 to connect to the remote host's local IP address 127.0.0.1 and the remote host's mysql port, usually 3306.
You can use whatever mechanisms / other parameters needed after this to connect through SSH to your remote host. In my case, I use key files configured in my ~/.ssh/config so I just need to specify user#host to get in.
Issuing the command like this runs SSH in the foreground, so I can easily close it with Ctrl + C. If you want to run this tunnel in a background process you can add -f to do this.
Step 3: Connect from PHP / other mysql compatible methods
The SSH tunnel running from above on your localhost will behave exactly like as if your mysql was running on 127.0.0.1. I use port 33306 (note the triple 3) which lets me run my local sql server at its normal port. You can now connect as you would normally do. The mysql command on the terminal looks like this:
mysql -h 127.0.0.1 -P 33306 -u yourmysqluser -p
where -P (capital P) specifies the port where your SSH tunnel's local end is accepting connections. It's important to use the 127.0.0.1 IP address instead of localhost because the mysql cli will try to possibly use the linux socket to connect.
For PHP connection strings, my data source name string (for my Yii2 config) looks like this:
'dsn' => 'mysql:host=127.0.0.1;dbname=yourdbname;port=33306',
Passwords, and usernames are specified as normal.
even i tried it by doing ssh both by root credentials and and public private key pair, but it allows me to conect through command line but not through php code. I tried by creating tunnel also(by using ssh2 functions),ans running shell commands from php code(system,exec etc), nothing worked. Finally i tried ssh2 function to execute shell command and it finally worked :) Here is code, if it helps you:----
$connection = ssh2_connect($remotehost, '22');
if (ssh2_auth_password($connection, $user,$pass)) {
echo "Authentication Successful!\n";
} else {
die('Authentication Failed...');
}
$stream=ssh2_exec($connection,'echo "select * from zingaya.users where id=\"1606\";" | mysql');
stream_set_blocking($stream, true);
while($line = fgets($stream)) {
flush();
echo $line."\n";
}
it worked for me try this if want to use php functions specifically.
Make sure that your username and password that you are connecting with has the right hostname permissions. I believe you can use '%' for a wildcard. Also if you are connecting to remote machine (which I would assume you are if you are trying to ssh into it) that is not on your local network you will have to forward the ports on your router where the server is for outside traffic to be able to connect to it.
http://www.lanexa.net/2011/08/create-a-mysql-database-username-password-and-permissions-from-the-command-line/
When using ssh2 to connect, since there is no special function for php to use ssh2 to connect to mysql, we can only use the traditional socket and mysql protocol for data interaction.
When we use the ssh2_tunnel method, if the creation is successful, a socket object will be returned, then we can use this socket object for mysql data interaction. Of course, we need to understand the mysql protocol, which is the so-called "handshake packet"
......
$tunnel = ssh2_tunnel(....)
......
//we need to construct the mysql data packet and then use fwrite to transfer this packet to mysql host
fwrite($tunnel, MYSQLDATAPACKET)
when we use ssh2_connect method to connect the mysql host, it will return a MySQL handshake packet, it looks like this:
a 5.5.5-10.3.34-MariaDB-cll-lve�WfyP`uKW����RtscuF:/}J7umysql_native_password!��
here is the mysql handshake packet structure:
size(byte) description
1 protocol version
n server version
4 connection id
8 auth-plugin-data-part-1
1 filler
2 capability flags
1 character set
2 status flags
2 capability flags
1 length of auth-plugin-data
10 reserved
13 auth-plugin-data-part-2
n auth-plugin name
so we should parse the byte array by using such structure.
After receving the Mysql handshake, which means we have connected the mysql host, and now we need to login mysql, so we need to construct the mysql send data packet
here is the data structure: (HandshakeResponse41)
4 capability flags, CLIENT_PROTOCOL_41 always set
4 max-packet size
1 character set
string[23] reserved (all [0])
string[NUL] username
if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA {
lenenc-int length of auth-response
string[n] auth-response
} else if capabilities & CLIENT_SECURE_CONNECTION {
1 length of auth-response
string[n] auth-response
} else {
string[NUL] auth-response
}
if capabilities & CLIENT_CONNECT_WITH_DB {
string[NUL] database
}
if capabilities & CLIENT_PLUGIN_AUTH {
string[NUL] auth plugin name
}
if capabilities & CLIENT_CONNECT_ATTRS {
lenenc-int length of all key-values
lenenc-str key
lenenc-str value
if-more data in 'length of all key-values', more keys and value pairs
}
See Also: https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse41
Okay, so we have connected the mysql host successfully, and then you can query the database.
Here is the sample code for the Mysql Handshake and Mysql Socket Login:
function parseMysqlHandshakePack($hex_string)
{
$dataField = {
"protocol_version" => "",
"server_version" => "",
"thread_id" => "",
"salt1" => "",
"salt2" => "",
"salt" => "",
}
$dataField["protocol_version"] = UtiliHelper::HexToInt(UtiliHelper::HexSub($hex_string,0,1));
$dataField["server_version"] = UtiliHelper::HexToStr(UtiliHelper::HexSub($hex_string,1,7));
$dataField["thread_id"] = UtiliHelper::HexToInt(UtiliHelper::HexSub($hex_string,8,4));
$dataField["salt1"] = UtiliHelper::HexSub($hex_string,12,8);
$dataField["salt2"] = UtiliHelper::HexSub($hex_string,39,12);
$dataField["salt"] = $dataField["salt1"] . $dataField["salt2"];
return $dataField;
}
function constructMysqlLoginPacket($username, $password, $database, $salt){
$tags = [
"power_tag" => "",
"power_ext" => "",
"max_length" => "",
"charset" => "",
"fill_pad" => "",
"username" => "",
"password" => "",
"database" => "",
"client_auth_plugin" => "",
"payload" => ""
];
$tags['power_tag'] = "8da2";
$tags['power_ext'] = "0b00";
$tags['max_length'] = "000000c0";
$tags['charset'] = "08";
$tags['fill_pad'] = "0000000000000000000000000000000000000000000000";
$tags['client_auth_plugin'] = "6d7973716c5f6e61746976655f70617373776f726400";
$tags["payload"] = "150c5f636c69656e745f6e616d65076d7973716c6e64";
$tags['username'] = UtiliHelper::StrToHex($username)."0014";
$tags['password'] = UtiliHelper::encryptionPass($password,$salt);
$tags['database'] = UtiliHelper::StrToHex($database)."00";
$message = "";
foreach ($tags as $tagv){
$message .= $tagv;
}
return UtiliHelper::IntToHex(strlen($message)/2)."01".$message;
}
the $salt parameter came from the Handshake phase, so you need to parse the mysql handshake message and get the salt.
The UtiliHelper is a custom class which is from a 3rd party party project in Github: https://github.com/gphper/PHPMysql/blob/master/src/UtiliHelper.php

Categories