Saturday, January 23, 2010

10 common network security design flaws

Date: October 23rd, 2009
Author: Brien Posey

Solid planning and design can help reduce the potential for security breaches. Here are some security design missteps to watch out for.


Network security is arguably one of the most critical functions of IT - yet I frequently see organizations that have overlooked easily implemented security design practices. Here are a few common mistakes that could compromise your network defenses and put company assets at risk.

1: Set it and forget it

The first flaw I want to talk about is more a planning flaw than a design flaw. It involves what I like to think of as the “set it and forget it” mentality. This is what happens when organizations work hard to secure their networks without stopping to reevaluate their security plans again. The threats to security are constantly evolving, and your security architecture must evolve too. The best way to accomplish this is to reevaluate your security needs on a regular basis.

2: Opening more firewall ports than necessary

We all know that opening an excessive number of firewall ports is bad, but sometimes opening ports is unavoidable. For instance, take Microsoft Office Communications Server 2007 R2. If you are planning on providing external access, about a dozen ports must be opened. In addition, OCS 2007 R2 assigns a wide range of ports dynamically. So what’s a security administrator to do?

One of the best solutions is to make use of a reverse proxy (such as Microsoft’s ForeFront Threat Management Gateway). A reverse proxy sits between the Internet and the server that requires the various ports to be opened. While there is no getting around the need for open ports, a reverse proxy can intercept and filter requests and then pass them on to the server they’re intended for. This helps hide the server from the outside world and helps ensure that malicious requests do not reach the server.

3: Pulling double duty

With the economy in shambles, there is increasing pressure to make the most of existing server resources. So it might be tempting to host multiple applications or multiple application roles on a single server. While this practice is not necessarily bad, there’s a law of computing that states that as the size of the code base increases, so does the chance that an exploitable vulnerability exists.

It isn’t always practical to dedicate a server to each of your applications, but you should at least be careful about which applications or application roles are hosted on a single server. For example, at a minimum, an Exchange 2007 organization requires three server roles (hub transport, client access, and mailbox server). While you can host all three roles on a single server, you should avoid doing so if you are going to be providing Outlook Web Access to external users. The Client Access Server role makes use of IIS to host Outlook Web Access. Therefore, if you place the client access server role on the same server as your hub transport and mailbox server roles, you are essentially exposing your mailbox database to the Internet.

4: Ignoring network workstations

About a year ago, someone asked me during a radio interview what I thought was the single biggest threat to network security. My answer was, and still is, that workstations make up the single largest threat. I constantly see organizations that go to great lengths to secure their network servers but practically neglect their workstations. Unless workstations are locked down properly, users (or malicious Web sites) can install unauthorized software with untold consequences.

5: Failing to use SSL encryption where it counts

We all know that a Web site needs to use SSL encryption any time a user is going to be entering sensitive information, such as a username and password or a credit card number. However, many organizations make some bad decisions when it comes to securing their Web portals. The security flaw I see most often is including insecure content on a secure page. When this happens, users receive a prompt asking if they want to display both secure and insecure content. This gets users in the habit of giving Internet Explorer permission to provide insecure content.

A less obvious but even more common problem is that organizations often fail to encrypt critical pages within their Web sites. In my opinion, any page that provides security information, security advice, or contact information should be SSL encrypted. It isn’t that these pages are especially sensitive. It’s just that the certificate used by the encryption process guarantees to users that they are accessing a legitimate Web page rather than a page someone has set up as a part of a phishing scam.

6: Using self-signed certificates

Since some organizations completely neglect the importance of SSL encryption, Microsoft has begun to include self-signed certificates with some of its products. That way, Web interfaces can be used with SSL encryption even if the organization hasn’t acquired its own certificate yet.

While self-signed certificates are better than nothing, they are not a substitute for a valid SSL certificate from a trusted certificate authority. Self-signed certificates are primarily intended to help boost a product’s security until an administrator can properly secure it. Yes, a self-signed certificate can provide SSL encryption, but users will receive warning messages in their browsers because their computers do not trust the certificate (nor should they). Furthermore, some SSL-based Web services (such as ActiveSync) are not compatible with self-signed certificates because of the trust issue.

7: Excessive security logging

Although it’s important to log events that occur on your network, it’s also important not to go hog wild and perform excessive logging. Too much logging can make it difficult or impossible to locate the security events you’re really interested in. Rather than trying to log everything, focus on logging the events that are really meaningful.

8: Randomly grouping virtual servers

Virtual servers are commonly grouped on host servers by their performance. For example, a high demand virtual server might be paired on a host with a few low demand virtual servers. From a performance standpoint, this is a good idea, but this approach may not be the best idea from a security standpoint.

I recommend using dedicated virtualization hosts for any Internet-facing virtual servers. In other words, if you have three virtual servers that provide services to Internet users, you might consider grouping those servers on a virtualization host, but don’t put infrastructure servers (such as domain controllers) on the host.

My reasoning behind this is to provide protection against an escape attack. An escape attack is one in which a hacker can escape from a virtual machine and take control of the host. To the best of my knowledge, nobody has figured out a way to perform a real-world escape attack yet, but I’m sure that day is coming. When it does, your odds of prevailing against the attack are going to be a lot higher if virtual machines that are exposed to the Internet share a virtualization host only with similarly hardened Web-facing servers.

9: Placing member servers in the DMZ

If you can avoid it, try not to place any member servers in your DMZ. If compromised, a member server can reveal information about your Active Directory.

10: Depending on users to install updates

One last common security flaw is depending on users to deploy security patches. I have seen several network deployments recently that use WSUS to patch network workstations. Unfortunately, many of these deployments rely on the users to click the option to install the latest updates. The problem with this is that the users know that the update process is going to require them to reboot their computers. Some users may end up putting off the updates indefinitely. Rather than relying on the end users, use a patch management solution that pushes security patches automatically without giving users a choice in the matter.

Tuesday, January 5, 2010

C Programming Tips 1

Here are some questions about C programming which I come across and answer very frequently on programming newsgroups. All of them can be used in C++ programs as well. You might find them helpful.

Contents

I need help with my homework!
Please don't void my main()!
What should my "int main()" return?
What should I use instead of gets()?
How do I input numbers as binary strings?
How do I output numbers as binary strings?

I need help with my homework!

If I posted a link to this page in reply to a message you posted on a programming newsgroup, it seemed to me that you were asking for someone to do your programming homework for you. Your post probably looked like one of the following:

You typed in your assignment word-for-word.
You complained that your instructor is stupid, but your instructor is smarter than you are, he or she already knows how to write this program!
You said you have tried and tried, but get nothing but errors, but you didn't include any of your code.
You said you do not have any idea where to start, and you didn't include any of your code.
Your post looked like a thinly disguised attempt into fooling somebody into thinking that it is not your homework.
You specifically asked for help, and not for someone to write the code, but you still didn't post any of the code you had written on your own. In this case, you still want to look at the suggestions below for the best way to get help.


First let me say that I apologize if I have misjudged you and you really aren't trying to get someone to do your homework for you.

But if you are...

The purpose of actually writing programs in a programming class is to learn how to write them. If you don't do the work, you will be cheating yourself out of the knowledge you are going to school for.

Most regular contributors to the programming newsgroups will not just write your program for you. Some of them will write a program with deliberate and subtle errors, in the hopes that you will turn it in as your own work and get caught for cheating.

Here are some of the replies I have posted in newsgroups to messages like yours. They are sarcastic, but don't worry, I will follow them with some suggestions on how to successfully get help with your homework.



If I did your homework for you, then you might pass your class without learning how to write a program like this. Then you might graduate and get your degree without learning how to write a program like this. You might become a professional programmer without knowing how to write a program like this. Someday you might work on a project with me without knowing how to write a program like this. Then I would have to do you serious bodily harm.
Please post your instructor's email address in case I have any questions about your assignment. In fact, I have a great idea. I could just email your assignment directly to your instructor and then you wouldn't even have to go to class either!

OK, I promised to give you advice on how to get useful help with your assignment in a programming newsgroup. The best results will come from following the steps below.

If you can't even figure out how to start, sit down with a pencil and paper and go work on the problem as a problem, not as a program. Take it one step at a time until you figure out how the actual operation needs to be done.
Try to write the program yourself. Give it your best effort, using everything you have learned so far.
If there is one particular part of the program which you just can't get to work right, perform the following steps.


  1. Cut your program down to the smallest possible code sample which compiles and displays your problem. In the process of doing this many people find and fix the problem on their own!
  2. If your program uses non-standard functions from non-standard headers with names like conio.h, dos.h, or bios.h, remove them from the program.
  3. Do not attach your executable or source code to your message. Just copy the source code in your text editor and paste in into the body of your message as plain text.
  4. Be sure to include the exact text of important error or warning messages which you get when trying to build your program.
  5. Include the brand and version number of the compiler you are using and the operating system on your computer. If your compiler can make more than one type of program (many PC compilers can make programs for MS-DOS or Windows) then specify exactly what type you are trying to make.
  6. Do not ask for an email reply. In the first place, it tends to annoy the people who can help you the most. In the second place, programming newsgroups are read by thousands of people worldwide. If your question is not important enough for you to take the time and effort to come back to the group in a few hours or a day to look for answers, it is certainly not important enough for you to waste the time and bandwidth of all those readers by posting it. Finally if someone posts an incorrect answer to your problem on the group, others will point out the error and correct it. If you get an answer by email, how will you know if it is right or not?
  7. Do not post your question again if you do not see any replies in 15 minutes or an hour. Usenet groups do not work that fast. It could take up to a day (or more) before you get a reply.
  8. It could be that you will not get a reply at all. It is possible that your question is outside the range of topics normally discussed on the group, and none of the people who see it are interested enough, or familiar enough with the subject, to post a reply.
  9. This last one seems too obvious to mention, but the fact that I am mentioning it indicates that some people can't figure it out for themselves. Be polite in your post, and use proper spelling and grammar. You are asking for help from people who know more than you, at least about the problem you are having. If they don't know more than you, you are wasting your time asking them for help! Remember that nobody is paying these people to help you, they do it because they want to. Politeness and respect will make the most knowledgeable contributors more likely to help you.

If you follow the advice above, and if your question is really on topic in the group you posted it too, there is a good chance that you will receive several excellent replies to help you solve your problem.

Please don't void my main()!

A commonly asked question:

Why do people say that main() has to return an int and void main(void) is wrong? I think it is all right because:

  1. I have a book by /* somebody */ and all of the example programs are written this way.

  2. My instructor tells us to write it that way.

  3. The examples in my online help show it that way.

  4. It works on my compiler.

First let's talk about the items above.

  1. Many books are written by people who don't even bother to actually learn their subject. They are good at writing books and think they know everything. Some of them test their code by compiling and running it with their favorite compiler, and never notice that they are using compiler specific extensions. Some authors have written books containing code which they just wrote off the top of their head. They never compiled and ran it because it would not compile with ANY compiler.

  2. Sadly, many instructors of computer programming classes do not know the actual standard for the language themselves. What they know they learned from reading the same books written by careless authors described above. This kind of ignorance of their subject matter would not be allowed in a subject like mathematics, chemistry, or physics, but it is all too common in computer programming.

  3. Microsoft is particularly bad at this. Almost all of the examples in their online help start with void main(void). Yet if you look up main in the online help, it displays a proper definition. The example programs in the online help for Borland compilers always show main() properly returning an int.

  4. The standards for the C and C++ programming languages specifically allow compiler writers to include extensions. In addition, the usual tradition is for a compiler to process your program and generate an executable if at all possible, even if your source code is questionable. The International Standard for each programming language defines what the language is, not whatever any particular compiler happens to accept. Most C compilers have some type of option to compile in strict standard mode. Again Microsoft is one of the worst offenders here. There is no way that I know of to get their compilers to issue an error or even a warning message if you define a void main() function in your code.

Some computer languages are proprietary, that is they belong to a particular company, and that company has final authority to determine what the language is and isn't.

One example is Java. This language is a product of Sun Microsystems, and it defined by them. Any other company which uses the Java language signs a contract with Sun and agrees that their version will be 100% compatible with Sun's standard.

Another language is Microsoft's Visual Basic. This is a programming package that only they sell, and only for their Windows operating systems. Since this language is completely theirs, the definition of what the language is is completely theirs.

This is not the case with most general purpose programming languages, and this includes C and C++. Nobody owns these languages or their definitions. Instead the languages are defined by International Standards, which are generated and maintained by ISO, the International Standards Organization.

ANSI, which is the American National Standards Institute, is the American national standards body and is one of the member bodies of ISO. You may have heard the term ANSI C or ANSI C++. These terms are in common use because it was ANSI that issued the first standard for C. When the ISO International Standard was adopted, ANSI adopted the ISO version and it became the ANSI version as well. The current ANSI standards for C and C++ are the ISO International standards.

The ANSI/ISO standards for C and C++ define the languages, not Microsoft or Borland or any other compiler vendor. Here is what the standards have to say:

ANSI/ISO/IEC 9899:1990 International Standard For C

The function called at program startup is named main. The implementation declares no prototype for this function. It can be defined with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):


int main(int argc, char *argv[ ]) { /* ... */ }

The newly ratified update to the C standard in 1999 will make this even clearer, perhaps because of all the clueless who could not understand that only the two formats above, both of which define main to return an int, are valid.

The draft of the new standard expands on the two definitions above by modifying the wording "It can be defined" to "It shall be defined with a return type of int".

ANSI/ISO/IEC 14882:1998 International Standard For C++

An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int but otherwise its type is implementation defined. All implementations shall allow both of the following definitions of main:

The two definitions which follow are identical to those in the C standard.

Practical Reasons To Return An int from main()

On many operating systems, the value returned by main() is used to return an exit status to the environment. On Unix, MS-DOS, and Windows systems, the low eight bits of the value returned by main() is passed to the command shell or calling program. This is often used to change the course of a program, batch file, or shell script.
Many compilers will refuse to compile a source code file containing a definition of main() which does not return an int.
On some platforms a program starting with void main() may crash on startup, or when it ends.

A program which contains a main() function that is not defined to return an int is just plain not real C or C++.

What should my "int main()" return?

As pointed out in the section above, it is extremely common for a program to return a result indication to the operating system. Some operating systems require a result code. And the return value from main(), or the equivalent value passed in a call to the exit() function, is translated by your compiler into an appropriate code.

There are three and only three completely standard and portable values to return from main() or pass to exit():

The plain old ordinary integer value 0.
The constant EXIT_SUCCESS defined in

If you use 0 or EXIT_SUCCESS your compiler's run time library is guaranteed to translate this into a result code which your operating system considers as successful.

If you use EXIT_FAILURE your compiler's run time library is guaranteed to translate this into a result code which your operating system considers as unsuccessful.

Note: Some operating systems, such as Unix, MS-DOS, and Windows, truncate the integer passed to exit() or returned from main() to an unsigned char and make this available to the shell script, batch file, or parent process which invoked the program. On these systems programmers sometimes use different positive numbers to indicate different reasons for the failure of the program to execute successfully. Such usage is not portable and may not work correctly on all implementations and operating systems. Only the values 0 and the constants EXIT_SUCCESS and EXIT_FAILURE are portable and guaranteed to work correctly on all hosted implementations.

C++ Note

In a C++ program you do not have to return anything from int main()! The language standard guarantees that if your int main() function "falls off the end" by reaching the closing brace, the compiler will automatically return 0 for you indicating success.

Warnings

It is not good programming practice to take advantage of this C++ feature. Programs should always specifically indicate a return status.
C++ does not provide this automatic return for any function except int main().
C does not provide an automatic return value for main() or any other function. It is up to the program to specify a return value or the status returned to the operating system is undefined.

What should I use instead of gets()?

You might have been told this in a C programming newsgroup already, but it is important enough that I will repeat it here:

Never, never, NEVER use the function gets(). It is the most dangerous function in the entire C standard library because there is there is no way to use it safely!

Consider this example:

#include  int main(void) {   char name [25];   printf("Enter your name: ");   fflush(stdout);   if (gets(name) != NULL)      printf("Hello and Goodbye %s\n", name);   return 0; }

What do you think will happen if the user types fifty characters into your twenty-five character array? What if the user types one hundred characters? Two hundred??

The answer is that gets() will fill up your array and then keep on going, trying to write to memory past the end of the array which your program does not have the right to access. A program crash is likely. Some notorious computer viruses have based their attack on deliberately overflowing buffers used by calling gets().

You might also have heard that you should use the fgets() function, with stdin as the FILE * parameter, instead of gets(). Most people stop after saying that, but that doesn't actually give you the same result. gets() removes the '\n' character from the input but fgets() does not. That means you must manually remove the '\n' before passing the string to fopen(), or for many other uses.

Here is my getsafe() function. Like gets() and fgets() both, it returns a pointer to char. This is either the pointer which was passed to it, or NULL if end of file or an error occurred. Like gets(), it removes the '\n' at the end of the string, if there is one. The prototype is:

char *getsafe(char *buffer, int count);

Here is the function:

#include  #include  char *getsafe(char *buffer, int count) {   char *result = buffer, *np;   if ((buffer == NULL) || (count < result =" NULL;" count ="=" result =" '\0';" result =" fgets(buffer," np =" strchr(buffer," np =" '\0';">

That's all it takes to safely get input strings from the standard input with the '\n' removed.

How do I input numbers as binary strings?

The first step is to read the binary string representing the number into a character array in your program. If the input is from a file which you opened with fopen(), you can use the standard function fgets(). If the input is coming from stdin, you can use my getsafe() function, or fgets() with a FILE * of stdin. There is no need to remove the trailing '\n' if you are receiving a single numeric value in the string.

The next step is to use the standard C library function strtoul() prototyped in

unsigned long int strtoul(const char *nptr, char **endptr, int base);

Here is what the parameters are:

nptr is a pointer to the array of characters containing the string you want parsed. It has the const type qualifier to indicate that the function will not modify the string.
endptr is a pointer to a pointer to char. If this parameter is not NULL, a pointer to the remainder of the string is stored in the object endptr points to. This can be useful in parsing complex input lines from a file, but for the purpose of processing one line at a time we will use NULL for this parameter.
base the final parameter is the number base to be used. If we use 10 for base, the string would be parsed as a decimal number. If we use 16, the string will be taken to represent a hexadecimal number even if it doesn't start with 0x or 0X. If we use 2, strtoul() will accept only the characters '0' and '1' and assume they represent a number in binary.

This code has a few other interesting features. It includes the standard header which defines some very important constants. In this case two of the constants it defines are used trim the unsigned long value returned by strtoul() to fit in an unsigned int and an unsigned char.

#include  #include  #include  int main(void) {   char buffer [50];   unsigned char uc;   unsigned int ui;   unsigned long ul;   if (fgets(buffer, sizeof buffer, stdin) != NULL)   {      ul = strtoul(buffer, NULL, 2);      ui = (unsigned int)(ul & UINT_MAX);      uc = (unsigned char)(ul & UCHAR_MAX);      printf("You entered %lu decimal, %lX hex\n", ul, ul);      printf("This is %X as an unsigned int\n", ui);      printf("This is %X as an unsigned char\n", uc);   }   return 0; }

The function strtoul() has another very useful feature. If you pass 0 as the base parameter, the function uses the same rules for converting the numeric string to a value that the C compiler uses on source code.

If the first non whitespace characters of the string are "0x" or "0X",strtoul() parses the string as hex, accepting the letters 'a' through 'f' and'A' through 'F' as well as the digits '0' through '9'.

If the first non whitespace character of the string is '0' and it is not followed by 'x' or 'X', strtoul parses the string as octal, and only acceptsthe digits '0' through '7'.

Finally if the first non whitespace character of the string is any digit except'0', that is '1' through '9', strtoul parses the number as decimal.

How do I output numbers as binary strings?

We had some help from the standard C library to answer the question How do I input numbers as binary strings? The standard library function strtoul() was available to do the work for us.

When it comes to outputting a numeric value of one of the integer types as a string of just 0's and 1's, however, we are strictly on our own. The code to do this is not very hard. Here is the code for displaying an unsigned char:

#include  #include  char *unsigned_char_to_text(char val) {   unsigned char usc = (unsigned char)val;   unsigned char mask = UCHAR_MAX - (UCHAR_MAX >> 1);   static char result [CHAR_BIT + 1];   char *store = result;   while (mask)   {      *store++ = (char)('0' + ((usc & mask) != 0));      mask >>= 1;   }   *store = '\0';   return result; }

To generate a function to output an unsigned int as a binary string, just make the following changes to this function:

Change val to int and usc and mask to unsigned int.
Change the two references to UCHAR_MAX to UINT_MAX.
Change the expression "UCHAR_BIT + 1" for the array size to "sizeof(int) *UCHAR_BIT + 1".

To make a function to do the same for unsigned long, change val to long and usc and mask to unsigned long, change UCHAR_MAX to ULONG_MAX, and use "sizeof(long) * UCHAR_BIT + 1".

Warning about the returned string. The string used to store the characters in the unsigned_char_to_text is defined as a static array of characters inside the function. It must be static because local variables defined inside a function go out of scope and cease to exist once the function returns, so it is illegal and a bug to return a pointer to a local non static variable.

The fact that the string is static means that each call to the function uses the same string space and overwrites the string produced by a previous call. So if you use this function more than once in a program you should be careful to do the following:

Use the string immediately to display or write to a file, before calling the function again.
If you do need to keep the generated string for some time, and the function might be called again, copy the string.

C++ Notes For Using These Functions

All of these functions are written in standard C. They can also be used and work correctly in a C++ program. In some of the cases, the features of C which I use in these functions could be replaced with C++ specific features. This is usually the preferred method in C++ if it provides a newer way of doing something than C does. Even so, the C code here is perfectly legal in C++.

If your have a newer C++ compiler that supports namespaces, you can modify the programs to use the C++ feature namespace feature to place the C library function names in the std namespace. In place of traditional C header names like