1.3k likes | 1.32k Vues
Cairo University, Faculty of Computers and Information. SE611 – 2018/2019 2 nd Term Secure Software Development Lectures 6 & 7 Defensive Programming. By Dr. Mohamed El-Ramly http://www.acadox.com/class/56820. Requirement. Requirement. Analysis. Analysis. Design. Design. Implement.
E N D
Cairo University, Faculty of Computers and Information SE611 – 2018/2019 2nd TermSecure Software DevelopmentLectures 6 & 7Defensive Programming By Dr. Mohamed El-Ramly http://www.acadox.com/class/56820
Requirement Requirement Analysis Analysis Design Design Implement Implement Testing Testing Integrating Security with SDLC • There are two approaches. Software Security Software Security
Secure Software SDLC • Requirements • Define Security Requirements (what the system should not do) • Develop abuse cases • Architecture • Perform architectural risk analysis • What types of risks, the proposed architecture poses • Threat Modeling
Threat Modeling Questions • What are you building? • What can go wrong? • What should you do about those things that can go wrong? • Did you do a decent job of analysis?
Q2. What can go wrong?STRIDE Spoofing S Attacker pretend to be someone he is not Tampering T Attacker modify something he is not supposed to modify Repudiation R Attacker denies he did something that he actually did Information disclosure I Attacker exposes info he is not authorized to see it Denial of service D Attacker prevents a system from providing its services Elevation of privilege E A program or user is able to do things he is not supposed to do.
Secure Software SDLC • Design • Security-oriented design of the system • Implementation • Applying principles and rules for secure coding (Defensive Programming) • Secure code reviews & automated scanning • Testing • Plan tests to address the risk of attacks. • Penetration testing, looking at the system as an adversary would do.
Best Practices for Secure Design • Try to eliminate security hazards from the design • Identify causes of security hazards and try to reduce their likelihood of occurring through design • Control security hazards once they occur • Design to reduce damage if it occurs
Principles • Least Privilege • Fail-Safe Defaults • Complete Mediation • Economy of Mechanism • Open Design • Separation of Duty • Least Common Mechanism • Psychological Acceptability • Defense in depth • Question assumptions
Lecture Outline • General Practices • C / C++ Language • Java • Specific Tasks Reading • Fedora Security Team Defensive Coding A Guide to Improving Software Security Edition 1 https://docs.fedoraproject.org/en-US/Fedora_Security_Team/1/pdf/Defensive_Coding/Fedora_Security_Team-1-Defensive_Coding-en-US.pdf
Defensive Programming • Is a set of programming techniques intended to ensure the continuing function of a piece of software under unforeseen circumstances. • Defensive programming practices are used to ensure correctness, availability, or security. • Defensive programming improves software quality and reduces bugs. • It makes software behave in a predictable manner despite unexpected inputs or user actions. • It reduces runtime and maintenance costs.
Defensive Driving • Defensive programming can be related to something called Defensive Driving. • In Defensive Driving we assume that everyone around us can potentially and possibly make mistakes. So we have to be careful even to others’ behavior. • The same concept applies to Defensive Programming where us, as developers shouldn’t trust others developers’ code. We shouldn’t trust our code neither. We should not trust user behavior.
Secure Programming • Secure programming is the subset of defensive programming concerned with computer security. • As with defensive programming, avoidingbugs is a primary objective, however the motivation to reduce the attack surface. • The assumption is that the software might be misused actively to reveal bugs, and that bugs could be exploited maliciously. • The goals is to reduce exploitable bugs.
Risky Programming intrisky_programming(char*input){ char str[1000+1]; // +1 for the null // ... strcpy(str, input); // Copy input // no boundary check // ... }
Secure Defensive Programming intsecure_programming(char*input){ char str[1000+1]; // +1 for the null // ... strncpy(str, input, sizeof(str))); // Copy within boundary str[sizeof(str) -1] ='\0'; // Terminate string with null // ... }
Fedora Security Team: Defensive Coding A Guide to Improving Software Security • This document provides guidelines for improving software security through secure coding. • It covers common programming languages and libraries, and focuses on concrete recommendations.
I. General Practices • Input Validation • Encoding & Canonicalization • Sanitization • Error Handling • …
1. Input Validation • Verification of the data provided and ensuring it is safe and free of hazards. • Validate input from all untrustedsources. • Proper input validation can remove the vast majority of software vulnerabilities. • Be suspicious of external data sources • command line arguments, • network interfaces, • environmental variables, and • user controlled files.
Input Validation • To ensure input datameets assumptions. • e.g. is printable, HTML, email, userid etc • Compare to what is known acceptable. • Use regular expressions • To reject or alter bad input
Input Validation • Validate numeric data. • Internally stored in fixed sized value, e.g. 8, 16, 32, 64-bit integers or 32, 64, 96 float • signed or unsigned • Must correctly interpret text form and then process consistently • Issues comparing signed to unsigned • e.g. large positive unsigned is negative signed • could be used to thwart buffer overflow check
Why Input Validation? • Lack of validation gives the attackers a chance to run different attacks. • Attacks can insert malformed input that • Crash the system • Leak data • Expose database • Possible attacks • Integer overflow • Injection • Invalid input format
2. Encoding & Canonicalization • Canonicalization: is the process of converting data that holds more than one possible representation to conform to a standard canonical form. • Ensure that SQL queries containing user-controllable input are encoded correctly to prevent single quote or other characters from altering query
Encoding & Canonicalization • Multiple representation of single characters needs to be brought into account. • Use whitelist input validation and reject non canonical forms of input • Validatefilters should be done afterinput is in canonical form.
3. Sanatization • Sanitization: is the process of changing something that is considered dangerous into its innocuous form. • Sanitize all data given to complex subsystems such as command shells, relational databases, and commercial off-the-shelf (COTS) parts.
Sanatization • Attackers may be able to invoke unused functionality in these ingredients through the use of SQL, command, or other injection attacks. • The complex subsystem being invoked does not understand the context in which the call is made, hence it would not validate the input. • The calling process understands the context, it is responsible for sanitizing the data before invoking the subsystem.
4. Error Handling • Murphy's Law “Anything that can go wrong will go wrong“ • Error conditions will happen, and your code needs to deal with them: • Out of memory, disk full, file missing, file corrupted, network error, …
Error Handling • Software should be tested to find out how it does under various error conditions • Simulate errors and see what happens • Simply because your program works on your computer doesn't mean that it will go everywhere else • Many weird things will go wrong when your software is utilized out in the "wild"
Error Handling • Use informative but non-verbose error messages. • How to Write Good Error Messages – UX Planet https://uxplanet.org/how-to-write-good-error-messages-858e4551cd4
Error Handling • Use an index of the value or reference map. • Best practice: redirect errors and exceptions to a custom and default error handling location and depending on the context of where the user has logged in (remote or local), appropriate message details can be displayed.
Lecture Outline • General Practices • C / C++ Language • Java • Specific Tasks Reading • Fedora Security Team Defensive Coding A Guide to Improving Software Security Edition 1 https://docs.fedoraproject.org/en-US/Fedora_Security_Team/1/pdf/Defensive_Coding/Fedora_Security_Team-1-Defensive_Coding-en-US.pdf
II. C / C++ – Core Language • Undefined behaviours • Standard does not describe what happens when the construct is executed • Examples • out-of-bounds array accesses • null pointer dereferences • overflow in signed integer arithmetic • Misuse of global variables • C provides no memory safety.
Out-of-bounds Array Access // Program to demonstrate // accessing array out of bounds #include <stdio.h> int main() { int arr[] = {1,2,3,4,5}; printf("arr [0] is %d\n", arr[0]); // arr[10] is out of bound printf("arr[10] is %d\n", arr[10]); return 0; }
Null Pointer Dereferencing • Why this is undefined ? • Because it will incur overhead to check every time if pointer is null or no. Compiler will have to inject this code. • Or, PDP-11 machines on which C was developed, always had 0 at location 0 and this was not a problem.
Null Pointer Dereferencing // Program to demonstrate // null pointer dereferencing #include <stdio.h> int main() { int *arr; printf("*arr is %d\n", *arr); return 0; } Crash
Overflow in Signed Integer // Program to demonstrate signed int overflow #include <stdio.h> #include <limits.h> int main() { int num = INT_MAX; printf("num is %d\n", num); num++; printf("num++ is %d\n", num); return 0; }
Recommendations for pointers and array handling • Keep track of the size of the array you are working with. • Keep a pointer past the last element of the array, and calculate the number of remainingelements by substracting the current position from that pointer. Better • Update a separate variable every time when the position is advanced, is usually less obviously correct. • Use C++ vectors or valarray.
Recommendations for pointers and array handling • Use C++ smart pointers: • shared_ptr • weak_ptr • unique_ptr
Basic Problem Resource sharing is often more efficient than copying But it’s hard to tell when all are done using a resource Must avoid early deletion Must avoid leaks Solution Approach Share both the resource and also a counter Each new reference increments the counter When a reference is done, it decrements the counter When count drops to zero, delete resource and counter “last one out shuts off the lights” shared_ptr reference reference Resource reference counter == 3
shared_ptr // shared_ptr constructor example #include <iostream> #include <memory> int main () { std::shared_ptr<int> p1; std::shared_ptr<int> p2 (new int); std::shared_ptr<int> p3 (new int, std::default_delete<int>()); std::shared_ptr<int> p4 (p2); std::cout << "use_count:\n"; std::cout << "p1: " << p1.use_count() << '\n'; std::cout << "p2: " << p2.use_count() << '\n'; std::cout << "p3: " << p3.use_count() << '\n'; std::cout << "p4: " << p4.use_count() << '\n'; return 0; }
Recommendations for integer arithmetic • Overflow in signed integer arithmetic is undefined. • This means that it is not possible to check for overflow after it happened or it can causedamage it happens. • We need approaches to check for overflow without causing it.
Integer arithmetic overflow example #include <stdio.h> #include <limits.h> int main() { int num = INT_MAX; printf("num is %d\n", num); printf("num*2 is %d\n", num*2); printf("num*3 is %d\n", num*3); printf("num*4 is %d\n", num*4); printf("num*5 is %d\n", num*5); printf("num*6 is %d\n", num*6); return 0; }
Recommendations for integer arithmetic • Use a wider type for calculation, check that result is within bounds & convert to original type. What is the drawback ? • All intermediate results must be checked as well • Addition of unsigned types • Perform the calculation in the corresponding unsigned type and check result to detect the overflow. • For three or more terms, all the intermediate additions have to be checked in this way.
Recommendations for integer arithmetic void report_overflow(void); Unsigned add_unsigned(unsigned a, unsigned b) { unsigned sum = a + b; if (sum < a) { // or sum < b report_overflow(); } return sum; }