Go Back   vb.org Archive > vBulletin Article Depository > Read An Article > Programming Articles
FAQ Community Calendar Today's Posts Search

Reply
 
Thread Tools
Create Secure Mods
Adrian Schneider's Avatar
Adrian Schneider
Join Date: Jul 2004
Posts: 2,528

 

Show Printable Version Email this Page Subscription
Adrian Schneider Adrian Schneider is offline 08-05-2007, 10:00 PM

In this post we'll go over some basic modification vulnerabilities and how to prevent them.

Preparing Data

Whenever you accept input from a user, you must understand that you have no control over what is entered, period. The short version is that anything client-side can be manipulated. Even if you use Javascript or HTML limitations, they can all easily be avoided. This input includes but is not limited to:
  • Entered form input
  • Hidden form input (!)
  • URL query string
  • User Agent
In your application it is your responsibility to ensure the validity of the data they enter.

Use the vBulletin Input Cleaner!

I'm going to assume you know how to use it, even though sometimes you choose not to use it. If not, dig up a tutorial on it.

A common example is fetching a row from a table using an ID in the query string. I often see this:
PHP Code:
$row $db->query_first("
    SELECT *
    FROM " 
TABLE_PREFIX "table
    WHERE id = '
$_GET[id]'
"
); 
Not good.

Here is what you should use...
PHP Code:
$id $vbulletin->input->clean_gpc('g''id'TYPE_UINT);

$row $db->query_first("
    SELECT *
    FROM " 
TABLE_PREFIX "table
    WHERE id = 
$id
"
); 
The TYPE_UINT ensures that the variable is an integer, and is >= 0. You can also use $id in the rest of your application, more confidently. But wait... if they don't enter a valid integer here, it will just be 0! We can prevent that query from running in this case. Though no harm would be done, it is good to do as little processing as possible to keep things running fast.

PHP Code:
$id $vbulletin->input->clean_gpc('g''id'TYPE_UINT);

if (!
$id)
{
    
standard_error('Please enter a valid product ID to view.');
}

$row $db->query_first("
    SELECT *
    FROM " 
TABLE_PREFIX "table
    WHERE id = 
$id
"
); 
That's better. The same concept applies to a lot of different types of data. If you expect them to enter an email address, then you can use the is_valid_email() function. If you expect them to enter one of a few specific answers, have those answers in an array and ensure that it matches. Example,
HTML Code:
<select name="meal">
    <option value="1">Breakfast</option>
    <option value="2">Lunch</option>
    <option value="3">Dinner</option>
</select>
PHP Code:
$meal $vbulletin->input->clean_gpc('p''meal'TYPE_UINT);

if (!
in_array($meal, array(123))
{
    
standard_error('Invalid meal specified.');

If you expect them to enter data in a certain pattern - for example, a md5 hash, then you can use a regular expression to match it.
PHP Code:
$hash $vbulletin->input->clean_gpc('p''hash'TYPE_STR);

if (!
preg_match('/^([a-f0-9]{32})$/'$hash))
{
    
standard_error('Please enter a valid md5 hash.');

Not a great example, but you should really learn to use regular expressions to match the validity of data. An EXCELLENT tool to have. If you use the vBulletin datamanager for your custom application, be sure to use the error checking functionality in it. Very flexible.

Point: ensure the data you expect is what you expect. If it's not, do not let them continue. False data can do a number of things of varying damage:
  • Display a PHP error. Not a big deal, but it may disclose your script names and path. The less information they know about it the better.
  • Display SQL error. Injection, anyone?
  • Get into DB . It looks horrible to visitors when they see random junk on your website. Ohter side effects include: breaking search capabilities, false statistics, even DB errors down the road! Injection round two!
SQL Injection

What is SQL injection? It's when your script does not properly clean user input, and they can actually manipulate the query using the input value.

Example,

A script to update a user's email address. Here is the query:
Code:
UPDATE user SET email = '$value' WHERE userid = 1;
You'd expect the user to enter an email address, but what if they don't? If the value they enter contains a single quote, it will end the string email value in the query, and it will be parsed as SQL. This is injection. What could happen from this? It really depends on the query, but it can be devastating. If I enter
Code:
my@email.com', username = 'HAHA', password = ''#
The final query is:
Code:
UPDATE user SET email = 'my@email.com', username = 'HAHA', password = ''#' WHERE userid = 1;
Since a # is a MySQL comment starter (one of the few ways), here is the query:
Code:
UPDATE user SET email = 'my@email.com', username = 'HAHA', password = '';
This affects the query in two ways:
  • Adds two new columns and values into the query
  • Removes the WHERE clause
In English - wiped out your entire user table.

Preventing Injection

It is very easy to prevent injection. Generally, users either input text (string) or a number (int/float). For the latter, they should already be safe assuming you used the input cleaner as suggested. For strings, however, you must use the $db->escape_string() function. What does this do? In a standard MySQL setup, it's just a wrapper for mysql_real_escape_string which in simpliest terms, prepends a backslash to any characters which may alter query. Remember to use $db->escape_string() instead of the actual function, because not every board runs MySQL!

Automation

I highly recommend using a function to generate queries. This is how the datamanager works, and also many of my own private hacks. How does this make it more secure? - by not relying on the coder to escape it. If you do so, you can wrap each value in the $db->sql_prepare() function, which makes it SQL safe.

Here is what the function does to...
  • Strings, returns it escaped, and wrapped in single quotes
  • Numbers, returns them as is
  • Booleans, returns them in their integer form (1 and 0)
  • Anything else, returns it escaped, and wrapped in single quotes
Very useful tool.



XSS (Cross Site Scripting)

Cross site scripting is when a user injects HTML into any text that he posts, and in that HTML uses Javascript to communicate with his own server. What can this do? He can steal your cookie information and take over your online identity. Imagine someone doing this to an administrative account. Uh oh!

Like SQL injection, XSS is actually very easy to prevent. You have two options:

1)
Use TYPE_NOHTML instead of TYPE_STR when accepting string input.This has a few (minor) downsides. If you want to allow searching, or for any other reason want to retain the exact information the user enters. The alternative, is

2)
Clean it using htmlspecialchars_uni() when displaying the text. This is where it gets tricky. If you display it all over the place, it can be difficult to remember to clean it every time.

So what does this do exactly? Any HTML characters, such as "<", ">", and "&" are parsed as HTML, so if you convert them to their entity form ("&lt;", "&gt;", "&amp;", the browser knows to display that character, and not parse it as HTML. Now it is impossible for any hackers to inject HTML into your site.


Summary
  • Only accept input that is what you expect.
  • Escape strings when using them inside queries.
  • Convert all HTML characters to entities before displaying them.
If you have any further questions, you may contact me at vBlogetin.com, or at SirAdrian.com


You may not copy this article without my permission.
Reply With Quote
  #2  
Old 08-06-2007, 01:45 PM
EnIgMa1234 EnIgMa1234 is offline
 
Join Date: Mar 2006
Location: .:: Ireland ::.
Posts: 1,306
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Excellant article. Very very helpful. Great job Sir Adrian
Reply With Quote
  #3  
Old 08-06-2007, 02:03 PM
Atakan KOC's Avatar
Atakan KOC Atakan KOC is offline
 
Join Date: Feb 2006
Location: Istanbul
Posts: 710
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Good Articles. Thanks Sir Adrian.
Reply With Quote
  #4  
Old 08-06-2007, 02:23 PM
D&Y's Avatar
D&Y D&Y is offline
 
Join Date: Jun 2005
Posts: 75
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Very Nice Article..

Thanks !
Reply With Quote
  #5  
Old 08-06-2007, 03:20 PM
Blaine0002's Avatar
Blaine0002 Blaine0002 is offline
 
Join Date: Jul 2003
Location: Wisconsin.
Posts: 1,350
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Thank you for this!
Reply With Quote
  #6  
Old 08-07-2007, 09:13 AM
kaptanblack's Avatar
kaptanblack kaptanblack is offline
 
Join Date: Mar 2007
Location: Turkey
Posts: 397
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Nice article SirAdrian

Thanks...
Reply With Quote
  #7  
Old 08-07-2007, 02:20 PM
Princeton's Avatar
Princeton Princeton is offline
 
Join Date: Nov 2001
Location: Vineland, NJ
Posts: 6,693
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

superb article SirAdrian :up:

A hghly recommended must read article.
Reply With Quote
  #8  
Old 08-07-2007, 02:29 PM
bobster65's Avatar
bobster65 bobster65 is offline
 
Join Date: Mar 2006
Location: Montana
Posts: 1,169
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Quote:
Originally Posted by Princeton View Post
superb article SirAdrian :up:

A hghly recommended must read article.
Couldn't have said it better Princeton!

Thank you SirAdrian :up:
Reply With Quote
  #9  
Old 08-07-2007, 03:16 PM
mihai11 mihai11 is offline
 
Join Date: Dec 2005
Location: Sibiu - Romania
Posts: 199
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Great article ! Exactly what I was asking in another thread !
Reply With Quote
  #10  
Old 08-07-2007, 03:26 PM
Andreas's Avatar
Andreas Andreas is offline
 
Join Date: Jan 2004
Location: Germany
Posts: 6,863
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Addition: Use fetch_query_sql() whenever it makes sense.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 02:08 PM.


Powered by vBulletin® Version 3.8.12 by vBS
Copyright ©2000 - 2024, vBulletin Solutions Inc.
X vBulletin 3.8.12 by vBS Debug Information
  • Page Generation 0.09899 seconds
  • Memory Usage 2,321KB
  • Queries Executed 23 (?)
More Information
Template Usage:
  • (1)SHOWTHREAD
  • (1)ad_footer_end
  • (1)ad_footer_start
  • (1)ad_header_end
  • (1)ad_header_logo
  • (1)ad_navbar_below
  • (1)ad_showthread_beforeqr
  • (4)bbcode_code
  • (1)bbcode_html
  • (5)bbcode_php
  • (1)bbcode_quote
  • (1)footer
  • (1)forumjump
  • (1)forumrules
  • (1)gobutton
  • (1)header
  • (1)headinclude
  • (1)modsystem_article
  • (1)navbar
  • (4)navbar_link
  • (120)option
  • (1)pagenav
  • (1)pagenav_curpage
  • (2)pagenav_pagelink
  • (10)post_thanks_box
  • (3)post_thanks_box_bit
  • (10)post_thanks_button
  • (1)post_thanks_javascript
  • (1)post_thanks_navbar_search
  • (1)post_thanks_postbit
  • (10)post_thanks_postbit_info
  • (9)postbit
  • (10)postbit_onlinestatus
  • (10)postbit_wrapper
  • (1)spacer_close
  • (1)spacer_open
  • (1)tagbit_wrapper 

Phrase Groups Available:
  • global
  • inlinemod
  • postbit
  • posting
  • reputationlevel
  • showthread
Included Files:
  • ./showthread.php
  • ./global.php
  • ./includes/init.php
  • ./includes/class_core.php
  • ./includes/config.php
  • ./includes/functions.php
  • ./includes/class_hook.php
  • ./includes/modsystem_functions.php
  • ./includes/functions_bigthree.php
  • ./includes/class_postbit.php
  • ./includes/class_bbcode.php
  • ./includes/functions_reputation.php
  • ./includes/functions_post_thanks.php 

Hooks Called:
  • init_startup
  • init_startup_session_setup_start
  • init_startup_session_setup_complete
  • cache_permissions
  • fetch_threadinfo_query
  • fetch_threadinfo
  • fetch_foruminfo
  • style_fetch
  • cache_templates
  • global_start
  • parse_templates
  • global_setup_complete
  • showthread_start
  • showthread_getinfo
  • forumjump
  • showthread_post_start
  • showthread_query_postids
  • showthread_query
  • bbcode_fetch_tags
  • bbcode_create
  • showthread_postbit_create
  • postbit_factory
  • postbit_display_start
  • post_thanks_function_post_thanks_off_start
  • post_thanks_function_post_thanks_off_end
  • post_thanks_function_fetch_thanks_start
  • fetch_musername
  • post_thanks_function_fetch_thanks_end
  • post_thanks_function_thanked_already_start
  • post_thanks_function_thanked_already_end
  • post_thanks_function_fetch_thanks_bit_start
  • post_thanks_function_show_thanks_date_start
  • post_thanks_function_show_thanks_date_end
  • post_thanks_function_fetch_thanks_bit_end
  • post_thanks_function_fetch_post_thanks_template_start
  • post_thanks_function_fetch_post_thanks_template_end
  • postbit_imicons
  • bbcode_parse_start
  • bbcode_parse_complete_precache
  • bbcode_parse_complete
  • postbit_display_complete
  • post_thanks_function_can_thank_this_post_start
  • pagenav_page
  • pagenav_complete
  • tag_fetchbit_complete
  • forumrules
  • navbits
  • navbits_complete
  • showthread_complete