PDA

View Full Version : Create Secure Mods


Adrian Schneider
06 Aug 2007, 02:38
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 AgentIn 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:

Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

Not good.

Here is what you should use...

Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

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.



Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

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,

Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------



Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

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.

Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

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:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

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:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

The final query is:

Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

Since a # is a MySQL comment starter (one of the few ways), here is the query:

Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------

This affects the query in two ways:
Adds two new columns and values into the query
Removes the WHERE clauseIn 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 (http://php.net/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 quotesVery 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 (http://www.vblogetin.com/), or at SirAdrian.com (http://www.siradrian.com)


You may not copy this article without my permission.

EnIgMa1234
06 Aug 2007, 14:45
Excellant article. Very very helpful. Great job Sir Adrian

Atakan KOC
06 Aug 2007, 15:03
Good Articles. Thanks Sir Adrian.

D&Y
06 Aug 2007, 15:23
Very Nice Article..

Thanks !

Blaine0002
06 Aug 2007, 16:20
Thank you for this!

kaptanblack
07 Aug 2007, 10:13
Nice article SirAdrian ;)

Thanks...

Princeton
07 Aug 2007, 15:20
superb article SirAdrian :up:

A hghly recommended must read article.

bobster65
07 Aug 2007, 15:29
superb article SirAdrian :up:

A hghly recommended must read article.

Couldn't have said it better Princeton!

Thank you SirAdrian :up:

mihai11
07 Aug 2007, 16:16
Great article ! Exactly what I was asking in another thread !

Andreas
07 Aug 2007, 16:26
Addition: Use fetch_query_sql() whenever it makes sense.

mihai11
07 Aug 2007, 16:33
Addition: Use fetch_query_sql() whenever it makes sense.

This looks like a function for reading from the database. I assume that once the data is there, it can be trusted. Does this function has any security implications ? (after all, this is the theme of this article)

Andreas
07 Aug 2007, 16:41
I assume that once the data is there, it can be trusted.
Wrong assumption.

Example: A post that contains

<script type="text/javascript">
alert('XSS!');
</script>

Now, if you parse this text or run htmlspecialchars_uni() on it you are safe.
However, if you just read pagetext from table post and output it, then you just created an XSS issue.




Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------



As you can see, it runs escape_string() automatically

Adrian Schneider
08 Aug 2007, 02:20
fetch_query_sql() was what I meant by automation, thouh I actually use my own function that combines the $db->query_write() call and the fetch_query_sql.

Thanks for pointing that out - I'll add it in later.

mihai11
08 Aug 2007, 04:02
Wrong assumption.

Example: A post that contains

Now, if you parse this text or run htmlspecialchars_uni() on it you are safe.
However, if you just read pagetext from table post and output it, then you just created an XSS issue.


As you can see, it runs escape_string() automatically

But you should escape data *before* entering the database. You should make sure that whatever enters the database is clean. The reason is that usually the data is put into the database once and read many times thus it is better to do all validations when putting the data into the database and not when reading it.

Adrian Schneider
08 Aug 2007, 04:14
XSS and SQL injection are two different vulnerabilities.

Escaping it will avoid injection, but the XSS threat still remains unless you TYPE_NOHTML or htmlspecialchars_uni() before displaying it.

mihai11
08 Aug 2007, 04:18
XSS and SQL injection are two different vulnerabilities.

Escaping it will avoid injection, but the XSS threat still remains unless you TYPE_NOHTML or htmlspecialchars_uni() before displaying it.

But what if I run "htmlspecialchars_uni()" before putting the data in the database ?

Adrian Schneider
08 Aug 2007, 04:19
That's fine, then.

Antivirus
12 Aug 2007, 01:02
very helpful SirAdrian, thank you. So it seems as if it should be a standard practive to use $db->sql_prepare() as opposed to $db->escape_string() since the former seems to do a bit more "cleaning" of the user input. Are there any instances where this might not be the case? I ask this because I tend to see $db->escape_string() more frequently than $db->sql_prepare() in vb code (default and mods). Or is one no better than the other?

Adrian Schneider
12 Aug 2007, 01:32
They do different things.

sql_prepare basically checks the data type, and adds quotes/escapes it if necessary. escape_string just escapes it.

EnIgMa1234
12 Aug 2007, 01:57
Why is it neccessary to clean admincp code?

Antivirus
12 Aug 2007, 01:59
Why is it neccessary to clean admincp code?

It's just good coding practice to do so. Say for instance you have a moderator or someone whom you allow access to the admin CP (a business partner for instance) and you have a falling out - he/she then injects something nasty theough the AdminCP and BOOM!


Sir Adrian,
ok, i'm learning this, thanks. So after escaping it, if I want to display it in a <textarea> type input box for user to edit it, it's showing as follows:


Posted the banner on my myspace profile. Also posted their video on my blog, etc... \r\n\r\nOh yes i did.\r\n\r\nThat\'s what I am talking about. &quot;oh yeah&quot; i said


Which is safe, however for display in the textarea, i want it to display as entered which was like this:


Posted the banner on my myspace profile. Also posted their video on my blog, etc...

Oh yes i did.

That's what I am talking about. "oh yeah" i said


When I display it, I can qet the &quot; to parse correctly, but all the /n/r/n/r i can't get to display correctly. Can you help? Thanks :)

Adrian Schneider
12 Aug 2007, 02:39
Why are you displaying it after escaping it? It goes into the query escaped, and when you read from the DB again it should be OK.

Antivirus
12 Aug 2007, 02:41
It goes into query escaped before being put into database. Then if user wants to edit the text, it's displayed again, within the <textarea> box for them to modify.

I'd be fine if the initial cleaning of the data totally stripped out all the /n/r/n/r stuff as well since i have no need for them to post data on new lines. Am i missing something or do i need to create a regex before passing data to the update or save query?

Adrian Schneider
12 Aug 2007, 02:58
Only escape just before using it in a query. That's all it is for. You should never display it after it has been escaped; that doesn't make sense!

Antivirus
12 Aug 2007, 03:00
Thanks for your help on this issue SirAdrian.
So you're telling me that somewhere I am escaping it before displaying it in the <textarea> box?

Adrian Schneider
12 Aug 2007, 03:01
Yeah you must be. Post your code here (or make a new thread in programming / coders discussion and I will take a look).

Antivirus
12 Aug 2007, 03:11
ok will post it in programming discussions - thanks

Kirk Y
24 Aug 2007, 06:15
Excellent article SirAdrian, was a great read. :)

Danny.VBT
24 Aug 2007, 06:46
Excellent article SirAdrian, was a great read. :)

Definitely. Thank you for taking the time to write this article, Adrian. :up:

Floris
02 Sep 2007, 12:28
Nice article, thanks for taking the time to write this out.

TECK
28 Sep 2007, 14:21
Good article, AJ. Congrats. :)

Lionel
18 Nov 2007, 04:38
Excellent article, thanks for writing it.

vuiveclub
04 Dec 2007, 07:21
Code:
---------------
Code is only visible to licensed users, and only when logged into the forums.
---------------


What's 'g' mean?
I've seen some code has 'r' or 'p'?

Danny.VBT
04 Dec 2007, 07:25
They are the some of the super globals:

g = $_GET, p = $_POST, r = $_REQUEST, and also c = $_COOKIE.

The above are all valid and alters what content the cleanser sanitizes.

More information can be found in the PHP manual:

http://us.php.net/variables.predefined

Tefra
13 Dec 2007, 11:56
vB doesn't seem to use htmlspecialchars_uni when displaying the fields it uses htmlspecialchars_uni only during the update/insert of the TYPE_STR fields, so if we do that we are on the safe side right ?

I am asking cause some of the opinions in this thread really messed up with mine :erm:

1. Use the vBulletin Input Cleaner!
2. Use the htmlspecialchars_uni to clean the TYPE_STR vars from the vBulletin Input Cleaner
3. Use the $db->escape_string() to insert/update/replace values

If you use again the htmlspecialchars_uni during display or in the inputs things get ugly.

The only exception in this rule is the text columns that you might want to use html instead of bbcode. In this case you don't use htmlspecialchars_uni to clean the $vbulletin->GPC['message'] but you use the htmlspecialchars_uni during the edit on the textarea.