Securing my software

Security goals

Application security is important. Security breaches expose the user to harm or future attacks and expose companies to brand damage and revenue loss. My objective of application security is to prevent unauthorized uses or actions regarding data or systems. I also want to prevent undesirable effects of any actions in hardware, software, networks and services.

Reasons for application security
Threat climate
Many risks and threats actually exist
Consequences of inaction
Security breaches, loss of revenue, loss of productivity, legal liability, and loss of customer trust
Laws, industry regulations, company policies, and contractual obligations
Business need
Good security makes good business sense

Firewalls and encryption are not a panacea for security. SSL doesn't prevent all application layer attacks and encryption can hide attacks.

Security is all about the software and its environment, and managing risks. I think like a bad guy, and make tradeoffs which maximize security. To be proactive about security and save costs, I build software right in the first place.

Project goals are not universal
Security vs. Software vs. Market
Not do what bad guys want it to do
or Quality
Do what it is supposed to do
or Improvement
Have an agile process
or Software efficiency or Cost efficiency
Input validation
Exception handling
or Flexibility or Error reporting
or Innovation or Schedule
Session management
Multi-level security
or Availability
or Training users
or Communication
or Supporting users

What about Simplicity?
Less lines of code... so Less bugs... so Less time debugging...
Simplicity aligns to all quality metrics

Security is absurd in the absolute sense.

Penetrate and patch does not work

It's the dirtiest little secret in the software industry.
Scott Berinato CSO Magazine .

People don't patch when stability is a concern. Patches are attack maps for software exploit. Scripting in automated attack tools happens after patch release. Decline of scripted attacks occurs because it is no longer cool or users were forced to update.

Why is security so hard?

Virtually all software is on the net.
Networked, distributed and mobile code is hard. Simple interfaces make for complex software.
Systems evolve and change on the fly — Unexpected changes hinder security by design.
Software engineers do not believe it is their job. Network security experts do not understand software development. Need engineers to have and maintain security knowledge. Need security organizations to have and maintain software development knowledge.

Building blocks for software security

Technology choices really matter. Important choices include the following list in approximate order of importance.

  1. Language and libraries
  2. Operating System
  3. Data systems (CORBA, DCOM, JavaBeans)
  4. (Multi-factor) Authentication
  5. Network

Design flaws and pitfalls

At the most basic level, I can fix bugs in security one line at at time.

Code quality
Error handling
Lack of consideration for abuse cases can make any program insecure. An attacker may attempt to cause an exception which does not fail safely or locate a backdoor which was not removed before release.
Credentials management
Cryptographic issues
The cryptography library may open holes in security as with any other library, but is often higher risk given the level of scrutiny subjected by attackers. I must zero out password strings in main memory, but this may be optimized away by compilers. In some languages (Python), strings are immutable so zeroing operations are (nearly) impossible if the program loaded the password as a string.
Insufficient input validation
Cross-site Scripting (XSS)
Adding characters into a variable which creates a string without escaping.
CRLF injection
Untrusted input with line endings (CRLF) is passed into a protocol (http) that relies upon line endings.
SQL injection
Untrusted input is added to an SQL query which allows attackers to execute any SQL commands.
javascript injection
Untrusted input allows attackers to add javascript listeners to another website to spy or modify form submission or client-server interaction.
OS command injection
Untrusted input is added to a system call enabling attackers to execute their own system commands.
API abuse
Information leakage
For instance, java may serialize classes for an attacker if not explicitly declared to prevent it.
Insufficient encapsulation
Keep private variables private!
Buffer management errors
Buffer overflow
Can be single stage attacks
  • Heap allocated buffers
  • Stack allocated buffers
  • Smashing the stack (most common)
Race condition
Takes advantage of time of check to time of use (TOCTOU). Two or more processes might access a shared resource in unexpected order.
  • Protect signal handlers
    • Only use re-entrant functions if possible.
    • Have a global variable to protect re-entry if nothing else.
    • Block signal delivery in signal handlers if possible.
  • Protect file handling
    • Step on a file
    • Always use library functions to create temp files
    • Avoid writing temp files or config files to world writeable directories such as /tmp
    • Always use full path names to files.
    • Use file descriptors instead of file names.
    • Set umask 077 on unix systems
    • Use explicit file permissions on all files.
Session fixation
An attacker hijacks a(nother) user's session
Directory traversal
Untrusted search path
Unsafe environment variables
Unsafe functions
Unsafe system calls System()
Client trust
Parameter manipulation can happen at the browser to modify hidden parameters in form fields (such as change price to $1.00). Instead of exposing parameters to modification, use session identifiers to reference state stored on the server side, or encrypt the hash with a secret key known only on the server side (HMAC).

Architectural flaws expose additional security problems in software.

  • Misuse of cryptography
  • Compartmentalization problems
  • Privileged block protection failure DoPrivilege()
  • Type safety errors
  • Insecure auditing
  • Broken or illogical access control (RBAC over tiers)
  • Method overriding (subclass issues)
  • Signing too much code
  • Asking for too much permissions
  • Format string vulnerabilities
    • printf(str)printf("%s",str)
      Important for any functions that require a format string and have untrusted input.
    • getsfgets
    • strcpystrncpy
    • strcatstrncat
    • sprintfsnprintf
    • scanf → implement my own parsing
  • Java has architecture problems
    • Does not separate namespaces from classes
    • Serialization — Can become an untrusted input problem because who's going to change the bits when I'm not looking?
    • Don't depend on initialization
    • Limit access to entities
    • Make everything final
    • Don't depend on package scope
    • Don't use inner classes
    • Avoid signing my code
    • Put all signed code in one archive
    • Make classes uncloneable
    • Make classes unserializable (private final void writeObject)
    • Make classes undeserializable (private final void readObject)
    • Don't compare classes by name
    • Don't store secrets

Threat analysis

I identify and secure the weakest link. I focus risk reduction effort on highest risk areas to maximize return on investment (ROI).

A guy who lives on a park bench is trying to get some gold to a guy living on a steam grate. He hires a Brinks security truck. Where would I steal the gold?

Door locks aren't home security. Kick in the door. Drill the lock. Break a window. Pick the lock. Don't forget high level view.

Another example is a tall electric fence with a short picket fence gate in one section.

  • Risk = Risk Contributors - Risk Reducers
  • Risk contributors
    • + sensitive information (confidentiality, privacy, PCI standards, ...)
    • + component criticality (availability, integrity, ...)
    • + trust deltas (for example, exterior doors have deadbolts, interior doors have trivially defeatable locks)
    • + factors which increase likelihood of attack or odds that a customer doesn't know they're accepting risk (for example, products that are actively accepting connections by default)
  • Risk reducers
    • - mitigations which defend against high or medium risk threats (for example, signed packets which defend against network spoofing)

Design principles

Designing software for security is critical! Besides thinking like a software architect, I also need to spend time thinking like a bad guy. I think like a hacker and try to break my own system.

Multi-level security

Use multiple strategies to enable and practice defense in depth. Locks work better if I have alarms on them. Adding motion sensors helps more.

Redundant subsystems can be as secure as the STRONGEST link.

Fail and recover securely because attackers break software on purpose!

Whenever possible, leverage existing sign on and authentication mechanism or credential systems available to the user.

Have secure defaults. Do not have default passwords — it is better to prompt for the initial password. Default to insecure is a bad practice. Better for customers to choose more features as needed than for the software to generally enable additional attack vectors in features the customer may not use.

Always fail securely. Use exception handling throughout the code base. I cannot predict every exception before it occurs, so I must have graceful but generic exception handling. Catch errors before they bubble up in a way that leaks too much information, and instead return generic harmless error messages to the client. For example, I can put a SecureZeroMemory call and other important cleanup code into a finally block so that it is never skipped by an exception.

Authentication, Authorization and Auditing

Authentication and authorization should not be equated. Authentication establishes identity, while authorization establishes permission based upon identity. Auditing establishes important actions taken on the system. For individual accountability, I do not allow shared accounts.

Authentication may be formulated from several factors. Multi-factor authentication often results in improved security. Factors of authentication include:

What the user's system has
Keys and certificates on laptop or mobile device.
What the user has
Oath token, smart card, and digital badge
What the user knows
Password systems, and systems to reset access
Who the user is
Biometrics, and badged access
Other secure systems to which the user authenticates
SSO implementation, SMS reset, and email reset

After the user authenticates, session management maintains access for the user until the session is terminated. If the session token can be guessed or snooped, it can be spoofed by an attacker. How to protect the session:

  • Use SSL to create a secure communication channel
  • For cookies, set the secure flag to true
  • Ensure the session tokens are not easily guessed
  • Allow the user to logout to invalidate the session
  • Allow the session to timeout to invalidate the session

Once the user's session is active, control the user's access according to the principle of least privilege.

  • Does the user need this type of access to do their job?
  • Ensure default ACLs don't give too much access to the user.
  • Check job roles before granting access that could expose sensitive data.
  • Audit authorization lists and permissions periodically.

I am reluctant and I keep trust to myself!

I am careful who I trust. I am careful what I trust. Trust is transitive in nature while intransitive in need.

The client shouldn't trust the server. The server shouldn't trust the client. In a client-server model, maintain trust barriers.

I follow the principle of least and necessary privilege. I give out no more privilege than necessary for shortest possible time. Bad things happen when programs have too much privilege. Giving up privilege is hard so I don't grant it in the first place. I do not run commands as root — instead I use /etc/logingroup.

I keep trust to myself. I do not give out more information than I must.

I trust no one. I question all assumptions and choices. I look for words like "ASSUME" in the code. Compilers can optimize out secure implementation choices.

I will do it wrong sometimes, so never trust myself and get someone to help me. I need other developers to code review and write unit tests. In security critical situations, I seek outside help.

Hiding secrets is harder than it seems. I don't have much control over the client or final program environment. Security by obscurity doesn't work.

Hackers launch attacks based upon easily collected information. Providing error messages can sometimes give away way too much information. Hackers can use error reporting with untrusted input risks to explore. Malicious hackers use this information to further the attack.

All input should be treated as malicious until proven otherwise. Perform data validation and authorization checks at point of input and at point of action. Do not accept commands from the user without validation. An attacker may inject special characters, quoting and special commands so encode output that includes user input and protect all inputs to query strings such as SQL.

XSS attacks come from anyone who would like to collect customer information from a legitimate site. If I found a site vulnerable to script injection, I might be able to perform a phishing attack as follows.

  • I obtain a legitimate email address from a newsletter email
  • I modify the e-mail source to make an attractive deal that looks like an existing deal
  • I embed the XSS code to point towards a javascript file on another site
  • I create the javascript file to overwrite the page with legitimate looking e-commerce pages
  • I modify the form posts via javascript to send to a secure site under my control
  • I obtain any number of email addresses for at most a few dollars
  • I spam the list via BCC or setup my own listserv
  • I send the message and wait... maybe 1% will follow through with the fake offer.

Misinformation may help. I implement proxy firewalls, honey pots, and intrusion detection.

I don't store passwords or private keys in code. Temporary files, trace reports, and log files leak information. Binaries are readable. Secrets can often be inferred from behavior. Reverse engineering is not hard.

Encryption and security software

I don't trust security claims. Security features do not guarantee security.

Protocols are broken. The network is assumed to be insecure so authentication must be encrypted. Of no less importance, the user must have appropriate assurances of the identity of the site before attempting to authenticate.

Encryption is intended to prevent the unauthorized disclosure or alteration of sensitive data. This purpose depends on the protection of the key. Encryption keys are not the same as passwords.

I don't bundle private copies of security libraries unnecessarily. Security libraries will have security bulletins issued against it. Were I to bundle this code, it might necessitate frequent imminent reaction on my part. If I bundle security code I may be forced to maintain and repeat those security bulletins and patches to customers.

I use encryption when needed. I don't invent my own encryption. Encryption may trigger import and export controls.

  • I encrypt authentication and session tokens.
  • I encrypt data across network links.
  • I encrypt on shared media devices.
  • I encrypt serializable data or treat as untrusted input.

Keep it Stupidly Simple

In contrast to defense in depth, sometimes the simplest approach is simply the best and most secure. Consider that users need simplicity anyway. Complex code tends to have more bugs more often.

I provide security choke points without back doors. I track all back doors during development. Back doors during development trade security for efficiency.

I compartmentalize to limit what can be done if security is broken.

I have fewer links or levels of indirection to make a system easier to secure, understand, and maintain.

Subtle API changes to my environment may wreak havoc. Semantic differences in system calls and software environments cause problems.

Product lifecycle

Consider security at all points in the product lifecycle (PLC). Earlier emphasis has greater impact on security and exponential impact on cost of security. Retrofitting security after the fact can be very expensive and ineffective.

Proposal and requirements phases

These lifecycle phases include opportunity development and project scoping.

In these phases, I evaluate target market requirements and sales forecasts to discover product security requirements. I get agreement on security requirements as with other requirements.

I develop use cases and abuse cases.

Architecture and Design phases

These lifecycle phases include project analysis and product design.

In these phases, I must create a one page view of security, and do architectural reviews including external reviews.

I must evaluate how each security requirement is met. I perform a threat analysis to identify the most significant risks.

Development and Test phases

These lifecycle phases include product construction, and product test.

Software Quality Management (SQM) is important. A good security implementation strategy relies upon a holistic view of expertise, processes, and tools.

I adhere to good programming practices. Though painful, I must perform code review and unit testing. Better coding practices make the job easier. I use automated tools on code for static analysis where possible.

Implementation errors do matter. Tracing back from vulnerable location to input is critical.

Most QA people can't do security testing. Conventional defect avoidance and testing methods aren't effective for security defects. Security defects are complex so common intuition does not work. Positive tests verify it works as expected. Perform risk-based security testing to maximize benefit.

Negative tests attempt to subvert the security of the system. Build abuse cases and non-functional requirements into testing for better results. Write a unit test for each back door to verify removal before shipping the product.

Products need experts looking inside-out from design phase forward.

Test results and field feedback phases

These lifecycle phases include product launch, implementation, and project sustaining or close out.

In these phases, I must perform risk analysis of all security defects.

Black box penetration testing is not nearly as good as expert review, but still I can add security probing of software from test phase forward.

I make sure to track security breaks and responses.