Designing and Building Secure Software- Introduction
How should we write software that will be secure? A flawed approach, would be to simply build the software while ignoring all security concerns, planing to revisit security later. There's no doubt that most of the time functionality is more important than security. We are writing software for a purpose. And we want the software's design to serve that purpose effectively, using efficient code, a nice user interface, and so on. But ignoring security at first means that security does not receive the attention that it should. And when the ship date arrives for our software, it shouldn't be surprising if the software has important vulnerabilities still in it. Therefore, a better approach is to build security in, right from the start. We want to incorporate security-minded thinking throughout the development process. As such, we can avoid missing important security requirements, or making critical security mistakes in the software design when the relevent, development activities are under way. That way, we won't discover problems at the end, when they can be very hard to fix. In this unit, we'll take a step back and look at the software development process as a whole. Since there are many processes in use today, from the traditional waterfall model to agile methods to combinations in between, we will focus on four common phases or activities that appear in all of them. The requirements phase involves determining what the software should do, and not do. The design phase looks at how to structure the system to meet these requirements. The implementation phase solves, involves actually writing code to implement the design. And the testing or assurance phase involves checking that the implementation actually does what it's supposed to. These phases recur in various guises throughout development. For example, they apply to the system as a whole and they apply to individual components. They may be reapplied as the system evolves. So, where does security minded thinking or said more actively, security engineering fit in. Well, all of these phases, of course. Throughout this unit, we'll look at several activities aimed at building secure software. During the requirements phase, we must consider security requirements that are specific to our security goals. To help, we should aim to develop, Abuse Cases that indicate potential security problems the software could experience. These Abuse Cases are informed by architectural risk analysis, which explicitly models the threats an attacker poses to a system, and considers the various ways under that model the attacker could try to compromise security. With the Security Requirements and risks identified, we can conduct a Security-oriented Design of the system, applying various tried and true principles and rules for securing that system. Once we have implemented some or part of this design, we can use Code Review. Ideally automated tools involved in that review, to try to find coding defects that could compromise security. We must also conduct a test plan which considers the risk of attack, and makes sure that a system can defend against the most likely, or most costly attacks. Finally, once the system, or particular components of it is fully constructed, we can make sure that it is secure end to end. Penetration testing looks at a system as an adversary would, and makes sure that the system really does resist attack as we expect it to. Note that in this course we're focusing on a system's software, but we should note that the hardware is also an important element of the design with respect to security. Software's strength is that it is malleable and easily changed. As such, last minute changes to design, and future improvements are easily accommodated. But this malleability creates a broader surface for attack.
Hardware on the other hand, is very efficient, but it's also very rigidly defined. The cost of fabrication is such that it is simply not cost effective to make changes too often. But this rigidity is arguably good for security. Hardware is often carefully vetted, since mistakes are so expensive to correct, and as such, security mistakes occur less often. This immutability of the hardware, also makes the hardware harder to attack. In fact, hardware security advantages are such that security features are starting to show up in hardware more often. For example Intel's AES-NI instructions, implement fast and resilient symmetric cryptography. Intel has advertised a new chipset called SGX, that supports a kind of encrypted computation. Essentially code is encrypted until it is executed, so it can be hidden from prying eyes. The motivation is privacy on cloud computing platforms. There are also emerging hardware primitives, that software can use to implement strong security. For example, physically uncloneable functions, or PUFs, exploit the physical properties of chips as a source of strong randomness. This randomness is useful for cryptographic schemes, for example, to implement authentication. Intel's announced MPX extension provides primitives that compilers can use to implement memory safety more efficiently. If you're interested in learning more, I encourage you to check out Gang Qu's course on Hardware Security, it's also part of our cyber security specialization hosted by the Maryland Cyber Security Center. Gang is an expert in Hardware Security, and he's an Associate Professor in the Electrical and Computer Engineering department at the University of Maryland. Okay, to finish up the segment I'll briefly introduce an example that we'll return to at several times throughout the remainder of the unit, and that is online banking. In particular supposed we wanted to write software that allows account holders at a bank to have online access to their accounts. What requirements might we have for this application? Well obviously, we would like account holders to be able to deposit funds into their accounts. And we would like to prevent other people who are not authorized by the account holder from withdrawing those funds, say by initiating a bank transfer. We would like the account holder to, at whenever time they choose, access their funds by say withdrawing them or transferring them from their account. We don't want access prevented by a third party. And of course, we may have other security properties in mind, we'll see several as we go forward.
Threat Modeling, or Architectural Risk Analysis
To decide how to secure your software, you have to understand what kind of adversary you're securing it against. To do so, you should make an explicit threat model for the system. A threat model defines the adversary's assumed powers in terms of how they could be used to attack your system. It's extremely important that this definition characterizes reality. Otherwise you may needlessly defend against attacks that are unlikely to happen or you'll fail to defend against those attacks that will. A threat model is thus critically important because it makes plain what you are up against. Even if the idea of an attacker is vague you can undoubtedly do some things to secure your system. But you are unlikely to put together a coherent defense. Making the attacking threat explicit makes it far more likely that you'll have all of your defenses aligned to a common purpose. All of this is part of architectural risk analysis. Which is the process of assessing the risk of a security failure based on the likelihood and cost of various attacks. And doing what you can to minimize that risk. So now let's consider a few example threat models. The first, and simplest, is that of a network user. A network user is an anonymous user that can connect to the service via network. What happens if such a user were malicious, that is, an attacker? Well, the user could measure the size and timing of requests and responses to and from the service. The user could run parallel sessions, for example, masquerading as multiple users and interleaving their messages in various ways. The attacker could provide malformed inputs or malformed messages. And the attacker could drop or send extra messages. Such an attacker, simply a network user, can perpetrate many attacks that we have seen so far, including SQL injection, cross site scripting, cross site request forgery, buffer overruns, perhaps by using return-oriented programming payloads, and many others. The second sort of attacker that we'll look at is the snooping user. This is an internet user on the same network as other users of some service. For example someone connected to an unencrypted wi-fi network at a coffee shop. Such a user has the same capabilities as a network user, but in addition, can read or measure other's messages. Intercept, duplicate, and modify those messages. And as a result, formulate the following attacks. Such an attacker could hijack another user's session by reading the session key out of their communications and then using it. The attacker could also violate privacy by reading unencrypted information that's exchanged between that user and the service. The attacker may even be able to learn something from encrypted communications by inferring properties based on the size or frequency of messages. Finally the attacker may be able to deny service by, for example, intercepting messages from a user but then throwing them away, rather than delivering them to the service as intended. As a final example threat model, let's consider the co-located user. This an internet user on the same machine as other other users of some service. This could happen by having malware installed on a user's machine, for example. Such a co-located user can addition. Such a co-located user can, in addition to all the capabilities we've already seen. Read or write the user's files, for example, reading cookies or writing those cookies, and even read memory. The attacker may be able to snoop key presses and other events. The attacker could read or write the user's display, for example, to spoof the users web browser to simulate an encrypted communication, when in fact none is taking place.
As an example attack, a co-located user can steal a password. For example, by snooping the key presses as the user types in their password when logging on to a secure website. Once you have your threat model defined, you can work out how you will respond to the threat. Different threat models will elicit different responses. For example, when considering a network-only attacker, which can communicate with your system but do nothing else, you can be sure that your communications are safe. And as such, there's no need to encrypt messages. The adversary can always see his own messages and by assumption, he cannot see the messages of other users. This is the sort of threat model that the old telnet remote login program considered. It assumed that one user might attempt to masquerade as another user and so passwords for authentication were required. But it did not assume that one user might snoop the traffic of another user, and thereby steal her password. In some settings, this might be a reasonable assumption, but today, with wi-fi and mobile networks the standard, it's probably not safe to assume that attackers cannot observe others' message traffic. Which gets back to the point that your threat model must match reality to be useful. A snooping attacker is a more likely threat. And in this case, you should use encryption to protect your data. The encryption could be deployed at the link layer by using encrypted wi-fi like WPA2. At the network layer using IPsec, or at the application layer using SSL or something similar. Note that you might want to distinguish between an attacker that can snoop traffic, as opposed to one that can intercept and change traffic. Encryption may not be sufficient for such an attacker. Now a co-located attacker is the most powerful threat and thus the most difficult to defend against. But such an attacker is real. Some banks assume that users connecting to their sites are doing so from machines with malware installed. As such, the attacker who controls the malware. Can use the user's id and password, perhaps by capturing keystrokes that the user is typing. One way to defend against such snooping is to provide users with a separate device that is involved in the authentication process, but is not connected to the computer, and therefore not under the purview of the malware. We assume that this device is not compromised, even though the computer might be. We'll look more closely at authentication troubles a bit later. Now, just to, just to reiterate. A poorly chosen model, will result in poor security. In particular, assumptions you make on what an attacker can not do. Are potential weaknesses if in fact the attacker does not have the assumed limitation. As I mentioned a moment ago, assuming that attackers cannot snoop messages on a network is probably a bad idea. But beyond this, there are other poor assumptions that you could make. For example, people mistakenly think that cryptography hides communications as if they were being carried by a pipe that completely hides its contents. In fact, the attacker can still see individual messages, and their metadata such as the source and destination, and features like message length. Prior work has shown that this information is sufficient to identify user interactions that produce particular messages, and this can result in lost privacy. People also sometimes fail to consider that adversaries can measure the timing and other features of a communication. In some cases such measurements are sufficient to infer secret information like cryptographic keys. Given these failures, what steps should you take to ensure your threat model is realistic? First, you can use other threat models as a starting point, when their systems are similar to yours. Second, you can stay informed about the state of the art in cybersecurity attacks and attack patterns. And you can then apply your knowledge to the system you are building to assess whether new attacks are a real threat. Finally, allow your threat model to evolve as you design your system. Challenge assumptions in your design. What capabilities must an attacker have to defeat defense mechanisms that you're thinking of using? How likely is an attacker to have these capabilities? How will you know that? And what would be the cost of a failure? You may decide that a strong attacker is unlikely, but that the consequences are high enough that you should defend against that attacker anyway. And this is the essence of risk assessment. Taking into account the potential costs of building a system, the cost of a successful breach, and weighing the likelihood of each.
Security Requirements
. When considering the overall requirements for the software that we want to build, we must also consider its security requirements. Normal software requirements are about what the software should do. Security requirements outline the security expectations of the software's operation. And these come in two flavors. First, there are the security-related goals or policies. For example, a policy for our online bank application might be that one user's bank account balance should be private. That is, no other user should be able to figure out what it is without proper authorization. Second, we may have requirements concerning the mechanisms we use to enforce security. For example, we may require that for password based authentication, passwords must be strong. And the database storing those passwords must not be accessible to any program other than the authenticating login program. Now let's dig a little deeper and explore different sorts of policies and mechanisms a designer might employ. There are three classic types of policy. Confidentiality policies, integrity policies and availability policies. Confidentiality policies are sometimes further broken down into privacy and anonymity requirements. Required security mechanisms often address three sorts of activities. Authentication, authorization and auditability. Let's look at each of these elements more closely. First, we'll consider the three kinds of security policy beginning with privacy and confidentiality. Privacy and confidentiality are ensured when sensitive information is not leaked to unauthorized parties. We typically refer to this property as privacy for individuals, and confidentiality for data. Sometimes we also refer to it as secrecy. As an example, for our online bank the bank account status. Which includes the bank account balance, should only be known to the account owner and not to any other parties. A violation of privacy or confidentiality could occur directly, or could occur by a side channel. For example, it might be possible to manipulate the system to directly display Bob's bank account balance to Alice, and thereby violate the policy. But it might also be possible to learn that Bob has an account at the bank, because there is a shorter delay on login failure. That is a poorly designed login system might take a long time to go through the database to find whether or not a user has an account. On the other hand, if the user does have an account, the returned login failure may occur more quickly. Another sort of confidentiality policy is anonymity. This is a specific kind of privacy. As an example, we could say that non-account holders should be able to browse the bank informational site without being tracked. And having their identity compromised by the bank, or the advertisers that the bank works with. In this case, the adversary is not users outside the banking system itself, but instead the bank and maybe third party advertisers. In the other examples, we considered the account holders or malicious third parties as the ad, adversaries. The next kind of security policy that we'll consider is an integrity policy. The idea here is that sensitive information should not be damaged by computations acting on behalf of unauthorized parties. For example, only the account owner can authorize withdrawals from her account. If some other party were able to affect an account withdrawal, that would violate the integrity of the bank account balance. Once again, violations of integrity can be direct or indirect. For example, we may be able to specifically withdraw from the account, because the account system does not properly authorize such actions. Or, there may be a way of confusing the system into, into doing it. For example, by using a cross-site request forgery. The third sort of security policy we'll consider is availability. In this case, availability means that the system is responsive to requests made to it. For example, we may want a user to always be able to access her account balance for queries and withdrawals. We do not, on the other hand, want a denial of service to be able to take place. In this case, an attacker is able to overwhelm a site with useless requests, and thereby, prevent the site from servicing the reasonable ones. Another way of having a denial of service attack is simply to cut off access by the network to the site. Once we've defined the security policies we have in mind for an application. We have to think about how we're gong to enforce those policies. Leslie Lamport defined the gold standard as the three sorts of mechanisms often provided by a system to perform this sort of enforcement. These mechanisms include authentication, authorization and audit. And of course, gold comes from the first two letters of each of these actions. A-U, from the periodic table. The Gold Standard is both a requirement and a kind of design. In particular, the sorts of policies that you are considering may determine how, what sort of authorization mechanism you need to use. For example, if you're using user versus user policies, like in our online banking system. We need to have an authentication and authorization mechanism that distinguishes the actions of different users. The kinds of users that we consider may determine how we authenticate them. Okay. Let's consider the first element of the Gold Standard, which is authentication. The goal of authentication is to determine the subject of a particular security policy. In particular, many policies require a notion of identity. In order to authorize a particular action, we need to know who is performing that action. By who, we also mean a principal. That is, it could also be a human being. Or it could be some service or a computer program. Either way, we need to identify that actor and determine whether the proposed action is continent with our policy. So, the question that authentication tries to answer is, is the user who he says he is? That is, a principal that claims to be a particular identity. Can we tell whether or not that claim is true? We might do this in several ways. First of all, we might try to see whether the claimed user knows something that only the real user would know. This is the theory behind passwords. Only the real user should know his or her password. And therefore, if the password is entered correctly we must be dealing with the real user. Another approach is to base it on something that the user is, like a biometric. So for a human user we might check a retinal scan or a fingerprint. Another idea is to base authentication on something the user has, like a smartphone. We might assume that a particular smartphone phone number is only in the possession of a particular user. And therefore, communication via that phone will be to that user. Authentication mechanisms that employ more than one of these factors, are called multi-factor authentication methods, or MFAs. As a common example, a bank may employ passwords as a first factor. And then send a text of a special code to a user's smart phone, asking the user to enter that code before authentication is complete. The next element of the Gold Standard is authorization. Authorization defines when a principal may perform a particular action. For example, Bob is authorized to access his own account, but he is not authorized to access Alice's account. There are a wide variety of security policies that involve some sort of authorization. For example, the policy we just saw, is a user based, access control policy. That is, accesses defined in terms of the users performing particular actions, like Alice or Bob. Access control policies could also be role based. For example, a bank teller or a bank manager may be allowed to perform certain things. And different individuals may at different times fulfill that role. Policies could even be originator based. For example, Bob could originate a policy that allows Alice to withdraw a fixed amount of money from his account. The final element of the Gold Standard is audit. The idea here, is to retain enough information to be able to determine the circumstances of a breach or misbehavior. Or, to establish that a breach or misbehavior did not in fact occur. Such information is often stored in log files. In order to be trustworthy, we must protect these files from tampering. As an example, every account-related action may be logged locally, and then mirrored at a separate site to avoid tampering. These logs could be used for example, to confirm or refute a claim that Bob might make that a withdrawal took place from his account. How should you determine what your security requirements are? There could be many processes that you could employ. First, forces external to your particular project may come into play. There may be laws or regulations governing applications of the kind that you are setting out to write. For example, if you are writing an online health record database for use in the US, you must consider the privacy requirements mandated by HIPAA, the Health Insurance Portability and Accountability Act. Or, if you're writing a financial management application, you may need to consider the implications of Sarbanes-Oxley, also known as SOX. You may also be encouraged to implement security requirements in line with your organization's values. For example, if you work at a consumer advocacy organization, you might have a strong inclination to protect user privacy. Security policy requirements might also arise from the exercise of threat modeling and architectural risk analysis. After considering the likely adversaries and their goals and capabilities, you might identify resources that you should protect. You might also draw insight from prior attacks that you have observed or experienced, to know more about what resources are security relevant. And how they might be compromised. And thereby define policies or mechanisms aimed at thwarting potential attacks. A particularly fruitful activity can be the design of abuse cases. In traditional software engineering processes, use cases are stories describing how software or software features can be used. As such, they illustrate functional requirements. Abuse cases on the other hand, illustrate security requirements. In particular, they identify things that a software system should not do. Considering our online bank application once again. An example use case might be that the system allows bank managers to modify an account's interest rate. An abuse case would be a user who is able to masquerade as the manager, and thereby change the interest rate on his own account. What process might you use to come up with abuse cases. One approach is to start with likely scenarios and patterns from known attacks. And then think of cases in which an adversary can break a security requirement. And adversary's power is determined by the threat model. And the violation of the requirement could be due to a failure of an assumed security mechanism. For example, suppose we were assuming a co-located attacker. Then an abuse case might be that the attacker steals the password file, which is within his capabilities as being co-located. And thereby learns all of the passwords. He might be able to do this because the password file is unencrypted. Another abuse case might be a snooping attacker that is able to capture a users message and then replay it. If the banking application cannot tell replayed messages from the originals, then the replay might have negative consequences. For example, it could affect a repeated transfer of funds from an account. Such replayed messages might be prevented by including a nonce, or other unique identifier in the message.
Avoiding Flaws with Principles
Once we've determined our normal requirements and our security requirements, we must design the software and we must implement it. Defects could be introduced in either of these two activities. Defects in the design are called flaws. And defects in the implementation are called bugs. The goal of the design phase from a security perspective is to avoid flaws. Now, while a lot of security related focus is on implementation bugs like buffer overruns. Flaws are equally important. According to security guru Gary McGraw in his book Software Security, roughly half of security-relevant software defects are flaws, not bugs. So getting the design right is extremely important. What I've just said may make it seem like there's a crisp distinction between the design and implementation of a piece of software. But that's not entirely true, rather design and implementation occur at many different levels of abstraction. For example, at the highest level we're concerned about a system's main actors. These might be elements like a web server, a database management system and a client's browser. Each running in it's own process and communicating with one another. The choice of programming language to use or framework is also a fairly high level design decision. At the next level down we're concerned with the internal design of each of these actors. A web server is going to consist of many different functions and objects, how should these be organized? And how do they interact? This is a question of design. At the lowest level we must decide how to represent data structures. How to implement a piece of functionality, for example, with one method or three and so on. When someone speaks generically about a software system's design he may be speaking about any or all of these levels. When speaking about it's implementation he might also be thinking about the lower level or maybe the lower two. However, we believe there is design happening at the lower levels and the choices made there effect security. So in speaking about design we mean all three levels, and which in particular, should be clear from context. The software design process aims to produce a software architecture according to good principles and rules. This is an iterative process. We first put down our initial design. And then we perform a risk based analysis of that design. As a result, we may determine that the design needs to be improved. And so, we apply our principles and rules and then improve until we are satisfied. A principle is a high-level design goal with many possible manifestations. While a rule is a specific practice that is consonant with sound design principles. Now the difference between these two can be fuzzy, just as the difference between design and implementation is fuzzy. For example, there is often a principle underlying specific practices. Principles may also sometimes overlap. The software design phase tends to focus on principles for avoiding flaws and less on rules which are more part of the implementation phase. We can group our set of software design principles into three categories. The first category is prevention. These sorts of principles aim to eliminate software defects entirely. For example, the Heartbleed bug would have been prevented by using a type-safe langauge, like Java, which prevents outright buffer overflows. The second category of principle is mitigation. These principles aim to reduce the harm from exploitation of unknown defects. That is, we can assume that some defects will escape our elimination during the implementation phase. But if we design our software in the right way, we may make it harder for an adversary to exploit these defects in a way that's profitable to him. As an example we could run each browser tab in a separate process. So that exploitation of one tab does not yield access to data that is stored in another tab because it is isolated by the process mechanism. The third category of principle is detection and recovery. The idea here is to identify and understand and attack, potentially undoing its damage if we're able to recover. As an example, we can perform monitoring of the execution of our program. To try to decide whether an attack has taken place or might be in the process of taking place. And we could perform snapshotting of various stages of our software's execution. In order to revert to a snapshot if we discover later that an attack has corrupted our system. So now, without further ado, here are the core principles we have identified for constructing secure software. First, favor simplicity and in particular use fail-safe defaults and do not expect expert users. Next, trust with reluctance by employing a small, trusted computing base and granting a component the least privilege it needs to get its work done. Ensuring least, least privilege may involve promoting privacy and compartmentalizing components. Next, defend in depth. Avoiding reliance on only one kind of defense. The word depth also means depth of attention. By employing community resources, such as hardened code and vetted designs. Finally, monitor and trace to be able to detect and diagnose breaches. Our principles are strongly influenced by a set of principles put forth way back in 1975. By Jerome Saltzer and Michael Schroeder in their paper, Protection of Information in Computer Systems. It's amazing to see how well these principles have kept up. Despite systems, languages, applications all being different today compared to what they were in 1975. Here are the principles suggested in section one of the Saltzer and Schroder paper. Where those in green are identified as principles and those in blue were considered by the authors as relevant. But imperfect in their application to computer systems. Comparing our principals to the classic ones, there is substantial overlap. Some of the classic principles have been regrouped or renamed and some expanded in scope. We have added monitoring in support of forensics because successful attacks are inevitable. And you need a way to diagnose and improve on past failures. Saltzer and Schroder may have been also concerned about this, but in their paper were more focused on prevention. Finally I have dropped their principle of complete mediation, that states simply that all security relevent actions. Must be mediated if policies on them are to be enforced. I don't think of this as a design principle because all designs that lack it are insecure by definition. In that sense, it is a requirement, not a principle. By comparison, a design could lack all the other principles and still be secure. But achieving security with such a design would be very difficult and would be very hard to maintain. Okay. Next we will dig into these different design principles, organized by category, and starting with favor simplicity.
Design Category- Favor Simplicity
. How do we know that the system is secure? Well, we must understand its behavior in the settings and environments it was designed to operate in. Our first design principle aims to help this understanding. In particular, we want to keep the system as simple as it possibly can be. Ideally, it can be so simple it's obviously correct. The goal of simplicity applies to all aspects of the system, from the external interface to the internal design and to the code. Favoring simplicity is consonant with the classic principal, economy of mechanism. And its goal is to prevent attacks from being successful by avoiding defects in the first place. Security expert Bruce Schneier described the principal of favored simplicity very well when he said we've seen security bugs in almost everything: operating systems, application programs, network hardware and software, and security products themselves. This is a direct result of complexity of these systems. The more complex a system is, the more options it has, the more functionality it has, the more interfaces it has, the more interactions it has, and the harder it is to analyze its security. One way to favor simplicity is to use fail-safe defaults. Some configuration or usage choices affect a system's security, like the length of cryptographic keys or the choice of a password or which inputs are deemed valid. When building a system with security in mind the default choice for these different configurations should be the secure one. For example, the default key length when a user is asked to choose a key length should be the best known secure key length of that time. For example, today 2048-bit RSA keys are what's recommended. Default passwords should not be allowed. Many systems are penetrated because administrators fail to change the default password, that password is published in publicly available manuals and therefore adversaries are able to learn it, guess, and penetrate the system. By instead making it so that it's impossible to run a system with a default password but instead the system must be assigned one by the user. This sort of attack no longer is possible. Also, favoring whitelisting over blacklisting is a more failsafe default. In particular, we list things we know for sure are secure and by default do not trust everything else, as opposed to list the things we know are insecure and assume everything else is secure, which inevitably it isn't. As a recent example of a failure to apply a fail safe default, consider the recent breach of healthcare.gov. In this case it was possible because the server was connected to the internet with a default password still enabled. Another recent incident where a fail-safe default could have played a role was the breach of Home Depot, in which tens of millions of credit card records were eventually stolen. John Pescatore, a cybersecurity expert, suggests that application whitelisting technology would have prevented the attack. Whitelisting is a technology that prevents applications from running unless they are included in the whitelist. Now, while people have complained that white listing is impractical for client-side machines, like user laptops, whitelisting on servers and single function appliances has proven to cause near zero business or IT administration disruption. And therefore, there's no good excuse for not employing it. Another principle that favors simplicity is to not expect expert users. Instead, software designers should consider how the mindset and abilities of the least sophisticated of a system's users might affect that system's security. One clear goal is to favor simple user interfaces to make it easier for users to do the right thing and hard for them to do something that could compromise security. For example, in a user interface, the natural or obvious choice should be the secure choice. Even better, you could avoid choices entirely, if possible, when it comes to security. Another good idea is to not have users make frequent security decisions. Otherwise, they may succumb to user fatigue and just click yes, yes, yes, allowing insecure activities to take place. Finally, it might be a good idea to design interfaces that help users explore the ramifications of their choices. For example, if they set a certain access control policy for accessing their personal information on a social networking site, it would be useful to allow them to see their data from the point of view of other users on the site adhering to the policy. For example, to see how the public might view the data as opposed to a close friend. Another way that users inevitably interact with software systems is by using passwords. These are a very common authentication mechanism, where the goal is for the password to be easy for the true user to remember but hard for an adversary to guess. Unfortunately, this assumption turns out not to be true in many cases. That is, hard to guess passwords are often hard to remember. To work around this problem, users often repeat passwords. That is, they memorize a difficult password for an adversary to guess, but then use that password repeatedly on different sites. Unfortunately, what that means is that if one site's password database is breached, then the adversary could try to use that password to access the same user's account on a different site. Adversaries often employ password cracking tools to attempt to guess passwords on breached password databases. Such databases are often encrypted and so these tools help guess what the encrypted passwords might be. One example is John the Ripper, another, Project Rainbow, and many others. These are the top ten worst passwords of 2013. As you can see, many of them are extremely simple so as to be easy to remember but as a result, not particularly hard to guess. A recent idea for solving the password problem is the password manager. A password manager is a piece of software that stores a database of passwords for a user, indexed by the site that those passwords apply to. This database is encrypted by a single master password that is chosen by the user. This password serves as the key. The password manager is responsible for generating very complicated, hard-to-guess passwords per site. The software then fills in these passwords when the user locally enters their master password. There are several benefits to this approach. First, the user only needs to remember a single password. Second, the user's password at any given site is hard to guess. As such, the compromise of a password at one site does not permit immediate compromise at other sites because the passwords at each site will be different. On the other hand, password managers do not solve the problem of having to protect and remember a strong master password. Another idea for improving passwords is to use a so-called password strength meter. The idea here is to give the user feedback on the strength of the password when they are entering it, possibly by providing a graphical depiction of that password strength. The idea of strength is to measure the guessability of that password. Research shows that password strength meters can work to improve the strength of passwords, but the design of the strength meter must be stringent. For example, it must force unusual characters and not allow users to enter passwords that it would deem to be weak. Now, an even better solution is to not use one or the other of these two ideas but to combine them together. In particular, a password manager makes one security decision that is the choice of the master password, not many. And, the password meter allows users to explore the ramifications of the various choices they are making by visualizing the quality of the password. Moreover, password meters do not permit poor choices because they enforce a minimum score. Phishing is a kind of attack in which a user is tricked into thinking he is dealing with a legitimate site or email, when in fact he is dealing with a fraudulent one that is part of the scam. The user is then tricked into installing malware or performing other actions harmful to himself. We can view phishing as a failure of a site to take its users into account. In particular, it's relying on the user to properly authenticate the remote site. Internet email and web protocols were not designed for remote authentication. While cryptographic solutions are possible, they're hard to deploy. What we would like is to use a hard to fake notion of identity, like public key cryptography. There are many competing systems but no universal solution as yet. If you'd like to learn more about how computers and humans should work together to ensure security, I encourage you to check out Jen Golbeck's course on usable security, which is part of our Specialization in Cybersecurity, hosted by the Maryland Cybersecurity Center. Jen is the director of the Human Computer Interaction Lab and a professor in the College of Information Studies at the University of Maryland.
Design Category- Trust With Reluctance
The next category of secure design principles is trust with reluctance. A system is constructed from many parts. And the security of the whole system thus depends on the security of each part. The parts are trusted to varying degrees. And as such, we can improve the security of the overall system by reducing the amount of trust placed in its various parts. And there are several ways to do this. First, we could use a better design. Second, we could implement functions more safely. For example by using a type safe language. Third, we could avoid assumptions on which we base our trust. For example, we could avoid the assumption that the third party libraries we use are actually correct. And instead we could perform some of our own testing and validation, code review, to ensure correctness and thus establish trust in those libraries. As another example we could avoid designing or implementing security-critical features that require expertise that we don't necessarily have. A key mistake made by many system builders is to attempt to design or implement cryptography from scratch. Something that it turns out is very hard to get right. The principle of trusting with reluctance, when followed, can help prevent security problems, and can mitigate problems that might otherwise arise. One secure design principle that follows the idea of trusting with reluctance is to maintain a small trusted computing base. The trusted computing base comprises the system components that must work in order for the system to behave securely. As an example modern operating systems are often part of the trusted computing base because they enforce security policies like access control policies on files. When you have a multiuser system and your files indicate which users are allowed to read and write the file it is the responsibility of the operating system to enforce those policies. Now we would like to keep the trusted computing base small and simple to reduce its overall susceptibility to compromise. The idea is that a small simple trusted computing base is easy for a human analyst to reason about and argue that it is correct. It should also be easier for an automated tool to establish that the trusted computing base is correct. Unfortunately today operating system kernels, returning to our example, are not small and simple. Instead, they are often millions of lines of code. Usually the reason is that by keeping all of these different disparate components together in the kernel, we can have higher performance. But we increase the attack surface of the kernel and therefore increase the risk of compromise. For example, a buggy device driver could be exploited and thereby give the attacker access to the entire kernel. The kernel's trusted computing base, that is the parts enforcing security policies, could therefore be subverted by the attack, by the device driver. An alternative design is to minimize the size of the kernel so as to reduce the trusted computing base. For example, device drivers could be moved outside of the kernel. And therefore, a compromise of one of these drivers does not compromise the portion of functionality that enforces security policies. This sort of design is advocated by microkernel architectures, which were popular in the 90s, and are regaining popularity today. As another example of a failure to follow the principle of a small, trusted computing base consider security software. This software is obviously part of the trusted computing base because it used by the system to enforce security. In other words, to make sure the system is free of viruses or worms and the like. Unfortunately over time this software has grown in size and complexity and is now has become vulnerable itself and is frequently attacked. This chart, put together by DARPA, shows that six of the vulnerabilities reported in August and July of 2010, were in fact in security software. Now let's consider another principle that follows the idea of trusting with reluctance, and that is the principle of least privilege. This principle states that we should not give a part of the system more privileges than it absolutely needs in order to do it's job. This is similar to the idea of need to know, not telling someone a secret unless they really need to know it, and the motivation is the same. We want to mitigate any affects of compromise. If a component is compromised by an adversary we want its capabilities to be limited so that the adversary's power is also limited. As an example, consider the idea of attenuating delegations to other components in the design of a system. For example, a mail program might delegate to an editor for authoring emails. This editor could be vi or emacs, which are both very powerful editors, and both very desired by users. Unfortunately, these two editors and others permit too much power. For example, they permit escaping to a command shell to run arbitrary programs. This is too much privilege for an email program, because if we gave an untrusted user access to the email program they could author an email and then use the delegated email editor to execute arbitrary shell commands. A better design is to use a restricted editor that only allows the user to perform the functions that are absolutely necessary to author emails. An important lesson here is that trust is transitive. If you trust something, you trust what it trusts. And, of course, that trust could be misplaced. In our previous email client example, the mailer delegates to an arbitrary editor. And that editor permits running arbitrary code by delegating to a shell. Hence, the mailer permits running arbitrary code. This is probably not what we had in mind. Rule that implements the principle of least privilege is input validation. The idea here is to only trust inputs that we have properly validated. That is, we don't want to trust any input. Instead we want to establish trust in that input by making sure that it follows an assumed format. In short, we are trusting a subsystem only under certain circumstances. And we want to validate that those circumstances hold. So we have seen several examples of this so far. We can trust a given function only if the range of its parameters is limited, for example to be within the length of a buffer. Or we could trust input taken from a client form field on a webpage only if it contains no script tags and therefore is not subject to cross-site scripting. We could trust a YAML-encoded string, but only if it contains no code. So by validating the input, we are limiting the influence of that input, and thereby reducing the privilege that we're replacing in the components that process it. Another secure design principle that aims to trust with reluctance is promote privacy. Broadly speaking, a manifestation of this principle is to restrict the flow of sensitive information throughout the system as much as possible. Basically the idea is that only those components that have access to sensitive information can possibly disclose it. And therefore, by limiting access, we reduce the trust in the overall system because we don't have to trust as many components. As a result, we are mitigating the potential downside of an attack because fewer components that could be compromised will be able to disclose that sensitive information. As one example of this principle in action, consider a student admission system that receives sensitive letters of recommendation for those students as PDF files from recommenders. A typical design would allow reviewers of student applications to download recommendation files for viewing onto their local computers. The problem with this design is that compromise of the local computer will then leak private information contained in the PDF files. A better design is to then permit viewing of PDF only in the browser, as opposed to downloading it to the local computer. As a result, if that local computer is compromised, no sensitive data is available for access by the attacker. The last design principle that we'll consider that has the flavor of trust with reluctance is compartmentalization. This design principal suggests that we should isolate system components in a compartment or a sandbox. And thereby reduce the privilege of those components by making certain interactions or operations impossible. In this way we can prevent bad things, that is, those only enabled by the operations that we're not allowing. And we can mitigate the results of an attack by preventing the compromised code from doing anything beyond what the sandbox would allow. As an example, we might disconnect a student records database from the internet and grant access only to local operators. This compartmentalizes the student records database, therefore preventing attacks from the outside. Just as importantly if a local operator attempted to compromise the database and exfiltrate the data it would not be able to do that via the Internet. Another example of a mechanism for compartmentalization is the Seccomp system call in Linux. This system call enables us to build compartments for untrusted code. SecComp was introduced in Linux in 2005. The way that it works is that the affected process can subsequently only perform a limited set of system calls. Read, write, exit, and sigreturn. Notice that there is no support for the open system call. Instead, the compartmentalized process can only use already open file descriptors. SecComp therefore isolates a process by limiting its possible interactions to only these system calls. Follow-on work produced seccomp-bpf, which is more general. In particular, it allows us to limit a process to a policy-specific set of system calls, rather than just the four I just listed. This policy is enforced by the kernel. It's specified by something like Berkeley Packet Filters, or BPF. SecComp BPF is used by Chrome, OpenSSH, vsftpd, which is the very secure ftp daemon, and others. So let's look at how we might isolate Flash player that aims to execute a file that we browse to on the internet. First suppose we browse to a website that has a shockwave file. Which is a program that's going to be run by Flash player. As usual, we'll save this file into the local machine. Next, we'll call fork to create a new process. And that process will open the file. Now this process is still running as Chrome, and so we must exec the process to now run as Flash player instead. Now, before the process can get much further, we call seccomp-bpf to compartmentalize it. Limiting the system calls that it can perform. In particular, we do not allow it to open network connections. And we don't allow it to write files to disc. That way, we can ensure that Flash, if compromised, will be limited in the damage that it can do.
Design Category- Defense in Depth, Monitoring-Traceability
The secure design principle of Defence in Depth aims to increase security through diversity. The idea is not to rely on a single defense or even a single kind of defense. That way if one defense is overcome there is still another one to get passed. This principle can mitigate damage from an attack or can prevent it altogether. An example application of Defense in Depth would be to do all of the following. Use a firewall to prevent access to all network ports other than that of the outward facing web server. Encrypt all sensitive data like employee records when stored at rest, for example on the hard drive. And use a type safe language to write the web server to prevent various classes of attack like buffer overruns. A less diverse defense might rely on one of these, but not all of them and then be vulnerable if the defense was bypassed. One common application of Defense in Depth is to the authentication process. Now, we know that passwords can be chosen poorly and possibly guessed by adversaries, thereby bypassing the intent of authentication. Passwords can also be stolen. Now, one way to defend against a stolen password is to encrypt the password database that keeps those passwords. But to do so would assume that compromise is possible. A company may think that all of their outer defenses, firewalls, and secure software, and fully patched systems should keep adversaries out. But this is probably a bad assumption. And therefore an extra defense of encrypting the password database is a good idea. That's what should have happened for the RockYou attack back in 2009 when RockYou was compromised and their password database was stolen. All of the passwords were stored in plain text exposing all of that information to attackers. Now another sort of Defense in Depth is the use of community resources. The idea here is to depend not only on yourself but to depend on a diversity of people toward solving the security problem. One way to do that is to use hardened code, perhaps from other projects. For example, rather than rolling your own crypto libraries you could use libraries implemented by others, and tested and assured over time. On the other hand, you shouldn't just trust them out of the box. You probably want to test that they will meet your needs and ensure that you're using them properly. Another way to use community resources is to vet designs publicly. This is typically what happens with modern cryptosystems. Instead of trying to keep the algorithm that is used hidden, the designers of the algorithm make it public so others can comment on it and potentially find mistakes. Finally, stay up on recent threats and research. There's a broad security community that is tracking the state of affairs in security today. So NIST is a good place for standards. OWASP, CERT, and Bugtraq have vulnerability reports. SANS Newsbites has a great collection of the latest top threats. And of course, the latest and greatest research in long term trends and technology can be found in academic and industry conferences and journals. Now let's look at a failure to use community resources, and that is using broken crypto implementations. So it turns out that getting crypto right is hard. Not only should you not design your own algorithms, you probably should not implement them yourself because there are many things that you could get wrong. For example, for a long time modern implementations of RSA had a timing channel that could be exploited to steal the entire key through remote experiments. It's also the case that people have used cryptosystems incorrectly. For example, not generating keys with sufficient randomness. The important thing is to use vetted implementations and algorithms, and be sure that you are using them correctly. The final design principle in our list is monitoring and traceability. This principle is an acknowledgement that there is no perfect security and eventually all systems will succumb to an attack. As such, we need to design systems that allow us firstly to discover when an attack has taken place and secondly to figure out how the attack succeeded so that we can try to stop it the next time. A way to satisfy both aims is to have the system log relevant information during it's operation. Such logs will contain things like transactions processed, failed log in attempts, and other unexpected events. The logs produced by a system can then be aggregated with other logs of systems on an enterprise's network. And together when considered, all of these can aid the diagnosis of attacks. Software like splunk can be used for aggregation.
Top Design Flaws
We have looked at a bunch of principles and rules that aim to ensure good design. And we've shown how failure to follow these principles can lead to flaws. Recently, a group of security professionals in industry, research and the government got together and formed the IEEE center for secure design. Their goal was to focus more attention on secure design. To reduce the prevalence of security flaws. One of the first activities of the center has been to dig deep into the collective experience of its members and identify and categorize common and dangerous design flaws. And here is their top ten list. Assume trust rather than explicitly give it or award it. Use an authentication mechanism that can be bypassed or tampered with. Authorize without considering sufficient context. Confuse data and control instructions, and process control instructions from untrusted sources. Fail to validate data explicitly and comprehensively. Fail to use cryptography correctly. Fail to identify sensitive data and how to handle it. Ignore the users. Integrate external components without considering their attack surface. Rigidly constrain future changes to objects and actors. We have considered flaws already in many of these various categories. So now we'll spend a little time considering a few more. The first design failure we'll consider results in authentication bypass. As one example of this, consider clients that are coerced to accept invalid SSL certificates. What's going to happen here, is that the client is failing to properly authenticate the server, because of the poor certificate. Keep in mind that SSL encrypts the connection to prevent eavesdroppers from seeing the communication between a client and a server, but SSL also, by using certificates, ensures that a client is talking to the server it thinks it's talking to. In other words, am I really talking to my bank or a site pretending to be my bank? The certificate tells you that it's the former and not the latter. Now, if an invalid certificate is presented to a client, a web browser should present a warning. Now we might wonder how many users will click through this warning. And that's a completely separate question of useability. However, we can also consider the case when the warning is not even shown. And this happens often times with mobile applications that use SSL behind the scenes. That is, they get an, an invalid certificate and now need to take action. Well, the action they should take is to give the user the opportunity to accept the connection or not, or to take some remedial action. Unfortunately, it turns out, according to research by Fahl et al, that many mobile applications fail to take any action and simply drop invalid certificates. This is because during development those developers often turn off SSL certificate validation, just to make it easier to test their applications. There's an important lesson here. Security is not a feature. We have to test not only what should happen, that you can connect to the correct bank. But, we need to test what should not happen. That is, if a bank provides an invalid certificate, we should not connect to it. Now as another example of a failure due to authentication bypass, consider authentication tokens with long time-outs. These motivate brute-force attempts to steal session cookies. Recall the Twitter auth_token failure that we discussed in the web security unit. On the other hand, making a time out too short, while improving security, will irritate users. So we have to find a balance. In general, a way to avoid authentication bypass is to develop good abuse cases. Consider what happens if we violate the assumption of unique knowledge, like, of a password, or unique possession, for example, of a key fob. How might an adversary learn a password, or spoof a biometric, or steal a session ID? Understanding these things help us builds better defenses. The next design failure we'll consider is bad, or wrong use, of crypto. I'll repeat what I said earlier. Don't roll your own crypto, whether algorithms or implementations. Instead use community resources both on the design and on implementation side to get it right. Another failure is to assume that a cryptoalgorithm that you do use gives you something that it doesn't. Encryption may protect confidentiality, but it does not always protect integrity. For example, if you encrypt something, someone maybe able to tamper with it and fool the party who decrypts it that you've said something that you didn't. Hashing on the flip side protects integrity but not confidentiality. So you could be sure no one tampered with your data but you can't be sure that they haven't seen what it contains. You also need to make sure that you use crypto properly. I've already mentioned once before, generating keys with insufficient randomness. You might also fail to generate keys of sufficient size, making your algorithm, your cyphertext, subject to brute force attacks. You need to also make sure you protect the keys from compromise. For example don't hard code them in binaries that your clients have access to. There's a lot to learn about cryptography, and we won't say much more about it in this course. But if you're interested, I recommend you check out Jonathan Katz's course on cryptography that's also part of the Coursera Cybersecurity specialization being offered by the University of Maryland's Maryland Cyber Security Center. The next design failure we'll consider is ignoring which data in your application is sensitive. Now it should go without saying that you need to think carefully about the sources of data that you're manipulating in your application. Which of these require protection? Obviously things like personally identifiable information or geolocation data due to sensor readings or cryptographic keys require protection. The problem is when you fail to identify data that requires protection and then expose it to general access. One example of this was the failure of the IOS designers in an earlier version of the iPhone back in 2011, to appreciate that geolocation data was being collected in a file called consolidated.db. This file ended up containing all of the locations that the phone went. Now this was not due to some Big Brother goal that, Apple had. Instead, it was just an oversight to remove the file or prevent it being captured on the phone. When considering the data sources that you may need to protect, you want to ask yourself, how are these data sources exposed? Are they exposed when the data is at rest or when in transmission? And what's the threat model? So if they're exposed at rest, you might want to encrypt them. If they're in, exposed in transmission, you might want to use an encrypted con-, connection technology like SSL. One possible failure for authentication is to embed an authentication token in an exposed URL. This fails to appreciate that an adversary could watch network traffic and see that exposed URL travel across the network, and thereby steal the session ID. Finally, we want to consider how data and its exposure changes over time. We can't just look at the application when we deploy. We have to look at each updated version of it to make sure that we continuously keep it secure. The last design failure we'll consider is ignoring the attack surface of external components. The attack surface are the elements of a system that an adversary can attack, or use in an attack. And the question is, when you build a system that involves third party components, how do these components contribute to my overall attack surface? Do they only do what I want and nothing else? Consider the recent shell-shock bug, which is a bug in the bash shell. Bash is often thought of as just a local shell that receives and processes commands. But it turns out to also be a third party component in other servers, like the Apache web server, which it uses to run CGI applications to generate dynamic content. It's also used by OpenSSH and DHCP servers. The failure here is that the bash shell, which is used by these sites, is far more powerful than necessary for these particular tasks. It really shouldn't have been used in the first place. As a result, a failure in bash now leads to a serious vulnerability in many network-facing servers.
Case Study- Very Secure FTP daemon
. We're going to finish off this unit by looking closely at a program I think has been very well designed for security. It's called, appropriately enough, Very Secure FTPD. FTP stands for file transfer protocol. This is a protocol that was particularly popular prior to the rise of the worldwide web, but you can still find FTP servers around today. It is used to allow clients to upload and download files to and from remote servers. In the 90s and early 2000s there seemed to be a never ending parade of compromises to FTP server software. VS FTPD was written in response to this poor state of affairs. It is a very thoughtful design aimed to prevent and mitigate defects. At the same time, it aims to get good performance. For example, it's written in the C programming language rather than a higher level in typesafe language like Java. It's written and maintained by Chris Evans, and as far as I know, there has never been a recorded security breach against it. Let's start by considering the VSFTPD threat model. First we assume that clients are untrusted until they are authenticated. And once they are authenticated they are granted a limited trust. That is trust according to the user's file access control policy. For those files being served by FTP and of course all others should be inaccessible. The possible attack goals of of an adversary are to steal or corrupt resources. For example to download files that shouldn't be downloadable or to upload malware. Remote code injection to take over the server. The circumstances might be that a client attacks the server or that a client attacks another client, trying to snoop information or to corrupt it. All right, now let's look at some of the defenses employed by VSFTPD's design. The first offense is the use of a secure string library. Here on the screen we see the code for implementing all strings that are used by VSFTPD. The first element is the normal, zero-terminated C string. The next element is the actual length of the string. That is, it's what you would get if you'd called strlen. And the first element is well defined. That is, it actually has a zero terminator. The third is the size of the buffer that was returned by malloc. Now of course the length of the string might be far less than the length of the overall buffer. And so we need to track both of those. Particularly when we consider being able to concatenate strings together. To use VSFTPD secure strings, we simply need to replace all occurrences of where we would normally use a care star which is the normal care string. With a stuck, with a struct mystr. And we would replace things like strcpy with the equivalent function str_copy. Here we can see the str_copy function is implemented by a call to a private function private str alloc menchunk. The private function takes the p_dest, this is the mystr of the destination of the copy. And from the source, it takes the original C string buffer p_buf, as well as the declared link that that buff should have. Now, let's look at the code for private_str_alloc_memchunk. We can see that this code implements a number of defensive checks for the protection and security. The first check checks for integer overflow when we account for the added zero terminator that's going to be put on the end. If len plus one is less than len then that implies that it wrapped around and overflowed. Next, we make sure that we have sufficient space in the target buffer per p_str to contain the source. And if not, we free the old buffer and allocate a new one. And then finally, we copy in the source contents up to the declared length and we are sure to add the zero terminator at the end. The next defense that very secure FTPD employs that we'll consider is a secure system call library. And this addresses the common problem of error handling. Libraries often assume that arguments are well-formed. And clients assume that library calls always succeed. For example, consider malloc. What if the argument to malloc is non-positive? We saw earlier that integer overflows can induce this behavior and lead to buffer overruns if malloc accepts, say a zero for its argument. Another question is, what if malloc returns null? Oftentimes, a deference of null will cause a crash, which is safe. But on platforms without memory protection, a dereference can cause corruption and create security problems. The VSFTPD solution to this problem is to wrap system calls and make sure that the error checking happens in the wrapper, so that clients can't forget to do it. Here we see that the size argument is checked. It should not be greater than the maximum integer and it should not be zero. On the other hand we also checked that malloc does not return null. And if so, we kill the program immediately. As such, this routine is going to fail if it receives malformed arguments or runs out of memory. Another defense that VSFTPD uses literally is to minimize privilege. For example untrusted input is always handled by non root processes, thereby minimizing the amount of privilege they have. When root level actions are required. IPC is used by those processes to send a message to a root process to perform the few actions that require root access. During execution processes reduce their privileges as much as possible. Either running as a particular user. For example, the one whose files are being served, or by using the capabilities or the Lennox sitcom system call to reduce the set of system calls that the process can actually use. VSFTPD also minimizes a process' access to files, on the host system, by hiding all directories other than the one that's serving files. All of these things are a principle of least privilege, and minimizing the amount of root code tries to minimize the trusted computing base. So let's look visually at how VSFTPD operates. Here we have a server and a client. The client sends a TCP connection request to the server in an attempt to start an FTP session. Upon receiving this request, the server forks and execs to create a command processor. This command processor then forks again to create a login reader. And the login reader now runs at lower privilege. The client can then send the user and password to the login reader. And the login reader will then send the user and password request to the command processor. Which will, using its root privileges, check whether or not this user and password is in the system's password database. If so, the user is authenticated, and allowed to proceed. At this point, the command processor forks again, and creates a, another reduced privilege command reader and executor, that the client can then interact with. So the client could ask to change directories or to CHOWN a file that is change the ownership of the file. Because CHOWN is a privileged operation this is delegated to the root command processor. Eventually the client completes and logs out. In all of the processes associated with that client are cleaned up. Okay, so given this design, what attacks are possible? Well the first attack we might consider is one at login. Suppose that during the login process, the client tries to inject a string to overrun a buffer. Or perform some other malicious action. Well, in this case the login reader is white listing its input. It's only allowing user password and ignoring everything else. As a result, it limits the attack surface, making it hard for the client to penetrate the login reader. For that matter, the login reader has very limited privilege. It's not running as root. The authentication, in fact, is happening in the separate command processor process. As a result, this mutes the capabilities of any code that could be injected if there was a vulnerability in the login reader. Note that the client is not going to be able to communicate with the command processor directly. This is because of the way the connection is managed. And once again, the command processor, when it interacts with the login reader, accepts very limited input. So it's unlikely the client could fool the login reader into sending something malicious to the command processor. Okay, so let's imagine the client has logged in. What sort of attacks might the client attempt? Well, if they attempt the command reader, again, they're going to be limited in their capabilities. The command reader is not root. Though it does handle most of the commands, these commands are only at a lower level of privilege. Those that do require privilege go back and forth between the command processor and the command reader. If the client attempted to speak to the command processor directly, again that's not going to be allowed, and those commands that are exchanged between the command processor and the command reader are very limited. The last attack that we might consider is one client trying to attack another via the FTP session. So let's imagine how that can happen. The first client connects and creates a command reader or executor. And then the second client does the same thing. Now these two clients have sessions between their respective processes. These session are isolated. They're in separate processes. As a result, there is no way that the command reader can talk to the client that it's not designated for. There are a few other things to say about VSFTPD's design. Interestingly, it has a secure sockets option for encrypted connections, but it's not turned on by default. There's an interesting comment in the release notes for VSFTPD that says it's a massive quantity of code which is essentially parsing complex protocols. As such, it's potentially not trustworthy. And as it turns out the recent heartbleed bug shows that that was in fact the case. Also VSFTPD attempts to reduce its attack surface by trusting as few third party executables as possible. For example, it does not use the standard LS command for directory listings but instead constructs its own directory listings in its own code. The software development life cycles consists of some organization of roughly four phases. In this unit, we have carefully considered how security fits into the first two of these phases, requirements and design. In earlier units, we spent a lot of time considering implementation issues. And various security bugs and how to prevent them or mitigate their affects. Next, we're going to consider the last phase testing and assurance. In particular, we're going to discuss how to use automated code review to find security bugs using techniques like static analysis. We're also going to look at a technology called symbolic execution that forms the core piece of a technology called automated whitebox fuzz testing. This technology is very good at finding certain sorts of bugs. Finally, we'll look at the general practice of penetration testing. Which aims to find security problems in a completed system involving various tools employed by testers with their black hats on.
thanks to a growing number of cyber attacks that are targeting everything from individuals and startups to Fortune 500 companies and entire government agencies.Serious Security CCTV Bayswater
ReplyDeleteIt is a very informative and useful post thanks it is good material to read this post increases my knowledge. Web Security Courses
ReplyDelete