Page 1 of 1

Security in guestbook

Posted: Sat Oct 21, 2006 5:46 am
by Bandy
Hi! This is my frist post in this forum and i hope many to come.

I done simple PHP guestbook using mysql on my website.
Now i get links from porn sites :evil: , how could i control who is posting or to stop this.
Only security i have in my guestbook is htmlspecialchars() function.

Thanks!

Posted: Sat Oct 21, 2006 6:04 am
by feyd
Care to post the code?

Posted: Sat Oct 21, 2006 6:32 am
by Bandy
Here it goes:

Code: Select all

<?php 
// include the database configuration and 
// open connection to database 
include 'library/config.php'; 
include 'library/opendb.php'; 

// check if the form is submitted 
if(isset($_POST['btnSign'])) 
{ 
    // get the input from $_POST variable 
    // trim all input to remove extra spaces 
    $name    = trim($_POST['txtName']); 
    $email   = trim($_POST['txtEmail']); 
    $url     = trim($_POST['txtUrl']); 
    $message = trim($_POST['mtxMessage']); 
     
    // escape the message ( if it's not already escaped ) 
    if(!get_magic_quotes_gpc()) 
    { 
        $name    = addslashes($name); 
        $message = addslashes($message); 
    } 
     
    // if the visitor do not enter the url 
    // set $url to an empty string 
    if ($url == 'http://') 
    { 
        $url = ''; 
    } 
     
    // prepare the query string 
    $query = "INSERT INTO guestbook (name, email, url, message, entry_date) " . 
             "VALUES ('$name', '$email', '$url', '$message', current_date)"; 

    // execute the query to insert the input to database 
    // if query fail the script will terminate          
    mysql_query($query) or die('Error, query failed. ' . mysql_error()); 
     
    // redirect to current page so if we click the refresh button  
    // the form won't be resubmitted ( as that would make duplicate entries ) 
    header('Location: ' . $_SERVER['REQUEST_URI']); 
     
    // force to quite the script. if we don't call exit the script may 
    // continue before the page is redirected 
    exit; 
} 
?> 
<html> 
<head> 
<title>Guestbook</title> 
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> 
<link rel="stylesheet" type="text/css" href="styles/styles.css"> 
<script language="JavaScript"> 
/* 
    This function is called when 
    the 'Sign Guestbook' button is pressed 
    Output : true if all input are correct, false otherwise 
*/ 
function checkForm() 
{ 
    // the variables below are assigned to each 
    // form input  
    var gname, gemail, gurl, gmessage; 
    with(window.document.guestform) 
    { 
        gname    = txtName; 
        gemail   = txtEmail; 
        gurl     = txtUrl; 
        gmessage = mtxMessage; 
    } 
     
    // if name is empty alert the visitor 
    if(trim(gname.value) == '') 
    { 
        alert('Please enter your name'); 
        gname.focus(); 
        return false; 
    } 
    // alert the visitor if email is empty or the format is not correct  
    else if(trim(gemail.value) != '' && !isEmail(trim(gemail.value))) 
    { 
        alert('Please enter a valid email address or leave it blank'); 
        gemail.focus(); 
        return false; 
    } 
    // alert the visitor if message is empty 
    else if(trim(gmessage.value) == '') 
    { 
        alert('Please enter your message'); 
        gmessage.focus(); 
        return false; 
    } 
    else 
    { 
        // when all input are correct  
        // return true so the form will submit         
        return true; 
    } 
} 

/* 
Strip whitespace from the beginning and end of a string 
Input  : a string 
Output : the trimmed string 
*/ 
function trim(str) 
{ 
    return str.replace(/^\s+|\s+$/g,''); 
} 

/* 
Check if a string is in valid email format.  
Input  : the string to check 
Output : true if the string is a valid email address, false otherwise. 
*/ 
function isEmail(str) 
{ 
    /*email java function*/
} 
</script> 
</head> 
<body> 
<form method="post" name="guestform"> 
 <table width="550" border="0" cellpadding="2" cellspacing="1"> 
  <tr>  
   <td width="100">Name *</td> <td>  
    <input name="txtName" type="text" id="txtName" size="30" maxlength="30"></td> 
 </tr> 
  <tr>  
   <td width="100">Email</td> 
   <td>  
    <input name="txtEmail" type="text" id="txtEmail" size="30" maxlength="50"></td> 
 </tr> 
  <tr>  
   <td width="100">Website URL</td> 
   <td>  
    <input name="txtUrl" type="text" id="txtUrl" value="http://" size="30" maxlength="50"></td> 
 </tr> 
  <tr>  
   <td width="100">Message *</td> <td>  
    <textarea name="mtxMessage" cols="80" rows="5" id="mtxMessage"></textarea></td> 
 </tr> 
  <tr>  
   <td width="100">&nbsp;</td> 
   <td>  
    <input name="btnSign" type="submit" id="btnSign" value="Sign Guestbook" onClick="return checkForm();"></td> 
 </tr> 
</table> 
</form> 
<br> 
<br> 
<?php 


// ======================= 
// Show guestbook entries 
// ======================= 

// how many guestbook entries to show per page 
$rowsPerPage = 10; 

// by default we show first page 
$pageNum = 1; 

// if $_GET['page'] defined, use the value as page number 
if(isset($_GET['page'])) 
{ 
    $pageNum = $_GET['page']; 
} 

// counting the offset ( where to start fetching the entries ) 
$offset = ($pageNum - 1) * $rowsPerPage; 

// prepare the query string 
$query = "SELECT id, name, email, url, message, DATE_FORMAT(entry_date, '%d.%m.%Y') ". 
         "FROM guestbook ". 
         "ORDER BY id DESC ".            // using ORDER BY to show the most current entry first 
         "LIMIT $offset, $rowsPerPage";  // LIMIT is the core of paging 

// execute the query  
$result = mysql_query($query) or die('Error, query failed. ' . mysql_error()); 

// if the guestbook is empty show a message 
if(mysql_num_rows($result) == 0) 
{ 
?> 
<p><br> 
 <br>Guestbook is empty </p> 
<?php 
} 
else 
{ 
    // get all guestbook entries 
    while($row = mysql_fetch_array($result)) 
    { 
        // list() is a convenient way of assign a list of variables 
        // from an array values  
        list($id, $name, $email, $url, $message, $date) = $row; 

        // change all HTML special characters, 
        // to prevent some nasty code injection 
        $name    = htmlspecialchars($name); 
        $message = htmlspecialchars($message);         

        // convert newline characters ( \n OR \r OR both ) to HTML break tag ( <br> ) 
        $message = nl2br($message); 
?> 
<table width="550" border="1" cellpadding="2" cellspacing="0"> 
 <tr>  
  <td width="80" align="left"> <a href="mailto:<?=$email;?>" class="email">  
   <?=$name;?> 
   </a> </td> 
  <td align="right"><small>  
   <?=$date;?> 
   </small></td> 
 </tr> 
 <tr>  
  <td colspan="2">  
   <?=$message;?> 
   <?php 
           // if the visitor input her homepage url show it 
        if($url != '') 
        {    
            // make the url clickable by formatting it as HTML link 
            $url = "<a href='$url' target='_blank'>$url</a>"; 
?> 
   <br> <small>Homepage : <?=$url;?></small>  
   <?php 
        } 
?> 
  </td> 
 </tr> 
</table> 
<br> 
<?php 
    } // end while 

// below is the code needed to show page numbers 

// count how many rows we have in database 
$query   = "SELECT COUNT(id) AS numrows FROM guestbook"; 
$result  = mysql_query($query) or die('Error, query failed. ' . mysql_error()); 
$row     = mysql_fetch_array($result, MYSQL_ASSOC); 
$numrows = $row['numrows']; 

// how many pages we have when using paging? 
$maxPage  = ceil($numrows/$rowsPerPage); 
$nextLink = ''; 

// show the link to more pages ONLY IF there are  
// more than one page 
if($maxPage > 1) 
{ 
    // this page's path 
    $self     = $_SERVER['PHP_SELF']; 
     
    // we save each link in this array 
    $nextLink = array(); 
     
    // create the link to browse from page 1 to page $maxPage 
    for($page = 1; $page <= $maxPage; $page++) 
    { 
        $nextLink[] =  "<a href=\"$self?page=$page\">$page</a>"; 
    } 
     
    // join all the link using implode()  
    $nextLink = "Go to page : " . implode(' &raquo; ', $nextLink); 
} 

// close the database connection since 
// we no longer need it 
include 'library/closedb.php'; 

?> 
<table width="550" border="0" cellpadding="2" cellspacing="0"> 
 <tr>  
  <td align="right" class="text">  
   <?=$nextLink;?> 
  </td> 
 </tr> 
</table> 
<?php 
} 
?> 
</body> 
</html>

Posted: Mon Oct 23, 2006 9:44 pm
by akimm
Add an approve function, or try a random number generator, have the user copy this string down in order to assure porn bots can't just do that.

Posted: Tue Oct 24, 2006 2:23 am
by matthijs
A couple of security problems I see:
- There is not enough input filtering (only javascript is not enough). For example $pageNum is used in the script without any validation (you'd expect a integer/number).
- your db queries are not being escaped properly. That makes the script vulnerable to sql injection attacks
For example $email and $url are not escaped. And for $name and $message addslashes is used. Better use mysql_real_escape_string() for ALL data going into the db.
- $_SERVER['PHP_SELF'] can contain tainted data as well.(used for your next/prev links)
- $email and $url are not being escaped (with htmlentities or htmlspecialchars)

There might be other things as well, but these I found from a quick look at the code.

For the spamming: something that has worked for me (without using some CAPTCHA) is to use Akismet. For non-commercial uses you can get a free API key, for commercial uses you'd have to contact them for a deal.

Posted: Tue Oct 24, 2006 5:09 am
by Bandy
Thx for your time.

Ill try to play with random number and database query "mysql_real_escape_string() ".

Thx again.