“secure” programming

75
“Secure” Programming Matt Bishop Department of Computer Science University of California, Davis Davis, CA 95616-8562 USA email: [email protected]

Upload: micheal

Post on 25-Feb-2016

49 views

Category:

Documents


2 download

DESCRIPTION

“Secure” Programming. Matt Bishop Department of Computer Science University of California, Davis Davis, CA 95616- 8562 USA email : [email protected]. क्यूंकि मैं हिंदी मे बात नहीं कर सकता, इसलिय यह टॉक इंग्लिश मे है . As I don't speak Hindi, this talk will be in English. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: “Secure” Programming

“Secure” Programming

Matt BishopDepartment of Computer ScienceUniversity of California, DavisDavis, CA 95616-8562 USA

email: [email protected]

Page 2: “Secure” Programming

Matt Bishop, UC Davis Slide 2

As I don't speak Hindi, this talk will be in English

क्यंूकि� मैं हि�ंदी मे बात न�ीं �र स�ता, इसलि�य य� टॉ� इंग्लि��श मे �ै

Page 3: “Secure” Programming

Matt Bishop, UC Davis

Weinberg’s Second Law

Slide 3

If builders built buildings the way programmerswrote programs . . .

then the first woodpecker to come alongwould destroy civilization

Page 4: “Secure” Programming

Matt Bishop, UC Davis

Outline

• Background• Where the problems are• Doing it right• Teaching it

Slide 4

Page 5: “Secure” Programming

Matt Bishop, UC Davis

What Is Robust Programming

• Robust programming, code– A style of programming that prevents abnormal

termination or unexpected actions• Handles bad input gracefully• Detects internal errors and handles them gracefully• On failure, provides information to aid in recovery

or analysis• Fragile programming, code

– Non-robust programming, codeSlide 5

Page 6: “Secure” Programming

Matt Bishop, UC Davis

Robust vs. Secure Programming

• “Secure” program conforms to a security policy– And implicitly requires robustness

• Robust programming does not require such conformance

Slide 6

Page 7: “Secure” Programming

Matt Bishop, UC Davis

Example: Buffer Overflow

• The program has additional privileges, so I use a buffer overflow to escalate my privileges in violation of a security policy (non-secure program)

• The program has no privileges other than mine, so I cannot use it to escalate privileges in a way that violates the security policy (secure program, but not robust one)

Slide 7

Page 8: “Secure” Programming

Matt Bishop, UC Davis

Who Should Care?

• Users of Facebook– Mishandled error condition made it inaccessible for over 2

hours• Users of medical equipment—and patients

– Errors have caused problems ranging from inconvenience to death

• Voters– Electronic voting system software shown to have severe

problems• And many more . . .

Slide 8

Page 9: “Secure” Programming

Matt Bishop, UC Davis

Problem

• We don’t build systems that meet security requirements

• We don’t write software that is robust– Some exceptions in special cases

• Many different models for developing software– Agile, waterfall, rapid prototyping, . . .

Slide 9

Page 10: “Secure” Programming

Matt Bishop, UC Davis

Quality of Code

• Underlying all this is programming– When coding, you make assumptions about

services, systems, input, output– Other components you rely on have bugs or

may act unexpectedly• Hard to have robust, secure software when

the infrastructure isn’t

Slide 10

Page 11: “Secure” Programming

Matt Bishop, UC Davis

Security is Cumulative

• Composing non-secure modules produces non-secure software

• Can ameliorate this with shims to handle non-secure results– Shims provide the security– What if they themselves are written, installed,

etc. non-securely?– What if they can be bypassed?

Slide 11

Page 12: “Secure” Programming

Matt Bishop, UC Davis

More Problems

• Refactor code, use external libraries, modules, services– You inherit their bugs and assumptions!

• Example: RSAREF2 library buffer overflow (1999)– Affected ssh, anything using that library

• Move code into different environment, assumptions may not hold

Slide 12

Page 13: “Secure” Programming

Matt Bishop, UC Davis

Policies and Procedures

• These affect security and robustness• Approach 1: ignore these

– Program will be used in a wide variety of environments

– Need to know in which ones it is safe to do so• Approach 2: take these into consideration

– Focus here is on use of program in particular environment with a certain set of procedures

Slide 13

Page 14: “Secure” Programming

Matt Bishop, UC Davis

Basic Principles

• Paranoia• Assume maximum stupidity• Don’t hand out dangerous implements• “Can’t happen” means it can

Slide 14

Page 15: “Secure” Programming

Matt Bishop, UC Davis

Looking for Problems

• Or . . .

How to Attack via a Program

Slide 15

Page 16: “Secure” Programming

Matt Bishop, UC Davis

What Is Intended?

• Figure out what the problem is– Control access: find out for whom, where,

when, what, why, how• Understand the policy and the practical

limitations– Example: you can’t secure anything from root

on UNIX-style system• This is an iterative process

Slide 16

Page 17: “Secure” Programming

Matt Bishop, UC Davis

Find Assumptions

• Implicit in all security are assumptions– Often about what is trusted

• Attacks based on these– Ask what happens if the assumption is wrong

• If program does something undesirable, continue– Ask how to make assumption wrong– Try it!

Slide 17

Page 18: “Secure” Programming

Matt Bishop, UC Davis

One Good Way to Find These

• Look at manual for programs– “can”, “must”, “should”, “will”, “ought”: don’t do

it– “can’t”, “don’t”, “shouldn’t”, “won’t”, “limit”,

“maximum”: do it– Look for ambiguity or contradictions in the manual,

and see what the program does• Good, accurate manuals tell you many

assumptions the program or system makes!Slide 18

Page 19: “Secure” Programming

Matt Bishop, UC Davis

General Thoughts

• Look at interactions with (internal and external) components– Anything involving user I/O– Anything involving network interactions– Anything involving dependencies

• Cryptography• Access control checking, especially credentials• Cleaning up (or not cleaning up)• Error handling

Slide 19

Page 20: “Secure” Programming

Matt Bishop, UC Davis

Good Places for This

• Network servers– Unknown users can access them

• Local servers– They perform acts normal users cannot

• Anything where privileges or rights are changed– For example, setuid/setgid; changing protection domains

• Shared resources– Privileged and unprivileged users both use these– This includes (local, remote) clients of servers

Slide 20

Page 21: “Secure” Programming

Matt Bishop, UC Davis

Network Servers

• Accessible from throughout the network• Gives access to system

– Attacker may not have access to account on target• Usually has privileges of some kind

– root or daemon; may be only that of ordinary user• But you can usually get whatever you need from any of these

• May make bogus assumptions– Weak authentication (identity from IP address)

• May be poorly written

Slide 21

Page 22: “Secure” Programming

Matt Bishop, UC Davis

Local Servers

• Accessible through system entry point – Usually socket, shared directory, shared files

• Usually has privileges of some kind– root, daemon, or some other system user

• May make bogus assumptions– Determine requester’s identity from ancillary

information (file ownership, etc.)• Initial environment may be poorly configured• May be poorly written

Slide 22

Page 23: “Secure” Programming

Matt Bishop, UC Davis

Privileged Programs

• Execute with privileges other than that of user• Executes in user’s environment

– User’s environment may be incorrectly configured• Usually has privileges of some kind

– root, daemon, or some other system user• May make bogus assumptions

– Determine requester’s identity from ancillary information (file ownership, etc.)

• May be poorly written

Slide 23

Page 24: “Secure” Programming

Matt Bishop, UC Davis

Clients

• Connect to (local or remote) servers• May not check input thoroughly

– Browsers may pass environment information via command strings

– If client is remote, can attack remote system with no other information beyond the server’s existence

• Need not be privileged– Client connects to privileged programs

• May be poorly written

Slide 24

Page 25: “Secure” Programming

Matt Bishop, UC Davis

Cryptography

• Avoid “homebrew” implementations– And (especially) algorithms

• When using a (pseudo-)random number generator, look for the seeding– Process, time of day, etc. easy to guess

• Key management problem– Hard-coded (default) keys a good example

Slide 25

Page 26: “Secure” Programming

Matt Bishop, UC Davis

Access Control Checking

• Race conditions (TOCTTOU, especially)• Mismatch between credentials sent and

expected• Trusting IP address as identity for credential• Assume ports under 1024 are trusted• Differences in interpretation of rights based

on object type (polymorphism in language)

Slide 26

Page 27: “Secure” Programming

Matt Bishop, UC Davis

Cleaning Up

• Core or intermediate files with sensitive data not deleted

• Passwords, crypto keys not erased as soon as possible

• File descriptors not closed when child is spawned• Signals caught by parent not reset when child is

spawned• Environment cleaned up, and not reset

Slide 27

Page 28: “Secure” Programming

Matt Bishop, UC Davis

Error Handling

• Program tries to recover but doesn’t handle some cases properly– Look for improper assumptions when recovery

attempted• Overly helpful error messages

– Classic: “invalid password” (now I know I guessed a user name right)

Slide 28

Page 29: “Secure” Programming

Matt Bishop, UC Davis

Key Ideas

• To know how to write a good program, you need to know how to find problems

• Assumptions are the basis for all security—so look for them!

Slide 29

Page 30: “Secure” Programming

Matt Bishop, UC Davis

Example of Fragile Code

• It’s always fun to pick apart someone else’s code! (Well, it’s mine )

• Library: implement standard queues (LIFO structures)– Written in C, in typical way

• Files– queue.h

• Header file containing QUEUE structure and prototypes– queue.c

• Library functions; compiled and linked into programs

Slide 30

Page 31: “Secure” Programming

Matt Bishop, UC Davis

Queue Structure

• In queue.h:/* the queue structure */typedef struct queue {int *que; /* array of queue elts */int head; /* head index in que */int count; /* number of elts */int size; /* max number of elts */

} QUEUE;

Slide 31

Page 32: “Secure” Programming

Matt Bishop, UC Davis

Interfaces

• In queue.h:– Create, delete queues

void qmanage(QUEUE **, int, int);– Add element to tail of queue

void put_on_queue(QUEUE *, int);– Take element from head of queue

void take_off_queue(QUEUE *, int *);

Slide 32

Page 33: “Secure” Programming

Matt Bishop, UC Davis

How To Mess This Up

• Create queue• Change counter value

QUEUE *xxx;…qmanage(&xxx, 1, 100);xxx->count = 99;

• Now the queue structure says there are 99 elements in queue

Slide 33

Page 34: “Secure” Programming

Matt Bishop, UC Davis

qmanage

/* create or delete a queue * PARAMETERS: QUEUE **qptr pointer to, queue * int flag 1 for create, 0 for delete * int sizemax elements in queue */void qmanage(QUEUE **qptr, int flag, int size){

if (flag){ /* allocate a new queue */*qptr = malloc(sizeof(QUEUE));(*qptr)->head = (*qptr)->count = 0;(*qptr)->que = malloc(size * sizeof(int));(*qptr)->size = size;

} else{ /* delete the current queue */(void) free((*qptr)->que);(void) free(*qptr);

} } Slide 34

Page 35: “Secure” Programming

Matt Bishop, UC Davis

What Can Go Wrong

• . . . within this routine?

• . . . calling this routine?

Slide 35

The first argument’s validity cannot be checkedParameters are not sanity checkedReturn values are not checkedThere is no checking for integer overflow

The order of parameters is easy to confuseThe parameter values have arbitrary meaningsThere is no check that this is an attempt to delete a deleted

(or non-existent) queue

Page 36: “Secure” Programming

Matt Bishop, UC Davis

Adding to a Queue

/* add an element to an existing queue * PARAMETERS: QUEUE *qptr pointer for queue involved * int n element to be appended */void put_on_queue(QUEUE *qptr, int n){

/* add new element to tail of queue */qptr->que[(qptr->head + qptr->count) % qptr->size] = n;qptr->count++;

}

Slide 36

Page 37: “Secure” Programming

Matt Bishop, UC Davis

What Can Go Wrong

• . . . within this routine?The first argument’s validity cannot be checkedqptr may not point to a valid queueThere is no checking for incorrect values in structures or

variablesThere is no check whether the array will overflow

Slide 37

Page 38: “Secure” Programming

Matt Bishop, UC Davis

Taking from a Queue

/* take an element off the front of an existing queue * PARAMETERS: QUEUE *qptr pointer for queue involved * int *n storage for the return element */void take_off_queue(QUEUE *qptr, int *n){

/* return the element at the head of the queue */*n = qptr->que[qptr->head++];qptr->count--;qptr->head %= qptr->size;

}

Slide #38

Page 39: “Secure” Programming

Matt Bishop, UC Davis

What Can Go Wrong

• . . . within this routine?

Slide 39

There is no checking for incorrect values in structures orvariables

The values of qptr and n are not checkedThere is no check whether the array will underflow

Page 40: “Secure” Programming

Matt Bishop, UC Davis

Protecting Your Code

• Doing it . . . – Robustly– “Securely”– Right!

Slide 40

Page 41: “Secure” Programming

Matt Bishop, UC Davis

General Rules

1. Design functions so that the order of elements in the parameter list can be checked

2. Choose meaningful values for the parameters3. Check the sanity of the parameters4. Using pointers (addresses, references) in

parameter lists leads to errors

Slide 41

Page 42: “Secure” Programming

Matt Bishop, UC Davis

Lessons

5. Check that the function’s operations are semantically meaningful

6. Check all return values unless the value returned does not matter

7. Check for overflow and underflow when performing arithmetic operations

8. Provide meaningful and useful error indicators and messages

Slide 42

Page 43: “Secure” Programming

Matt Bishop, UC Davis

Example Program

• login program from UNIX(-like) systems• Clear goals

– Authenticate user as required– change UID of process to that of authenticating

user– update log files– initiate shell

• Security-critical functionalitySlide 43

Page 44: “Secure” Programming

Matt Bishop, UC Davis

Restating the Goals . . .

• Goal 1: only allow authorized user onto the system

• Goal 2: restrict user’s privileges to those allowed to that user

• Goal 3: log information to reconstruct any unauthorized login (break in)

Slide #44

Page 45: “Secure” Programming

Matt Bishop, UC Davis

Environment and Assumptions

• login program accesses correct authentication data– Is it /etc/passwd, Kerberos, or something else?– How do you know it’s up to date?– Does it use environment variables?

• login program sets up correct environment– If not, it should not use environment, or allow

any subprocess to use that environment

Slide #45

Page 46: “Secure” Programming

Matt Bishop, UC Davis

Bad Code

if ((p = getenv(“HOST”)) < 0). . . do something else . . .

if (strcmp(p, “host1”) == 0)authenticate(SKEY);

else if (strcmp(p, “host2”) == 0)authenticate(KERBEROS);

elseauthenticate(PASSWORD_FILE);

Problem is HOST is under user’s control. Using gethostname, which is under system control, eliminates this trust in user.

Slide #46

Page 47: “Secure” Programming

Matt Bishop, UC Davis

More Bad Code

authenticate = YES;while ((o = getopt(argv, argc, “fph:n”)) != EOF){

switch(o){case ‘n’: authenticate = NO; break;…

}

Problem: assumption is that -n flag (to turn off authentication) cannot be invoked by user

Slide #47

Page 48: “Secure” Programming

Matt Bishop, UC Davis

Really Bad Code

if ((fp = popen(“mail staff”, “w”)) != NULL){fprintf(fp, “Send help soon!\n”);fclose(fp);

}

Problems:1) Implicit assumption that PATH variable gets right mail program2) Invocation of shell implies command works as expected (hint:

think “rc file”)3) Implicit assumption that no other variables affect shell’s

interpretation of command (hint: IFS)

Slide #48

Page 49: “Secure” Programming

Matt Bishop, UC Davis

Cutely Bad Code

for(k = 0; environ[k] != NULL; k++)if (strncmp(environ[k], “PATH=“, 5) == 0)break;

if (environ[k] != NULL)environ[k] = “PATH=/bin:/usr/bin:/usr/etc”;

. . . system(“echo hithere | mail bishop”);

Problem: multiple definitions of PATH variable. If shell takes last definition of variable, this won’t force the right mail program to be selected. (To put multiple definitions, write a short C wrapper …)

Slide #49

Page 50: “Secure” Programming

Matt Bishop, UC Davis

Doing It Right

environ = allocate_env_array(NUM_ENV);environ[0] = “PATH=/bin:/usr/bin:/usr/etc”;environ[1] = / * something else */;. . . environ[NUM_ENV-1] = NULL;if (execve(arg_ct, arg_array, environ) < 0)

perror(/* error message prefix here */);

Just create a new environment that you know to be safe!

Slide #50

Page 51: “Secure” Programming

Matt Bishop, UC Davis

Checklist #1

• What will the users/remote servers be supplying?– How can I check it for validity?– What happens if it’s bogus?– What am I assuming about the environment?

Slide #51

Page 52: “Secure” Programming

Matt Bishop, UC Davis

Checklist #2

• What am I assuming about each library function’s actions?– This including side effects!– What assumptions does the library function make?– What information does it obtain from the

environment and remote servers?– Does the library function do what the manual

claims it does?

Slide #52

Page 53: “Secure” Programming

Matt Bishop, UC Davis

Back to the login Program

• Authenticate user• Get user’s authenticator• Get user’s authentication data• Check for match

• Change UID, group info• Get UID, primary GID, secondary GIDs• Change to them

Slide #53

Page 54: “Secure” Programming

Matt Bishop, UC Davis

More Implementation Detail

• Update log files• For each log file, open it, write out info, close it

• Give user appropriate command interpreter• Obtain user’s shell name• Verify it is a valid shell• Spawn it

Slide #54

Page 55: “Secure” Programming

Matt Bishop, UC Davis

Libraries and Such

• Know their assumptions– strcpy assumes second buffer no longer than first– strncpy assumes same or it may omit NUL byte– gets doesn’t check input length– And so on …

• Know their outputs and side effects– malloc(–2048) gives what?– strcpy(a, b, –5) does what?

Slide #55

Page 56: “Secure” Programming

Matt Bishop, UC Davis

Checklist #3

• Have I structured my program appropriately?– Non-security-relevant elements– Security-relevant elements– Modularize security-relevant elements so each

module performs exactly one security-related function

– KISS principle

Slide #56

Page 57: “Secure” Programming

Matt Bishop, UC Davis

Checklist #4

• Have I checked my interfaces?– User/Programmer: no passing pointers or

addresses; use some other structure (like tickets)• May have to in some cases, but try to avoid it

– Are the call/data dependency graphs simple and easy to follow?

– Do I check everything anyway?

Slide #57

Page 58: “Secure” Programming

Matt Bishop, UC Davis

Login Preconditions

• Exporting environment variables preconditions– Don’t export any with string legth over 1024 chars– Don’t export any with empty (or no) value– Don’t export any named in noexport list

• Postcondition– Environment variable placed in list of environment

variables to be made available to the user

Slide #58

Page 59: “Secure” Programming

Matt Bishop, UC Davis

The Fragile Export Function

static int export(const char *s){

p = strchr(s, ‘=’);*p = ‘\0’;(void) setenv(s, p + 1, 1);*p = ‘=’;return(1);

}

Note boundary cases not checked (for example, no “=“ causes problem)

Slide #59

Page 60: “Secure” Programming

Matt Bishop, UC Davis

Check Preconditions

static int export(const char *s) {

char *p; const char **pp; size_t n;/* check precondition 1 */if (strlen(s) > 1024) return(0);/* handle precondition 2 */if (strchr(s, ‘=’) == NULL) return(0);/* handle precondition 3 */for (pp =- noexport; *pp != NULL; pp++){

n = strlen(*pp);if (s[n] == ‘=’ && strncmp(s, *pp, n) == 0)

return(0);

Slide #60

Page 61: “Secure” Programming

Matt Bishop, UC Davis

Check Postconditions

/* now the original code */p = strchr(s, ‘=’);*p = ‘\0’;rv = setenv(s, p + 1, 1);*p = ‘=’;/* check the postcondition */if (rv == -1)

return(0);return(1);

}

Note: just like FreeBSD code, but postcondition not checked!

Slide #61

Page 62: “Secure” Programming

Matt Bishop, UC Davis

Lessons

• Again, know your assumptions!– Check them whenever possible– Examine results when you can’t check inputs

• Don’t expect system calls, library functions to do what they do not claim to do– And read the fine print; that strncpy does not

add a NUL byte if it truncates the copy is implicitly stated, but not explicitly

Slide #62

Page 63: “Secure” Programming

Matt Bishop, UC Davis

Checklist #5

• Is the goal of my program/routine well-defined?

• Did I check my assumptions?• Do I check the values of operators?

– /: be sure denominator isn’t 0– %: be sure both operands are positive– ++: be sure you don’t go off the end of the array

(can detect it, don’t reference through it)Slide #63

Page 64: “Secure” Programming

Matt Bishop, UC Davis

How Do We Teach Secure Programming?

• SESS report has suggestions– http://nob.cs.ucdavis.edu/~bishop/notes/2011-sess/2011-sess.pdf

• Key conclusion: no one sector can improve the state of the practice on its own– “We must all hang together, or we shall all hang

separately” (B. Franklin)

Slide #64

Page 65: “Secure” Programming

Matt Bishop, UC Davis

Questionable Idea #1: TestingStudents’ Knowledge

• Who creates the tests?• Who is being tested?• How do you know that you are testing what

is important (that is, the “right thing”)?• Who determines what is an acceptable

result?• Teaching for the test, not the material

Slide #65

Page 66: “Secure” Programming

Matt Bishop, UC Davis

Questionable Idea #2:Unsupported Mandates

• The support has to come from somewhere– It’s like a zero-sum game

• What do you want to weaken?– If you only have so many resources, something

will have to give– You don’t want to weaken the core foundation

of understanding why certain programming paradigms are critical

Slide #66

Page 67: “Secure” Programming

Matt Bishop, UC Davis

What Can Academia Do?

• Include robustness in evaluation of programs, programming projects

• Create a “secure programming clinic”– Like an English clinic, or a writing clinic for

law schools• Provide supplementary material for

textbooks, classes– These should emphasize robust programming

Slide #67

Page 68: “Secure” Programming

Matt Bishop, UC Davis

What Can Industry Do?

• Key is to do more than say it is important• Make clear that the skills are important for

hiring– Mention their need in job openings– Preference to those with skill in this also helps

Slide #68

Page 69: “Secure” Programming

Matt Bishop, UC Davis

Work With Students and Faculty

• Internships– Students love these; good recruiting tool– Tasks requiring robust programming emphasize

its importance to students• Help teach students

– Review students’ code– Team with colleges in senior/capstone projects

Slide #69

Page 70: “Secure” Programming

Matt Bishop, UC Davis

What Will This Do?

• Increase student demand– If students see it as important, they will ask

about it in class, evaluate programs, faculty in part on it

• Increase your visibility– Good recruiting tools– A corporate “good citizen”

Slide #70

Page 71: “Secure” Programming

Matt Bishop, UC Davis

Government Support

• Act like an industry (see above)• Government can also fund programs• Imperative: target funding towards this

specific purpose– That will require funding to be used for

supporting robust programming– If done as adjunct, it is likely to disappear in the

main purpose of the funding

Slide #71

Page 72: “Secure” Programming

Matt Bishop, UC Davis

Key Point

• Build on existing programs• Understand that academia is a different

environment—completely– Business models don’t work well because the

“end product” is intangible

Slide #72

Page 73: “Secure” Programming

Matt Bishop, UC Davis Slide 73

Conclusion: A Touch of Wisdom

Zymurgy’s Law of Worms

Whenever you try to put worms back intothe can they came from …

you need a bigger can

Page 74: “Secure” Programming

Matt Bishop, UC Davis

Clear Overall GoalsGentlemen, Whilst marching from Portugal to a position which commands the approach to Madrid and the French forces, my officers have been diligently complying with your requests which have been sent by H.M. ship from London to Lisbon and thence by dispatch to our headquarters. We have enumerated our saddles, bridles, tents and tent poles, and all manner of sundry items for which His Majesty's Government holds me accountable. I have dispatched reports on the character, wit, and spleen of every officer. Each item and every farthing has been accounted for, with two regrettable exceptions for which I beg your indulgence. Unfortunately the sum of one shilling and ninepence remains unaccounted for in one infantry battalion's petty cash and there has been a hideous confusion as to the number of jars of raspberry jam issued to one cavalry regiment during a sandstorm in western Spain. This reprehensible carelessness may be related to the pressure of circumstance, since we are war with France, a fact which may come as a bit of a surprise to you gentlemen in Whitehall. This brings me to my present purpose, which is to request elucidation of my instructions from His Majesty's Government so that I may better understand why I am dragging an army over these barren plains. I construe that perforce it must be one of two alternative duties, as given below. I shall pursue either one with the best of my ability, but I cannot do both: 1. To train an army of uniformed British clerks in Spain for the benefit of the accountants and copy-boys in London or perchance: 2. To see to it that the forces of Napoleon are driven out of Spain.

—Duke of Wellington, to the British Foreign Office, London, 1812

Slide 74

Page 75: “Secure” Programming

Matt Bishop, UC Davis

Thank You! Any Questions?

Slide #75

सुनने �े लि�ए धन्यवाद!

�ोई सवा�?