Monday, August 31, 2015

Software Security - Week 4 - Notes

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.       

2 comments:

  1. 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

    ReplyDelete
  2. It is a very informative and useful post thanks it is good material to read this post increases my knowledge. Web Security Courses

    ReplyDelete