You are expected to understand this. CS 111 Operating Systems Principles, Fall 2006
You are here: CS111: [[2006fall:notes:lec18]]
 
 
 

Lecture 18 - Computer Security

By Aditya Advani, Eric Chang, Jonathan Harms, Albert Liu

What is Computer Security?

  • Computer Security : protecting a computer system against adversaries
  • Principal : representation of a user inside a computer system.

Mapping is not one to one. People can own more than 1 principal, and a principal can be owned by more than 1 person. The most common principal for computers are usernames.
Another example:

 a credit card : 
  * A person can have more than 1 credit cards ( 1 person multiple principals )
  * 1 credit card can be used by more than 1 person ( 1 principle multiple people )

Goal: Secure System Principles

A computer system should perform exactly the legitimate actions requested by the legitimate principals

* Positive: If Legitimate user requires legitimate actions, system should do it.
* Negative: Computer does not perform other actions.

  for example: **2 trivially secure system:**
    * Computer is off => satisfy negative, not positive.
    * Do anything that anyone asks => satisfy Positive, not Negative.

Basically, it's a tradeoff. It's impossible to meet both!

  If you deny more requests => less positive
  However, there's a higher possibility of getting a Denial of Service attack!

How can you tell if request is legitimate?

3 subproblems to solve:

  1. Authenticity: Is principal's identity authentic? Is their identity really who they are?
  2. Integrity: Is the request what the principal actually sent? Has it been tampered with?
  3. Authorization: Does the computer allow this request? (for example, file permission problems)

First 2 (Authenticity and Integrity) are more related to Authentication problem.
Last one (Authorization) is more of a Policy problem. --- Albert Liu 2006/12/09 01:04

Complete Mediation

In order to prevent mistakenly giving elevated permissions to a principal, a system must ensure that every request is authenticated and authorized. It is particularly important to authenticate and authorize every time a principal requests an object and not only the first time because access rights may chance after time. A common example of systems that tries to provide complete mediation is firewalls.

The following figure from page 375 of our textbook depicts the security model for complete mediation:

complete_mediation.jpg

Trusted Computing Base (TCB)

The Trusted Computing Base is the portion of the system that must be correct to preserve security. Almost any bug in the TCB consequently results in a security issue. Therefore, the TCB must be minimized because simpler means less bugs! For example, 5 lines of code is less likely to have as many bugs as 5,000,000 lines of code.

So what exactly is inside the TCB? First, lets look at an example of something that’s not in the TCB. Remember that one important goal for building operating systems was process memory isolation. Right away we can conclude that process code cannot be in the TCB otherwise process memory isolation would be violated.

The following belong in the TCB:

  1. Kernel
  2. Processor
  3. Compiler
  4. Physical Memory (DRAM)
  5. Network Controllers and I/O Devices
  6. Hardware

Programs rely on or “trust” all of the above to be correctly implemented. Regardless of how well a program is written, it can only be as secure as the TCB. Once attackers compromise any component of the TCB—whether they exploit a bug in the kernel or even physically gain access to the hardware—we can assume the security of every program has also been compromised.

Reflections on Trusting Trust

This is an actual story that happened. Ken Thompson, who created UNIX along with Dennis Ritchie, devised a way to get root login access on every UNIX machine:

Basically 3 main steps:

  1. Add code in (login function login.c) to log him in as root
  2. Add code in C compiler to insert code from (1) to login.c everytime u create the login.c
  3. Add code in compiler to add both bugs (1 & 2) to the compiler everytime u create the compiler

(steps taken from fall05 notes)
Here's the idea:

If you have the follow code:

  char v[] = {
     "int main() {
       ##$%#%^#$^&$#%#@@#%
     }"
  }
  int main(int c, char *v){
    printf("char v[] = {\n");
    printf("%s", v);
    printf("\", %s, u);
  }

This is a self generating code

Step 1 : Add code in (login function login.c) to log him in as root in login.c add (in pesudocode)

        "if (username == "KT" and password = "haha")
              allow_login();"

Now whenever someone compiles his login function, he can login using his username and password (pattern 1 = login function) However, if anyone would look in the login.c, they could rid the bug.

Step 2 : Add code in C compiler to insert code from (1) to login.c everytime u create the login.c in compiler

  compiler () {
    .....
     if (input code matches "login.c"){
         generate extra code:
            "if (username == "KT" and password = "haha")
              allow_login();"
     }
    .....
   }

Now whenever someone compiles his login.c, their login program will generate the bug. However, if anyone would look in the compiler code, they would still find the bug.

Step 3 :Add code in compiler to add both bugs (1 & 2) to the compiler every time you create the compiler in compiler

  compiler () {
    .....
     if (input code matches "login.c"){
         generate extra code for bug 1;      
     }
     if (input code matches "compiler"){
         generate extra code for bug 2;
     }
    .....
   }    

Now his tracks are completely covered.

Moral of the story: Don't Trust Code not written by yourself! (for more information, look here:)

--- Albert Liu 2006/12/09 01:04

Authentication Mechanisms

A good example of a simple authentication mechanism is password authentication. Of course, password authentication makes a basic assumption that no one knows anyone else's password. Basically, any entity that knows a principal's password is authenticated as that principal.

Want: Given a principal p, it should be hard to find pass(p) where pass() denotes a principal's password.

Bad Ideas

  1. Dictionary Words
  2. Username
  3. Anything easy to guess
    1. Name, birthday, "password" etc...

Problem: Given principals p1 and p2, a world-readable password file makes it easy for p1 to get p2's password.

Furthermore, anytime a password is sent in the clear, anyone listening can read that password. When a password is "in the clear," it means that the password is unencrypted (i.e. it's in plain-text). In addition, timing information can expose information about a password when keystrokes are sent immediately over a connection.

lec18b_1.gif

lec18b_2.gif

David Wagner, a professor at UC Berkeley, performed work relating to how timing information can provide hints about a password. The above image shows the transfer of the password "HELLO" when each keystroke is sent immediately. Notice that the time between the two 'L's (the delta) is shorter than the delta between other keystrokes. Also notice that 6 packets are sent for the 5 character password.

  • Number of characters in password = Number of packets sent -1
  • Short delta ---> Likely a double letter

Cryptographic Primitives

Cryptographic Hash: function h() with the following properties:

  1. Given h() and message m, it's easy to compute h(m). In other words, it's easy to run the hash function.
  2. Given h() and h(m), it's hard to compute m.
  3. Given h() and h(m), it's hard to find another message m' where mm' such that h(m)=h(m'). In other words, it's hard to find a hash collision
  4. Given h(), it's hard to find two messages m and m' that hash to the same value where mm'. In other words, it's hard to find ANY collision.

What does "hard" mean?

  • No known algorithm does better than a brute force exhaustive search

For an n-bit hash (meaning h(m) is n bits long), brute force takes, on average, 2^(n-1) tries to find an m' where h(m)=h(m'). It also takes, on average, 2^(n/2) tries to find m and m' such that h(m)=h(m').

What all this means is that in order for a cryptographic hash to be "hard," there must be no know algorithm to either:

  • Find m' in less that 2^(n-1) tries on average, such that h(m)=h(m') when given both h() and m
  • Find m and m' in less that 2^(n/2) tries on average, such that h(m)=h(m') when given only h()

An example of a password authentication process using a cryptographic hash

  1. A world-readable password file stores h(pass(p))
  2. Login process reads password, calculates h(password) and compates it with the password file
  3. If there's a match, the user is authenticated

Some cryptographic hashes

  • MD5
    • 128 bit hash
    • Not hard because requirement 4 has been broken
  • SHA-1
    • 160 bit hash
    • 2^69 attach, so technically not hard, but not too bad either
  • SHA-256
    • 256 bit hash
    • still hard

Message Authentication

Message Authentication: Allowing a user to login over a non-trusted channel. In other words, the user connects over a network.

lec18b_3.gif

Problems: Others could theoretically

  1. Eavesdrop
  2. Change contents
  3. Drop messages
  4. Add messages

In other words, networks are ALWAYS considered INSECURE! In contrast, there is a general assumption that the keyboard physically connected to a workstation is safe (although this may not always be true). In 2004, thieves used custom keylogging software in a nearly successful attempt to heist $400 million from the London branch of Japanese bank Sumitomo Mitsui. More on the heist attempt here: and here:.

Replay Attack A replay attack is when someone observes a login message sent over a connection and simply resends that message to login himself.

lec18b_4.gif

The Wikipedia article on authentication: includes links to a number of commonly used secure authentication methods.

Encryption

Since networks should always be considered insecure, messages should not be sent in the clear. Instead, messages should be sent over a network using some sort of code; that's where encryption comes in.

For a encryption function e() and keys k and k-1

  1. Given e(), k and a message m , it's easy to compute e(m,k).
    • m is the plaintext and e(m,k) is the cipher text
  2. Given e() and e(m,k) it is hard to comput m, k or k-1
    • it's hard to decipher a message without the key
  3. Given e(), e(m,k) and k-1, it's easy to compute m
    • m = d(e(m,k),k-1) where d() is a decryption function
      • Often, e() and d() are the same
  4. [Public Key only] Given e(), k, m and e(m,k), it's hard to find k-1
Secret-Key (Symmetric) Public-Key (Asymmetric)
Both k and k-1 must be kept secret k can be made public but k-1 is kept secret
Usually k=k-1 Usually k and k-1 are the same length
Usually fast Usually slow

Public-Key encryption relies on trapdoor functions.

Trapdoor function: Given e(), m and k, it's easy to compute e(m,k)

  • Everyone knows k
  • Everyone can encrypt

Prime factorization is an example of public-key encryption.

  • Given a set of factors, it's easy to multiply them.
  • Given a product, no one knows an efficient algorithm for factoring.

Encryption is often not enough to assure security. Security expert Bruce Schneier has a short essay on the importance of security engineering and the "Risks of Relying on Cryptography":.

--- Jonathan Harms 2006/12/11 00:16 --- Aditya Advani 2006/12/11 18:21

 
2006fall/notes/lec18.txt · Last modified: 2007/09/28 00:25 (external edit)
 
Recent changes RSS feed Driven by DokuWiki