Real Programmers do not Write Secure Programsdoc
Real Programmers do not Write Secure Programsdoc
Page 1 of 8
William Hugh Murray shared this file. Want to do more with it?
  1. Real Programmers do not Write Secure ProgramsLet me tell you a story. Once upon a time, back in the late sixties, for my sins, I spent five years as a development manager. Among other things, I developed the access control facility for IBM’s Advanced Administrative System. In its day this transaction-driven business processing system was state-of-the-art and the access control system served as a model for later systems. (It survives today but is hidden behind a layer or two of web servers.)The Advanced Administrative System ProjectThe AAS project was managed by some of the most experienced large-system managers and architects in IBM. Many of the senior managers were veterans of the American Airlines Sabre System. They included the legendary Cecil Webb, Ed O’Dea, George Evans, and Joe Wimbrow. Collectively and even individually, these great pioneers forgot more about large system implementation than most of us remember. I was fortunate to have them as my mentors. While I had already been workingin information technology for a decade, I had really barely paid my dues. I was one of the junior managers on this 500-man-year project and it was to be my masterwork. Only about ten percent of the programmers on the project had any experience. It did not take very much experience to qualify one as a manager. Ninety percent of the code on the project was written by new hires. As one of the junior managers, I did not qualify for any real programmers, only new-hire coders and a couple of analysts and designers. I was to learn that this was to be the key to my success. In order to write secure security programs securely, it really helps not to have any real programmers. As time went on and I became more senior, I began to get a few real programmers.Most of them did not much like the way we worked. I found that managing real programmers was like herding cats. Real programmers are individuals, not to say individualists. However, in my group we worked as a team. It was the only way to write serious computer programs with new hires. Division of FunctionsReal programmers do the whole job while we divided the job up into small very small tasks and functions. We divided design from coding from testing. First we all worked on design and we did notstart to write code until user management had accepted the design. Real programmers design as they go. They design from a vision rather than from requirements. Therefore, real programmers do not talk to users and certainly do not allow them to approve their work.
  2. Restrictive Programming LanguageReal programmers write in assembler while we used a minimal set of project-specific macros. One of the “features” of this language was that it contained no “goto” instruction, i.e., no unconditional branch instruction. Real programmers do not work in such primitive and restrictive languages. However, never having seen a “goto” instruction, our coders did not miss it. The result was programs that were significantly less complex than those traditionally produced by real programmers. A feature of the environment was that programs could only be entered at the beginning; they could not be entered at arbitrary points. By convention, there was only one exit from the program and that was at the end. While the IBM 360 did not enforce data types, it did have a few different ones; for example, it distinguished between binary integers and characters. While it was very sensitive to the distinction between the two, it did not enforce the distinction. There were some instructions that were only valid on certain data types; an attempt to use one of these instructions on improperly formatted data would result in a “data exception” or the dreaded ABEND. Unlike modern systems, the 360 was very unforgiving; it was far more likely to fail to a halt than to fail by doing the wrong thing. While this made it far less vulnerable to malice than modern systems, the pain of error could be very high. In almost every case, it required operator intervention to correct. Since operator intervention was expensive, the privileges required by operators were very powerful, and because operators were also error prone, we had to have protection from these exceptions. Since the architecture did not protect us from them and because new-hire coders are essentially error-prone, we used the restrictive language to protect usCommon Tools and WorkbenchReal programmers use their own tools. We used a system much like that used in a machine shop where all of the tools are produced by a master tool and die maker. All members of our team used the same set of tools and workbench produced by a master programmer. Such master programmers are different from real programmers in that the tools that they produce are not for their own use but the use of others. Needless to say, real programmers did not like these tools or workbench because they were far too restrictive. On the other hand, our coders found these tools supportive and comforting. From management’s perspective, these tools and the workbenchenabled relatively inexperienced coders to be very productive. Indeed, in their absence, it is not clear that they would have been productive at all. Restricted Definition of Variables and Naming of ProgramsReal programmers define and name their own variables, both local and global. Our macro language did not provide the coder with the functional capability to do this. A group within data management defined global variables while a designated individual within each project defined local variables. While real programmers name their own programs, the power to create and name programs within our system was reserved to management and each program was bound to both its name and its specification by the system environment.
  3. Parsing of InputReal programmers parse (or fail to parse) their own input. In modern systems the failure to properly parse input results in the deadly “buffer overflow.” While most of these failures only impact a user or an application, there are a sufficiently large number of them visible to the Internet as to put the whole network at risk. In our system, the failure to properly parse input might cause a data exception and require operator intervention. In order to get around this, we developed the “Standard (input) Edit” routine. This routine ensured that all input matched the definition of the named variable. This routine protected us from data exceptions and coder error. Standard Edit employed the “Standard (user) Error” routine to communicate with the end user to correct input errors that it detected. Thus, our coders were protected from user error and the system was protected from coder errors. Test Data and TestingReal programmers write their own test cases and test data; they test their own programs while we divided these tasks. Real programmers write the test cases and test data only after the code is written. However, in our team, we wrote the test data before we wrote the code; it was part of the specification. The program had to fit the test data; real programmers may write the test data to fit the program. Real programmers record the test input but they carry the expected output in their heads. Our inexperienced coders needed to have the expected results written down. Real programmers reconcile their own test results while we reconciled one another’s tests; we found this to be helpful in identifying and correcting errors and in demonstrating that testing was complete and that the code worked as intended.Special Testing EnvironmentAll real programmers know that they must test in the production environment and against live data. They know that there are error conditions that just cannot be found any other way. However, we had 5000 users who sat on their hands when production was down. We knew that testing tends to destabilize production systems. Moreover, our production data was sensitive both to disclosure and contamination. Therefore, we maintained a rigorous separation between development, testing, and production. Coders did not have user names on the production system. Any testing on production machines had to be done by authorized users, never by our coders. This required that we have a much more supportive test environment than real programmers are accustomed to. It required that part of the generation of the test data had to be automated and that the coders and testers be provided with tools for this automated test data generation. Where the coders and testers could convince the user of the necessity, the users could, on their authority and at their discretion and risk, provide sanitized copies of the live data for use in testing. However, only programs already accepted by user management, operations, and the maintainers could ever be run in the production environment and against live data.
  4. Completion of TestingReal programmers have reliable intuitions to enable them to know that testing is complete and the program is ready for production. Our inexperienced coders did not have the benefit of such intuition. We had to rely upon users, operators, and maintainers to tell us that the program was complete. Thus a program was not complete when the coder said that it was, when he was no longer able to find any more of his own errors, but when he could convince others that it was.Program Acceptanceby MaintainersYes, maintainers. Real programmers do not do maintenance and they certainly do not let those second-class programmers judge their work. Real programmers throw their product over the wall and the maintainers are expected to make do with whatever they get. Our experienced management understood that it is the maintainers who keep the system going long after the originators have moved on. They understood that the maintainers, whose work required them to work with foreign code and to do so under pressure, were the truly elite programmers. Therefore, they gave the maintainers the same right to negotiate acceptance criteria up front, that users and operators required. We wanted to please the users; they ran the business and made the money topay us. They were able to appreciate the function that we produced. However, they could not appreciate the quality of the code or the documentation. It was the maintainers that could appreciate that quality. The maintainers could appreciate elegance, that is, they appreciated simplicity, structure, code that was obvious to as to its intent, and well documented. They appreciated code that was modular, where they could safely make changes and know that the effect of those changes would be localized. Praise from the maintainers was sweet indeed. The price of not getting the maintainers to accept your work was that you might be condemned to maintain it yourself. The reader will not be surprised to learn that in our shop the pay scale for maintenance programmers was a notch higher than that for mere coders, or even that for real programmers. Similarly, it should come as no surprise that there were no new-hires in maintenance, or that transfer to maintenance always involved a promotion. Restricted Module Size and FunctionReal programmers understand that the scale and size of a program are the same thing; a program will be just the size that it needs to be, no larger and certainly no smaller. One real programmer left IBM rather than consent to the constraint that object modules had to be atomic and could not exceed 4K in length. He could divide his program into single lines of code but not into atomic modules. He would be surprised to learn that ninety percent of the modules were smaller than 2K. Youwill not be surprised to learn that when last heard from he was teaching at Columbia University, creating more real programmers.
  5. Our coders new, in a different sense of knowing, that, as a program can be decomposed into lines of code, it can also be divided into modules of almost any size. Their management knew that the ability of anyone to understand the intent and behavior of a program goes down as its size goes up. Similarly, the number of people who can understand a program goes down as the size of the program goes up. Long before the size of the program approaches infinity, the number of people capable of understanding it reaches zero. As far as I can remember, the choice of a 4K-module size was mostly arbitrary. While it is probably not purecoincidence that the size was equal to one page, it was more important to us that the size be limited to a comprehensible scale than that the loader might be a little more efficient. Separation of Procedure and DataReal programmers know that one man’sprogram is another man’s data. They also know that memory can be conserved and performance improved if a program may operate upon itself. Indeed, if the computer is ever to approach the generality and flexibility of paper and pencil, this capability is essential. Therefore, real programmers believe that there should not be any separation between a program and its data. It is hard to remember now but even as late as 1975 storage was very dear and the implementation of paging was still novel. In AAS, for reasons of performance and efficient memory use, we wanted a only single copy of a program to be present in memory at a time, concurrently usable by any number of our five thousand users. This meant that the program could have only one state. It could not be modified at all, either by itself or any other program. Programs were stored in write-protected pages. They were paged in on demand and out on a least-used algorithm. There was no capability in the system for a program to be modified. Therefore, there was no way for it to be contaminated or interfered with by its user, its data, or even another program. While this system property was primarily about performance, it improved its reliability and security.State was associated with a terminal or a user and was kept in a special storage block called the terminal working area (TWA). Each TWA was 4K in length and much of its format was project defined. Applications used it for user-to-program, program-to-user, and program-to-program communication. Its size could be extended on demand in fixed-size increments. Real programmers will recognize this as the discredited Aiken model of computing. They will point out that had we adhered to this model, the computer would not have been as general, flexible, or popular as it has become. However, we are not talking about popular but secure. While it is possible for a program to be insecure even if it is protected from all interference and contamination, it can never be secure unless it is so protected. Real programmers prefer to provide such protection locally so as to preserve
  6. as much generality and flexibility as possible. We argue that this is much harder to do than the kind of global protection that we used. ScheduleReal programmers have a real problem with schedule. While they focus on it, not to say obsess about it, they are not very good at meeting schedule. This is in part because they believe that schedule is about the number of lines of code per unit time. When they fail to make schedule, they vow that the next time they will produce more lines of code per unit time. Now, no matter where one sets the target for the number of lines of code per unit time, one will make it. They will make it at the expense of quality, which is what they should have been managing in the first place. The problem is not that they do not produce enough lines of code per unit time but that when they put them all together they do not work as intended. The problem is in part that real programmers think that programming is about coding. In practice we spend far more time on designing and testing then we spend on coding. Coding faster cannot help much. Real programmers want to make personal schedules even at the expense of the team schedule. We learned thatonly external schedules count. We learned that if we managed quality, even at the expense of internal schedule, we would always make external schedules. If there is another way to make them, we did not need it and did not learn it. Application-only SystemWhen I first described this work to Dr. Willis Ware, he responded that it did not solve the “real problem,” the general case, more specifically the case where the end user could write and execute an arbitrary program of his own choice. True. However, my response to Dr. Ware was that one cannot say that one has reserved all generality and flexibility to all users and still make any useful statements about security. The question is what is one prepared to give up in the name of what security. Dr. Ware’s concern is real programmers. Even today, we have few operating systems that can contain a user who can execute a program of his own choice. Most hackers assume that if they can run a program of their own choice, they can expand their privileges.They have standard procedures, called “root kits,” that they use to do this; their assumption is that if they can run the kit, they can often get system manager privileges. While root kits may fail on well configured and managed systems, their existence, let alone their effectiveness, suggests how difficult it is to contain real programmers. Today, IBM warrants that an unprivileged user cannot expand his privilege (except by means of a mechanism subject to management control). In the late sixties andearly seventies there were no such systems or warranties available. Even today the only remedy that IBM offers is that if it can happen, they will fix it.
  7. In the AAS, users could only process transactions using named and static programs. The production system did not contain any functionality to create or maintain programs. Dr. Ware did not like this restriction on functionality but even he would have to admit that it is much easier to achieve security in such an environment. Hidden Operating System AAS used a general-purpose operating system. While it ended up running on MVS, it also ran on earlier versions of OS/360. However, an application monitor that ran on top of the operating system provided both the application programming interface and the user interface. This monitor hid the operating system from all of the users. Modern transaction, database, and web servers hide their operating systems in a similar manner. When they do so, it may still be possible for an attacker to cause the system to fail but much more difficult to gain control of the system. Much of what we talk about as major security vulnerability is when these servers fail to hide their operating systems. Application programs did not fail very often but when they failed, they failed to user logon, not to the application monitor and certainly not to the real operating system. Using Real Programmers in Producing Secure ProgramsWe learned to use real programmers, mostly to mentor the coders, do code and document reviews, and supervise testing. Most of them did not like it much and moved on. Every now and then one of them would “discover” the method in our madness. Some became quite smug about it; others became “true believers.” They went on to apply these ideas to otherdifficult projects. Even real programmers like success. Now do not get me wrong. I have nothing against real programmers. Some of my best friends are real programmers. I recognize that they are the product of their environment and that I am part ofthat environment. I take my part of the responsibility for making them what they are. I appreciate that they produce most of the real programs in the world and that I am indebted to them for the applications I use every day. They do many things very well. I simply point out that their experience and life view disqualify them from writing secure programs. Producing Secure Security Programs SecurelyThus, we produced secure security programs securely. However, this method was not really about and was not limited to security. We used it on all programs because it was about quality and productivity. I wish that I could tell you we understood all this at the time, that we did it all by design and intent. I even wish that I could tell you that it was asideal as I make it sound or even as I remember it. Of course, we were more lucky than smart. We thought we were simply compensating for our limitations and at one level we were. However, at another we were compensating for the fundamental difficulty ofwriting complex systems. Without really knowing it, we were recapitulating the methods of the accountants, machinists, and engineers.
  8. I recognize that much of this is counter-intuitive. I can only say that programming, like security, is a space in which intuition does not serve us well. Application of these Rules and ProceduresI do not recommend that all programs and systems be developed under the rules and procedures described above. The use of these rules and procedures requires vision, planning, management, and control. These may be justified only on large and sensitive programs. I believe that they are justified for most operating systems and for any system or application that expects to make any claims to security or “securability.”Similarly, I do not recommend that all, or even most, of these rules be used on any system. It is not my purpose to advocate the use of these rules and procedures but to use them to demonstrate how we can produce secure programs securely when we want to do it. That said, I hope that the story illustrates some interesting points about what it means to talk about secure programming. I like to think that most programmers, real or otherwise, can find a useful idea or two in this story.
We use cookies to provide, improve, protect and promote our services. Visit our Privacy Policy and Privacy Policy FAQs to learn more. You can manage your personal preferences, including your ‘Do not sell or share my personal data to third parties’ setting using the “Customize cookies” button below.