Saturday, March 21, 2015

C# Chat Client v2

In this post I want to present a somewhat improved version of the chat client of the previous post, which uses instead of the PHP class mysql the class mysqli and thus prevents SQL-injections through prepared statements.
The PHP scripts now look as follows:

register.php (http://bloggeroliver.bplaced.net/Chat/Secure/register.php):

<?php
include("connect.php");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$stmt = $conn->prepare("SELECT username FROM Users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();

$stmt->store_result();
$num_rows = $stmt->num_rows;

if ($num_rows > 0) {
     echo "Existing";
}
else {
     $stmt = $conn->prepare("INSERT INTO Users (username, password) VALUES (?, ?);");
     $stmt->bind_param("ss", $username, $hashedpw);
     $stmt->execute();
     echo "Success";
}
?>

login.php (http://bloggeroliver.bplaced.net/Chat/Secure/login.php):

<?php
session_start();

include("connect.php");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$stmt = $conn->prepare("SELECT password FROM Users WHERE username = ? LIMIT 1");
$stmt->bind_param("s", $username);
$stmt->execute();

$stmt->bind_result($rowpassword);
$row = $stmt->fetch();

if($rowpassword == $hashedpw) {
    $_SESSION["username"] = $username;
    echo "LoginGood";
}
else {
    echo "LoginBad";
}
?>

send.php (http://bloggeroliver.bplaced.net/Chat/Secure/send.php):

<?php
session_start();

include("connect.php");

$Recipient = $_POST["Recipient"];
$Message = $_POST["Message"];
$Sender = $_SESSION['username'];

if(!isset($_SESSION['username'])) {
     echo "Login first.";
     exit;
}  

$stmt = $conn->prepare("INSERT INTO Messages () VALUES (?, ?, ?);");
$stmt->bind_param("sss", $Sender, $Recipient, $Message);
$stmt->execute();
    
?>

receive.php (http://bloggeroliver.bplaced.net/Chat/Secure/receive.php):

<?php

session_start();

include("connect.php");

if(!isset($_SESSION['username']))
   {
   echo "Bitte erst login";
   exit;
   }   
    
$Recipient = $_SESSION['username'];

$stmt = $conn->prepare("SELECT Sender, Message FROM Messages WHERE Recipient = ?");
$stmt->bind_param("s", $Recipient);
$stmt->execute();
    
$stmt->bind_result($sender, $message);
while($row = $stmt->fetch())
   {
          echo "$sender<br />";
          echo "$message<br />";
          $stmt->bind_result($sender, $message);
   }

$stmt = $conn->prepare("DELETE FROM Messages WHERE Recipient = ?");
$stmt->bind_param("s", $Recipient);
$stmt->execute();
?>

In the code of the C# client only one line has to be changed, which is the variable ServerUrl. It has to be set to the new location of the script folder:
string ServerUrl = "http://bloggeroliver.bplaced.net/Chat/Secure/";

The complete source code can be downloaded here, the complete chat program set to my server can be installed via this link.
Also in this chat I am called bloggeroliver and am looking forward to your messages!

Now I here want to discuss the general security of this chat. Roughly speaking I see 2 security issues:

  • On login the password is transmitted unencrypted. An attacker, who has access to the network, could read this out or simply send the same message again to log in. I will handle this problem in the next post. One possibility would be the usage of, for example, TLS / SSL to encrypt the connection. For this though a (probably not free) certificate has to be acquired, I would like to solve this using "C# means" only. Therefor I will implement a Challenge / Response Authentification with a public key.
  • Further the messages are transmitted unencrypted as well, so the same problem as above occurs, every attacker with access to the network could read the messages. Further the database owner could read everything. But I would not call this a vulnerability (and also if, less important than the first one), just a feature. If no encryption of the messages is desired, one can leave it like this (then not directly the security of the whole system is endangered, just no confidentiality is ensured), otherwise implement an encryption (which is of course recommended). In the overnext post I will solve also this with a Public key method.

C# Chat Client v1

In today's post I want to connect all the previous posts to present a first version of my chat client. The actual client is written in C# and looks as follows:


















There is a central server on which PHP scripts lie. These are used for the user management (registration / login) and the message management. All files are saved into a MySQL database on the server. So there is one table for the users and one for the messages. When sending a message the corresponding PHP script is called by the client, which then writes the messages into the database. The client of the receiver periodically calls a script for checking for messages, this then forwards the possible messages to him.

Basically there are 2 classes: The class Form1 for the representation of the graphical interface, and the class SimpleChatClient, which represents the core of the chat client and does the communication with the server.
The design of the graphical interface is the same as in the Advanced Facebook Chat, I do not want to discuss this code here but instead focus on the core of the chat.

In the class SimpleChatClient the function HTTPPost() is of great importance. It creates HTTP POST requests with the HttpWebRequest class and also uses cookies, so we can use PHP sessions. We pass the target URL and parameters to the function. The functions Register(), Login(), Send() and Receive() do what their names say and call HTTPPost() with the right arguments. The functions are very easy, they bascially just forward the request to the corresponding PHP scripts and the (possible) outputs to the client. The functions Send() and Receive() should only be available to authorized users, therefor the corresponding PHP scripts check wether the current user already started a session by loggin in. The global variable Cookies saves the cookie for the HTTP request, which is created when loggin in.

Let's come to the PHP code on the server. All PHP scripts have at the beginning the line include("connect.php");, which includes the script connect.php. This simply prepares the MySQL connection via $conn = new mysqli(server, username, password, database);. I will not publish the data here, because I want that the client can indeed be used (safely).
Of course everybody can create his own server and for that just has to change the script connect.php (and of course also the URLs in the code), in the database the following tables are needed: Users, with the attributes username and password, and Messages with Sender, Recipient and Message.
For simplicity here I again first want to use PHP code using the class mysql, and then in the next post introduce a secure variant with mysqli.
The files register.php and login.php are the ones presented in the post about a login system in PHP:

register.php (http://bloggeroliver.bplaced.net/Chat/Insecure/register.php):

<?php
include("connect.php");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$checkuser = mysql_query("SELECT username FROM Users WHERE username = '$username'");
$num_rows = mysql_num_rows($checkuser);

if ($num_rows > 0) {
     echo "Existing";
}
else {
     mysql_query("INSERT INTO Users (username, password) VALUES ('$username', '$hashedpw');");
     echo "Success";
}
?>

login.php (http://bloggeroliver.bplaced.net/Chat/Insecure/login.php):

<?php
session_start();

include("connect.php");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$result = mysql_query("SELECT username, password FROM Users WHERE username = '$username' LIMIT 1");
$row = mysql_fetch_object($result);
    
if($row->password == $hashedpw) {
    $_SESSION["username"] = $username;
    echo "LoginGood";
}
else {
    echo "LoginBad";
}
?>

In the file send.php (http://bloggeroliver.bplaced.net/Chat/Insecure/send.php) first for a valid session is checked, and if this exists the submitted message is written to the database, the logged in user is entered as sender:

<?php
session_start();

include("connect.php");

$Recipient = $_POST["Recipient"];
$Message = $_POST["Message"];
$Sender = $_SESSION['username'];

if(!isset($_SESSION['username'])) {
     echo "Login first.";
     exit;
}

mysql_query("INSERT INTO Messages () VALUES ('$Sender', '$Recipient', '$Message');");
?>

In receive.php (http://bloggeroliver.bplaced.net/Chat/Insecure/receive.php) again for a valid session is checked, then all messages with the current user as recipient are read from the database, returned and eventually deleted:

<?php

session_start();

include("connect.php");

if(!isset($_SESSION['username']))
   {
   echo "Bitte erst login";
   exit;
   }
    
$Recipient = $_SESSION['username'];

$eintrag = "SELECT Sender, Message FROM Messages WHERE Recipient = '$Recipient'";
$eintragen = mysql_query($eintrag);
while($row = mysql_fetch_object($eintragen))
   {
          echo "$row->Sender<br />";
          echo "$row->Message<br />";
   }
$delete  = "DELETE FROM Messages WHERE Recipient = '$Recipient'";
$eintragen2 = mysql_query($delete);
?>

Now the code of the C# client:

Form1.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Net;
using System.IO;

namespace SimpleChatClient
{
    public class SimpleChatClient
    {
        public class User
        {
            public string Username;

            public User(string username)
            {
                Username = username;
            }
        }

        public class Message
        {
            public string Sender;
            public string Msg;

            public Message(string sender, string message)
            {
                Sender = sender;
                Msg = message;
            }
        }

        User CurrentUser = null;
        CookieContainer Cookie = null;
        string ServerUrl = "http://bloggeroliver.bplaced.net/Chat/Insecure/";

        public string GetUsername()
        {
            return CurrentUser.Username;
        }

        private string HTTPPost(string url, string postparams)
        {
            string responseString = "";

            try
            {
                // performs the desired http post request for the url and parameters
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.CookieContainer = Cookie; // explicitely use the cookiecontainer to save the session

                string postData = postparams;
                byte[] data = Encoding.UTF8.GetBytes(postData);

                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
                request.ContentLength = data.Length;

                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }

                var response = (HttpWebResponse)request.GetResponse();

                responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

                return responseString;
            }
            catch
            {
                // MessageBox.Show("error" + url + postparams + responseString);
                return null;
            }
        }

        public string Register(string username, string password)
        {
            // register a new user
            return HTTPPost(ServerUrl + "register.php", "username=" + username + "&password=" + password);
        }

        public bool Login(string username, string password)
        {
            // login
            Cookie = new CookieContainer();
            string Login = HTTPPost(ServerUrl + "login.php", "username=" + username + "&password=" + password);
            if (Login == "LoginGood")
            {
                CurrentUser = new User(username);
                return true;
            }
            else
            {
                Cookie = null;
                return false;
            }
        }

        public void Logout()
        {
            // logout
            CurrentUser = null;
            Cookie = null;
        }

        public string Send(string recipient, string message)
        {
            // send
            return (HTTPPost(ServerUrl + "send.php", "Recipient=" + recipient + "&Message=" + message));
        }

        public List<Message> Receive()
        {
            // receive messages
            if (CurrentUser == null)
                return new List<Message>();
            string Messages = HTTPPost(ServerUrl + "receive.php", "");
            if (Messages == null)
                return new List<Message>();

            // message format is: sender<br />message<br />, split regarding to this in single messages
            string[] Splits = Messages.Split(new string[] { "<br />" }, StringSplitOptions.None);
            List<Message> Received = new List<Message>();
            for (int i = 0; i < Splits.Length - 1; i += 2)
            {
                Received.Add(new Message(Splits[i], Splits[i + 1]));
            }
            return Received;
        }
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        SimpleChatClient Client;
        bool LoggedIn = false;
        Dictionary<string, Chat> Chats = new Dictionary<string, Chat>(); // stores all active chats

        private void button1_Click(object sender, EventArgs e)
        {
            if (!LoggedIn)
            {
                Client = new SimpleChatClient();

                bool Login = (Client.Login(textBox1.Text, textBox2.Text));
                if (Login)
                {
                    // Login successful, enable chatting etc.
                    textBox3.Enabled = true;
                    button3.Enabled = true;
                    this.Height = 570;
                    tabControl1.Visible = true;
                    LoggedIn = true;
                    button1.Text = "Logout";
                    button2.Enabled = false;
                }
                else
                    MessageBox.Show("Login failed.");
            }
            else
            {
                // logout, disable chatting etc.
                Client = new SimpleChatClient();
                Chats = new Dictionary<string, Chat>();
                tabControl1.TabPages.Clear();
                textBox1.Text = "";
                textBox2.Text = "";
                button2.Enabled = true;
                textBox3.Enabled = false;
                button3.Enabled = false;
                this.Height = 176;
                tabControl1.Visible = false;
                LoggedIn = false;
                button1.Text = "Login";
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Client = new SimpleChatClient();
            string Register = Client.Register(textBox1.Text, textBox2.Text);
            if (Register == "Success")
            {
                MessageBox.Show("Account created.");
            }
            if (Register == "Existing")
            {
                MessageBox.Show("Username already in use.");
            }
        }

        private void button3_Click_1(object sender, EventArgs e)
        {
            ProvideChat(textBox3.Text);
        }

        private void ProvideChat(string name)
        {
            Chat Dummy;
            bool ChatExisting = Chats.TryGetValue(name, out Dummy);
            if (ChatExisting)
            {   // if chat already exists, make its tabpage active
                tabControl1.SelectedTab = Dummy.ChatPage;
                return;
            }
            else
            {
                // create new tabpage for the conversation
                TabPage NewPage = new TabPage(name);

                TextBox ChatWindow = new TextBox();
                ChatWindow.Left = 10;
                ChatWindow.Top = 10;
                ChatWindow.Width = 532;
                ChatWindow.Height = 180;
                ChatWindow.Multiline = true;
                ChatWindow.ScrollBars = ScrollBars.Vertical;
                ChatWindow.ReadOnly = false;
                NewPage.Controls.Add(ChatWindow);

                TextBox SendBox = new TextBox();
                SendBox.Left = 10;
                SendBox.Top = 200;
                SendBox.Width = 450;
                NewPage.Controls.Add(SendBox);
                SendBox.Name = "snd" + name;
                SendBox.Click += new EventHandler(SendBox_Click);
                SendBox.TextChanged += new EventHandler(SendBox_TextChanged);

                Button SendButton = new Button();
                SendButton.Left = 470;
                SendButton.Top = 200;
                SendButton.Text = "Send";
                SendButton.Name = "btn" + name;
                SendButton.Click += new EventHandler(SendButton_Click);
                NewPage.Controls.Add(SendButton);

                Chat NewChat = new Chat();
                NewChat.ChatWindow = ChatWindow;
                NewChat.SendBox = SendBox;
                NewChat.ChatPage = NewPage;
                NewChat.Partner = name;
                NewChat.SendButton = SendButton;

                NewPage.Name = "tpg" + name;
                tabControl1.SelectedIndexChanged += new EventHandler(tabControl1_SelectedIndexChanged);

                Chats.Add(name, NewChat);

                this.AcceptButton = NewChat.SendButton;

                if (tabControl1.InvokeRequired)
                {
                    tabControl1.Invoke(new Action(() =>
                    {
                        tabControl1.TabPages.Add(NewPage);
                        tabControl1.SelectedTab = NewPage;
                    }));
                }
                else
                {
                    tabControl1.TabPages.Add(NewPage);
                    tabControl1.SelectedTab = NewPage;
                }

                this.ActiveControl = NewChat.SendBox;
            }
        }

        private void tabControl1_SelectedIndexChanged(Object sender, EventArgs e)
        {
            // change tabpages / chats
            if (((TabControl)sender).SelectedIndex != -1)
            {
                string Receiver = ((TabControl)sender).TabPages[((TabControl)sender).SelectedIndex].Name.ToString().Substring(3, ((TabControl)sender).TabPages[((TabControl)sender).SelectedIndex].Name.ToString().Length - 3);
                this.AcceptButton = Chats[Receiver].SendButton;
                this.ActiveControl = Chats[Receiver].SendBox;
            }
        }

        private void SendBox_Click(Object sender, EventArgs e)
        {
            // click in textbox for sending
            string Receiver = ((TextBox)sender).Name.Substring(3, ((TextBox)sender).Name.Length - 3);
            Chat CurrentChat = Chats[Receiver];
            CurrentChat.NewMessage = false;
            CurrentChat.ChatPage.Text = CurrentChat.Partner;
        }

        private void SendBox_TextChanged(Object sender, EventArgs e)
        {
            string Receiver = ((TextBox)sender).Name.Substring(3, ((TextBox)sender).Name.Length - 3);
            Chat CurrentChat = Chats[Receiver];
            CurrentChat.NewMessage = false;
            CurrentChat.ChatPage.Text = CurrentChat.Partner;
        }

        private void SendButton_Click(Object sender, EventArgs e)
        {
            // send message
            string Receiver = ((Button)sender).Name.Substring(3, ((Button)sender).Name.Length - 3);
            Chat CurrentChat = Chats[Receiver];
            Client.Send(Receiver, CurrentChat.SendBox.Text);
            CurrentChat.ChatWindow.Text += Client.GetUsername() + ": " + CurrentChat.SendBox.Text + Environment.NewLine;
            CurrentChat.SendBox.Text = "";
            Chats[Receiver].ChatWindow.SelectionStart = Chats[Receiver].ChatWindow.TextLength;
            Chats[Receiver].ChatWindow.ScrollToCaret();
        }

        private void IncomingMessage(string NameSender, string message)
        {
            // rceive an incoming message and display it in the correct chat window
            ProvideChat(NameSender);

            Chats[NameSender].NewMessage = true;
            Chats[NameSender].ChatWindow.Invoke(new Action(() =>
            {
                Chats[NameSender].ChatWindow.Text += NameSender + ": " + message + Environment.NewLine;
                Chats[NameSender].ChatWindow.SelectionStart = Chats[NameSender].ChatWindow.TextLength;
                Chats[NameSender].ChatWindow.ScrollToCaret();
            }));
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            // periodically poll messages from server
            if (Client != null)
            {
                List<SimpleChatClient.Message> Messages = Client.Receive();
                foreach (SimpleChatClient.Message m in Messages)
                {
                    IncomingMessage(m.Sender, m.Msg);
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Height = 176;
        }

        private void timer2_Tick(object sender, EventArgs e)
        {
            // timer used for the blinking effect for new messages
            foreach (Chat c in Chats.Values)
            {
                if (c.NewMessage)
                {
                    string BlankName = "";
                    BlankName = BlankName.PadLeft(c.Partner.Length, ' ');
                    if (c.ChatPage.Text == BlankName)
                        c.ChatPage.Text = c.Partner;
                    else
                        c.ChatPage.Text = BlankName;
                }
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            if (tabControl1.SelectedIndex != -1)
            {
                string Current = tabControl1.TabPages[tabControl1.SelectedIndex].Name.ToString().Substring(3, tabControl1.TabPages[tabControl1.SelectedIndex].Name.ToString().Length - 3);
                tabControl1.TabPages.RemoveAt(tabControl1.SelectedIndex);
                Chats.Remove(Current);
            }
        }
    }

    public class Chat
    {
        public TextBox ChatWindow;
        public TextBox SendBox;
        public bool NewMessage = false;
        public TabPage ChatPage;
        public string Partner;
        public Button SendButton;
    }
  
}

Form1.Designer.cs:

namespace SimpleChatClient
{
    partial class Form1
    {
        /// <summary>
       /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
       /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
       /// Required method for Designer support - do not modify
       /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.button2 = new System.Windows.Forms.Button();
            this.button1 = new System.Windows.Forms.Button();
            this.label2 = new System.Windows.Forms.Label();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.button3 = new System.Windows.Forms.Button();
            this.textBox3 = new System.Windows.Forms.TextBox();
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.timer2 = new System.Windows.Forms.Timer(this.components);
            this.button4 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // button2
            //
            this.button2.Location = new System.Drawing.Point(125, 86);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(80, 25);
            this.button2.TabIndex = 11;
            this.button2.Text = "Register";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            //
            // button1
            //
            this.button1.Location = new System.Drawing.Point(25, 86);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(81, 25);
            this.button1.TabIndex = 10;
            this.button1.Text = "Login";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // label2
            //
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(271, 49);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(53, 13);
            this.label2.TabIndex = 9;
            this.label2.Text = "Password";
            //
            // textBox2
            //
            this.textBox2.Location = new System.Drawing.Point(25, 49);
            this.textBox2.Name = "textBox2";
            this.textBox2.PasswordChar = '*';
            this.textBox2.Size = new System.Drawing.Size(219, 20);
            this.textBox2.TabIndex = 8;
            //
            // label1
            //
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(271, 23);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(55, 13);
            this.label1.TabIndex = 7;
            this.label1.Text = "Username";
            //
            // textBox1
            //
            this.textBox1.Location = new System.Drawing.Point(25, 23);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(219, 20);
            this.textBox1.TabIndex = 6;
            //
            // tabControl1
            //
            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.tabControl1.Location = new System.Drawing.Point(0, 199);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(804, 329);
            this.tabControl1.TabIndex = 12;
            this.tabControl1.Visible = false;
            //
            // button3
            //
            this.button3.Enabled = false;
            this.button3.Location = new System.Drawing.Point(274, 150);
            this.button3.Name = "button3";
            this.button3.Size = new System.Drawing.Size(77, 32);
            this.button3.TabIndex = 13;
            this.button3.Text = "Start Chat";
            this.button3.UseVisualStyleBackColor = true;
            this.button3.Click += new System.EventHandler(this.button3_Click_1);
            //
            // textBox3
            //
            this.textBox3.Enabled = false;
            this.textBox3.Location = new System.Drawing.Point(25, 157);
            this.textBox3.Name = "textBox3";
            this.textBox3.Size = new System.Drawing.Size(219, 20);
            this.textBox3.TabIndex = 14;
            //
            // timer1
            //
            this.timer1.Enabled = true;
            this.timer1.Interval = 1000;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
            //
            // timer2
            //
            this.timer2.Enabled = true;
            this.timer2.Interval = 1000;
            this.timer2.Tick += new System.EventHandler(this.timer2_Tick);
            //
            // button4
            //
            this.button4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.button4.Location = new System.Drawing.Point(751, 157);
            this.button4.Name = "button4";
            this.button4.Size = new System.Drawing.Size(41, 26);
            this.button4.TabIndex = 15;
            this.button4.Text = "X";
            this.button4.UseVisualStyleBackColor = true;
            this.button4.Click += new System.EventHandler(this.button4_Click);
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(804, 528);
            this.Controls.Add(this.button4);
            this.Controls.Add(this.textBox3);
            this.Controls.Add(this.button3);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.textBox2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.tabControl1);
            this.Name = "Form1";
            this.Text = "SimpleChat";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.TabControl tabControl1;
        private System.Windows.Forms.Button button3;
        private System.Windows.Forms.TextBox textBox3;
        private System.Windows.Forms.Timer timer1;
        private System.Windows.Forms.Timer timer2;
        private System.Windows.Forms.Button button4;
    }
}

The complete project can be downloaded herehere you find the link to the install files (executing setup.exe will install the program).
I am also called bloggeroliver (casesensitive) in the chat and am looking forward to your messages!

In the next post I show, as mentioned, how to prevent MySQL Injections. Further I will also describe there other security issues of this chat and show its solution.

Friday, March 20, 2015

(Secure) PHP Login System (Protected Against SQL-Injections)

In a previous post I presented a login system with PHP. Due to the usage of the simple queries of the mysql class there it was vulnerable against SQL-injections though. Today I want to fix this by using the class mysqli and prepared statements, which were introduced in a previous post.
Since the principle of the login system stayed the same and the prepared statements were explained in the linked post, here simply the code:

register.php (http://bloggeroliver.bplaced.net/PHPExamples/LoginV2/register.php):

<?php
$conn = new mysqli("db4free.net", "csharptricks", "12345678", "csharptricks");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$stmt = $conn->prepare("SELECT username FROM Users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();

if ($stmt->num_rows > 0) {
     echo "Existing";
}
else {
     $stmt = $conn->prepare("INSERT INTO Users (username, password) VALUES (?, ?);");
     $stmt->bind_param("ss", $username, $hashedpw);
     $stmt->execute();
     echo "Success";
}
?>

login.php (http://bloggeroliver.bplaced.net/PHPExamples/LoginV2/login.php):

<?php
session_start();

$conn = new mysqli("db4free.net", "csharptricks", "12345678", "csharptricks");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$stmt = $conn->prepare("SELECT username, password FROM Users WHERE username = ? LIMIT 1");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($output1, $output2);
$row = $stmt->fetch();

if($output2 == $hashedpw) {
    $_SESSION["username"] = $username;
    echo "LoginGood";
}
else {
    echo "LoginBad";
}
?>

Since the interface to the outside stayed the same, of course the C# code for the login application from the previous post can also be reused.

Thursday, March 19, 2015

Secure Access to a MySQL Database with PHP

In a previous post I explained how one can access a MySQL database with PHP. There I chose a simple, deprecated variant for demonstration purposes, namely the class mysql. Nowadays the class mysqli should be used though, the i stands for improved.
I want to present especially one new, important function, namely the possibility to use so called prepared statements.
One reason for this is that the method of communication with the database from the previous posts allows a serious attack: An SQL-injection is possible. This means the injection and execution of code on the server. Why? Well, let's take a look at the communication with the SQL server: We used the POST parameters in the script, for example to get the entered username and write it to the database. An attacker now could insert a semicolon, quotation marks etc. in the username, this way finish the statement and continue with a new query, which would then also be executed, because the database simply gets to see 2 queries!
To prevent, or at least try to prevent, this (100% security never can be guaranteed), one could for example try to escape special characters like ; in the SQL query by hand. I wil here choose a higher abstraction level and use an existing method of the class mysqli, which does this for us. The keyword for this is prepared statements, which prepare a query and then bind the "clean" parameters to it. Prepared statements offer a very good protection against SQL-injections.
The basic principle of a prepared statement is the following:
First we create a new connection variable:

$conn = new mysqli(server, username, password, database);

Then we use this for the query:

$stmt = $conn->prepare("query ... ?");
$stmt->bind_param("s...", params ...);
$stmt->execute();

In the 1. line we prepare the query, in it we replace the parameters by ?. In the 2. line we bind the parameters to these place holders, the first string expresses the typ. s stands for string, i for integer, d for double. The next arguments are the parameters. Finally we execute the query.

To state a query and read its output, we also execute the 3 lines from above. The answer we get with the following structure:

$stmt->bind_result(params);
while($row = $stmt->fetch())
   {
echo params .... ;
$stmt->bind_result($sender, $message);
   }

I now want to rewrite the script from the previous post about MySQL with mysqli, I think the principle then should be clear:

<?php
$FirstName = $_POST["FirstName"];
$LastName = $_POST["LastName"];

$conn = new mysqli("db4free.net", "csharptricks", "12345678", "csharptricks");

$stmt = $conn->prepare("INSERT INTO Customers (FirstName, LastName) VALUES (?, ?);");
$stmt->bind_param("ss", $FirstName, $LastName);
$stmt->execute();
  
$stmt = $conn->prepare("SELECT FirstName, LastName FROM Customers;");
$stmt->execute();
$stmt->bind_result($output1, $output2);
while($row = $stmt->fetch())
   {
          echo "$output1<br />";
          echo "$output2<br />";
          $stmt->bind_result($output1, $output2);
   }
?>