VIEW MODULE CONTENT

USE THIS MODULE

Give this link to students

 

Encapsulation-“Serve and Protect”-CS2

 

Read Background
Execute Lab Assignment
Complete Security Checklist
Answer Discussion Questions

Background

top

Summary:

Encapsulation is an important feature of C++ that allows a programmer to control access to components of a class. Used properly, encapsulation can enhance program security. Used improperly, encapsulation can create major security risks in a program.

Description:

Object-oriented programming languages such as C++ include features that support the object-oriented programming paradigm. These features include data abstraction, encapsulation, information hiding, polymorphism, and inheritance. Encapsulation refers to the bundling of both data and methods (behaviors) into a single entity called a class, which can be easily incorporated into different programs. A class defined in a C++ program is used to declare objects of that class type. Using the feature of encapsulation, the programmer can restrict access to components of the class, which is a key strength of encapsulation in C++. C++ classifies class members (data members and member methods) into one of three categories using access specifiers, which are also reserved words: private, public, and protected. Access specifiers determine the visibility or accessibility of an object’s members, as specified in the table below:

Types of Threat Accesibility
Private Accessible only by member methods of the class or by friends of the class
Public Accessible by methods both inside and outside of the class
Protected Accessible by member methods of the class, by friends of the class, and by member methods of any subclass

Note: Friends should be used with great caution because the use of friends compromises encapsulation and data security. For more information about friends, see C++ How to Program by Deitel and Deitel.

The general format of a C++ class declaration is:

class ClassName
{
    private:
        // Declarations of private members appear here

    protected:
        // Declarations of protected members appear here

    public:
        // Declarations of public members appear here
};

Note that each access specifier is followed by a colon (:), and then followed by declarations for one or more class members. In the general format shown above, the private access specifier is shown first; however, the access specifiers can be shown in any order. All of the declarations that follow the private access specifier, up to the protected access specifier, are private members of the class. All of the declarations that follow the protected access specifier, up to the public access specifier, are protected members of the class. All of the declarations that follow the public access specifier, up to the closing curly brace (}), are public members of the class.

An example of a C++ class declaration follows:

class Time
{
    private:
        int hour;
        int minute;
        int second;

    public:
        Time (void);
        void setTime (int newHour, int newMinute, int newSecond);
        void getTime (int& currHour, int& currMinute, int& currSecond);
        void incrementTime (void);
};

In the example above, the data members hour, minute, and second and the member methods setTime(), getTime() and incrementTime() are bundled into a single autonomous class named Time. This demonstrates the encapsulation feature of C++. When all data is declared as private, the data is only accessible through the methods provided by the class. This restricted access is illustrated below.

Such restricted access requires programmers to write specialized accessor methods like getTime() for acquiring the values of private data members and specialized mutator methods like setTime() and incrementTime() for performing operations on them. This allows programmers to validate changes to data members before making such a change. In this example, the setTime() method would be written to check for valid values for military time (hour is between 0 and 23 and minute and second are between 0 and 59.)

Encapsulation hides the private data members and the implementation details of a class, but it does not necessarily mean a class is completely isolated. Many objects must share information with other objects, usually with good reason. However, protecting private data by providing accessor and mutator methods ensures that an object is affected only in known ways by other objects in the system. Sharing should be minimized so that the class provides as few interfaces (public methods) as possible. Other methods required for the class should be private, so they are not available outside the class.

Using encapsulation properly and providing a controlled public interface is similar to a fast-food restaurant that has no indoor customer access and provides only a drive-up window. The customer can read the menu, place an order from the menu, pay for the order, and pick up the completed order. The customer does not know exactly what is occurring inside the restaurant; he or she knows only that placing an order and paying for it results in the food showing up in the completed order. The customer may return to the same fast-food restaurant every day for a month and interact with the restaurant in the same way. If the restaurant hires a consultant to cut costs and changes the way orders are filled, the customer has no knowledge of the change, and does not really need to know about it. As long as the customer interacts with the restaurant in the same way and the food tastes just as good each time, the customer does not really even care about the internal operations of the restaurant.

Proper use of encapsulation in C++ enhances program security for several reasons:

  1. The initial value of all data members is controlled by the programmer writing the code for the class.
  2. Access to an object’s data is controlled by the programmer writing the code for the class, resulting in fewer human errors caused by improper use of the class by other programmers. Encapsulated objects that declare data as private or protected act as a black box, protecting the object’s data and allowing access to the data only by calling class member methods, which control how the data can be accessed and/or changed. Data is secure because it can only be accessed in a safe manner, in the same way that a bank account is secure because it can only be accessed in a safe manner.
  3. Encapsulated objects provide full functionality; however, calling objects do not know the implementation details, and cannot rely on or take advantage of a particular implementation. This means that the implementation can be enhanced or updated in the future without affecting calling objects.
  4. If a class is isolated from the effects of other classes in the program using encapsulation, it is easier to trace a fault to the class that caused it and to limit the damage caused by the fault.

Risk – How can it happen?

If a programmer does not use encapsulation properly, the existence of the encapsulation feature in C++ provides no enhanced security. In fact, using encapsulation improperly can greatly increase the insecurity of the program. Encapsulation violations increase program insecurities in direct proportion to how much intelligence the class contains. If the class stores one simple data element, such as a char, whose value can be any legal char value, then encapsulation violations are probably not a big issue. As the data becomes more complicated, encapsulation violations create more and more insecurities, since the special properties of the data (such as limiting hour to values between 0 and 23, inclusive) are more and more likely to be violated.

As the rules of proper encapsulation are violated, all clients of the class become responsible for more and more code to correctly access and use the object’s data. As the class is enhanced and updated over time, it is not possible to correct all uses of the class, particularly if external users are out of the programmer’s control. Eventually the class loses integrity entirely and becomes a security vulnerability.

Example in Code:

Consider the following class declaration:

class RiskyTime
{
    public:
        int hour;
        int minute;
        int second;
        void setTime (int newHour, int newMinute, int newSecond);
        void getTime (int& currHour, int& currMinute, int& currSecond);
        void incrementTime();
};

The RiskyTime class has no modesty; its data is hanging out in public for the entire program to see. The reason this is an issue for a class is the same reason it is a personal issue. When a person is exposed, he or she may get sunburned in all the wrong places. When data is exposed, other components have complete access to that data and can cause mischief. Since RiskyTime allows all methods in the entire program direct access to its data members, an external method could set hour, minute, and second to an invalid time, such as 36:99:-33 (hour:minute:second). Clients that use the class cannot count on the time being valid. If the client uses the class, anyway, and does not check to be sure the object’s data is valid every time it is used, the invalid values can cause strange, and even dangerous, things to occur when the program executes.

Code Responsibly – how to encapsulate properly?

  1. Restrict access: Declare all data members as private. In addition, keep all methods that do not need to be called by external program components private. Create public methods to provide a controlled interface between the object’s data and all external program components.
  2. Know the limits: Familiarize yourself with the range of valid values for each data element being stored in the class.
  3. Initialize the data: Set all the data elements to valid initial values for an empty or non-existent object using a default constructor.
  4. Choose the data types wisely: For example, C++ contains multiple data types for storing integer values. Be sure to choose integer types that are large enough to hold all the valid values that will be stored on each type of computer that will be used.
  5. Validate the input: Check input for validity before changing the data values stored in the object.
  6. Double check all operations performed: Be sure that any operations that change the data, such as incrementing the time, maintain the validity of all the data elements.

Lab Assignment

top

Program 1

// file Time.h
class Time
{
   public:
      int hour;    // current hour in military time
      int minute;  // current minute in military time
      int second;  // current second in military time

      // set the time to the time specified by the parameters
      void setTime (int newHour, int newMinute, int newSecond);

      // return the time to the calling method using the provided
      // parameters
      void getTime (int& currHour, int& currMinute, int& currSecond);

      // increment the current time by one second
      void incrementTime (void);
};

// file Time.cpp
#include 
#include "Time.h"
using namespace std;

const int MAX_HOURS = 23;
const int MAX_MIN_SECS = 59;

void Time::setTime (int newHour, int newMinute, int newSecond)
{
   hour = newHour;
   minute = newMinute;
   second = newSecond;
}

void Time::getTime (int& currHour, int& currMinute, int& currSecond)
{
   currHour = hour;
   currMinute = minute;
   currSecond = second;
}

void Time::incrementTime (void)
{
   ++second;
}

// file main.cpp
#include 
#include 
#include "Time.h"
using namespace std;

int main (void)
{
   Time currTime;  // object that stores the current time
   int hr;         // current hour obtained from currTime
   int min;        // current minute obtained from currTime
   int sec;        // current second obtained from currTime

   currTime.setTime(20, 15, 43);
   currTime.getTime(hr, min, sec);
   cout << "The current military time is set to: "
        << hr << ":" << min << ":" << sec << endl;

   currTime.incrementTime();
   currTime.getTime(hr, min, sec);
   cout << "After incrementing the time, the current military time is: "
        << hr << ":" << min << ":" << sec << endl;

   return EXIT_SUCCESS;
}

Lab Questions:

  1. Type Program 1 above into the 3 separate files specified by the header comments. Compile and run the program.
  2. Look at the output. Does it make sense? Why or why not?
  3. Change the call to currTime.setTime() in main.cpp to the following:
    currTime.setTime(-55, 99, 1025);

    Compile and run the program.

  4. Look at the new output. Does it make sense? Why or why not?
  5. Add the following lines to main.cpp just before the return statement at the end of the method:
       currTime.hour = 31;
       currTime.minute = -10;
       currTime.second = 450;
       currTime.getTime(hr, min, sec);
       cout << "After direct assignment, the current military time is: "
       << hr << ":" << min << ":" << sec << endl;
    

    Compile and run the program.

  6. Look at the last line of output. Does it make sense? Why or why not?
  7. We need to fix the problem caused by declaring the data in the Time class as public. Change Time.h to make the 3 data declarations private. Compile the program. What happens? Why?
  8. Remove the lines that were added to main.cpp in step 5 above. Compile and run the program.
  9. Now, let's fix the setTime() method. Change it as shown below:
    void Time::setTime (int newHour, int newMinute, int newSecond)
    {
       if (newHour >= 0 && newHour <= MAX_HOURS)
       {
          hour = newHour;
       }
       else
       {
          cout << "Error: hour must be between 0 and 23 inclusive" << endl;
          hour = 0;
       }
       if (newMinute >= 0 && newMinute <= MAX_MIN_SECS)
       {
          minute = newMinute;
       }
      else
       {
          cout << "Error: minute must be between 0 and 59 inclusive" << endl;
          minute = 0;
       }
       if (newSecond >= 0 && newSecond <= MAX_MIN_SECS)
       {
          second = newSecond;
       }
       else
       {
          cout << "Error: second must be between 0 and 59 inclusive" << endl;
          second = 0;
    }
    

    Compile and run the program.

  10. Why is this version of the setTime() method more secure than the previous version?
  11. Look at the new output. Does it make sense? Why or why not?
  12. Change the call to currTime.setTime() in main.cpp to the following:
    currTime.setTime(23, 59, 59);

    Compile and run the program.

  13. Look at the new output. Does it make sense? Why or why not?
  14. Add an appropriate constructor to the Time class. Compile and run the program.
  15. What values should be used to initialize hour, minute, and second in the constructor? Why are these times appropriate?

*Copying and pasting programs may result in syntax errors and other inconsistencies. It is recommended you type each program.

 

Security Checklist

top

  1. Print Time.h, Time.cpp, and main.cpp.
  2. Print the Security Checklist shown below and use the printouts to complete the checklist.

Security Checklist

Vulnerability: Improper Encapsulation Course: CS2  

Completed
1. Circle each private data member.  
2. Mark with a V each public data member.  
3. Underline the method header for each method that accesses the values of private data members,but does not change them  
4. Mark with a V the method header for each method that changes the value of any private data members.  
5. Mark with a C any constructors that initialize data members. C stands for constructor (see question #4 below.)  
Highlighted areas indicate vulnerabilities!  

Discussion Questions

top
  1. Why are public data members in a class a major security issue?
  2. Are accessor methods vulnerabilities in a class? Why or why not?
  3. Why is it important for a programmer to know the range of valid values for each data element being stored in the class?
  4. Is it acceptable to omit default constructors in a class? Why or why not?
  5. Why should the data types for class data members be chosen carefully?
  6. Why should a mutator method validate the input before changing the data values stored in an object?
  7. Why is it important for a programmer to double check any operations that change the value of class data members?

Further Work (optional – check with your instructor to see if you need to perform the following steps)

  1. Now, let's fix the incrementTime() method. Change it as shown below:
    void Time::incrementTime (void)
    {
       second = ++second % (MAX_MIN_SECS + 1);
       
       if (second == 0)
       {
          minute = ++minute % (MAX_MIN_SECS + 1);
       }
       if (minute == 0)
       {
          hour = ++hour % (MAX_HOURS + 1);
       }
    }
    

    Compile and run the program.

  2. Why is this version of the incrementTime() method more secure than the original version?
  3. Look at the new output. Does it make sense? Why or why not?
  4. Add the following lines to main.cpp just before the call to currTime.setTime():
           currTime.getTime(hr, min, sec);
           cout << "The initial military time is set to: "
           << hr << ":" << min << ":"
           << sec << endl;
    

    Compile and run the program.

  5. Look at the first line of output. Does it make sense? Why or why not?

 
Copyright © Towson University