| Introduction | xxiii |
| PART I CONTEMPORARY SECURITY | |
| 1 The Need for Secure Systems | 3 |
| Applications on the Wild Wild Web | 5 |
| The Need for Trustworthy Computing | 7 |
| Getting Everyone's Head in the Game | 7 |
| Using Tact to Sell Security to the Organization | 8 |
| Using Subversion | 11 |
| Some Ideas for Instilling a Security Culture | 13 |
| Get the Boss to Send an E-Mail | 14 |
| Nominate a Security Evangelist | 15 |
| The Attacker's Advantage and the Defender's Dilemma | 19 |
| Principle #1: The defender must defend all points; the attacker can choose the weakest point. | 19 |
| Principle #2: The defender can defend only against known attacks; the attacker can probe for unknown vulnerabilities. | 20 |
| Principle #3: The defender must be constantly vigilant; the attacker can strike at will. | 20 |
| Principle #4: The defender must play by the rules; the attacker can play dirty. | 21 |
| Summary | 21 |
| 2 The Proactive Security Development Process | 23 |
| Process Improvements | 25 |
| The Role of Education | 26 |
| Resistance to Mandatory Training | 29 |
| Ongoing Training | 29 |
| Advancing the Science of Security | 29 |
| Education Proves the More Eyes Fallacy | 31 |
| Now the Evidence! | 31 |
| Design Phase | 32 |
| Security Questions During Interviews | 33 |
| Define the Product Security Goals | 34 |
| Security Is a Product Feature | 37 |
| Making Time for Security | 40 |
| Threat Modeling Leads to Secure Design | 41 |
| Build End-of-Life Plans for Insecure Features | 41 |
| Setting the Bug Bar | 41 |
| Security Team Review | 43 |
| Development Phase | 43 |
| Be Hardcore About Who Can Check In New Code (Check-Ins Checked) | 43 |
| Security Peer Review of New Code (Check-Ins Checked) | 44 |
| Define Secure Coding Guidelines | 44 |
| Review Old Defects | 44 |
| External Security Review | 45 |
| Security Push | 45 |
| Be Mindful of Your Bug Counts | 46 |
| Keep Track of Bug Metrics | 46 |
| No Surprises and No Easter Eggs! | 47 |
| Test Phase | 47 |
| Shipping and Maintenance Phases | 47 |
| How Do You Know When You're Done? | 47 |
| Response Process | 48 |
| Accountability | 49 |
| Summary | 49 |
| 3 Security Principles to Live By | 51 |
| SD3: Secure by Design, by Default, and in Deployment | 51 |
| Secure by Design | 51 |
| Secure by Default | 53 |
| Secure in Deployment | 53 |
| Security Principles | 54 |
| Learn from Mistakes | 54 |
| Minimize Your Attack Surface | 57 |
| Employ Secure Defaults | 57 |
| Use Defense in Depth | 59 |
| Use Least Privilege | 60 |
| Backward Compatibility Will Always Give You Grief | 62 |
| Assume External Systems Are Insecure | 63 |
| Plan on Failure | 64 |
| Fail to a Secure Mode | 64 |
| Remember That Security Features != Secure Features | 66 |
| Never Depend on Security Through Obscurity Alone | 66 |
| Don't Mix Code and Data | 67 |
| Fix Security Issues Correctly | 67 |
| Summary | 68 |
| 4 Threat Modeling | 69 |
| Secure Design Through Threat Modeling | 70 |
| Assemble the Threat-Modeling Team | 72 |
| Decompose the Application | 73 |
| Determine the Threats to the System | 83 |
| Rank the Threats by Decreasing Risk | 93 |
| Choose How to Respond to the Threats | 106 |
| Choose Techniques to Mitigate the Threats | 107 |
| Security Techniques | 108 |
| Authentication | 109 |
| Authorization | 114 |
| Tamper-Resistant and Privacy-Enhanced Technologies | 115 |
| Protect Secrets, or Better Yet, Don't Store Secrets | 116 |
| Encryption, Hashes, MACs, and Digital Signatures | 116 |
| Auditing | 117 |
| Filtering, Throttling, and Quality of Service | 118 |
| Least Privilege | 118 |
| Mitigating the Sample Payroll Application Threats | 118 |
| A Cornucopia of Threats and Solutions | 120 |
| Summary | 124 |
| PART II SECURE CODING TECHNIQUES | |
| 5 Public Enemy #1: The Buffer Overrun | 127 |
| Stack Overruns | 129 |
| Heap Overruns | 138 |
| Array Indexing Errors | 144 |
| Format String Bugs | 147 |
| Unicode and ANSI Buffer Size Mismatches | 153 |
| A Real Unicode Bug Example | 154 |
| Preventing Buffer Overruns | 155 |
| Safe String Handling | 156 |
| A Word of Caution About String-Handling Functions | 166 |
| The Visual C++ .NET /GS Option | 167 |
| Summary | 170 |
| 6 Determining Appropriate Access Control | 171 |
| Why ACLs Are Important | 171 |
| A Diversion: Fixing the Registry Code | 173 |
| What Makes Up an ACL? | 175 |
| A Method of Choosing Good ACLs | 178 |
| Effective Deny ACEs | 180 |
| Creating ACLs | 181 |
| Creating ACLs in Windows NT 4 | 181 |
| Creating ACLs in Windows 2000 | 185 |
| Creating ACLs with Active Template Library | 189 |
| Getting the ACE Order Right | 191 |
| Be Wary of the Terminal Server and Remote Desktop SIDs | 193 |
| NULL DACLs and Other Dangerous ACE Types | 195 |
| NULL DACLs and Auditing | 197 |
| Dangerous ACE Types | 197 |
| What If I Can't Change the NULL DACL? | 198 |
| Other Access Control Mechanisms | 199 |
| .NET Framework Roles | 199 |
| COM+ Roles | 201 |
| IP Restrictions | 202 |
| SQL Server Triggers and Permissions | 203 |
| A Medical Example | 203 |
| An Important Note About Access Control Mechanisms | 205 |
| Summary | 206 |
| 7 Running with Least Privilege | 207 |
| Least Privilege in the Real World | 208 |
| Viruses and Trojans | 209 |
| Web Server Defacements | 210 |
| Brief Overview of Access Control | 211 |
| Brief Overview of Privileges | 211 |
| SeBackupPrivilege Issues | 212 |
| SeRestorePrivilege Issues | 215 |
| SeDebugPrivilege Issues | 215 |
| SeTcbPrivilege Issues | 216 |
| SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege Issues | 217 |
| SeLoadDriverPrivilege Issues | 217 |
| SeRemoteShutdownPrivilege Issues | 217 |
| SeTakeOwnershipPrivilege Issues | 217 |
| Brief Overview of Tokens | 218 |
| How Tokens, Privileges, SIDs, ACLs, and Processes Relate | 218 |
| SIDs and Access Checks, Privileges and Privilege Checks | 219 |
| Three Reasons Applications Require Elevated Privileges | 220 |
| ACL Issues | 220 |
| Privilege Issue | 221 |
| Using LSA Secrets | 221 |
| Solving the Elevated Privileges Issue | 222 |
| Solving ACL Issues | 222 |
| Solving Privilege Issues | 223 |
| Solving LSA Issues | 223 |
| A Process for Determining Appropriate Privilege | 223 |
| Step 1: Find Resources Used by the Application | 224 |
| Step 2: Find Privileged APIs Used by the Application | 224 |
| Step 3: Which Account Is Required? | 226 |
| Step 4: Get the Token Contents | 226 |
| Step 5: Are All the SIDs and Privileges Required? | 232 |
| Step 6: Adjust the Token | 233 |
| Low-Privilege Service Accounts in Windows XP and Windows .NET Server 2003 | 248 |
| The Impersonate Privilege and Windows .NET Server 2003 | 250 |
| Debugging Least-Privilege Issues | 251 |
| Why Applications Fail as a Normal User | 251 |
| How to Determine Why Applications Fail | 252 |
| Summary | 258 |
| 8 Cryptographic Foibles | 259 |
| Using Poor Random Numbers | 259 |
| The Problem: rand | 260 |
| Cryptographically Random Numbers in Win32 | 262 |
| Cryptographically Random Numbers in Managed Code | 268 |
| Cryptographically Random Numbers in Web Pages | 269 |
| Using Passwords to Derive Cryptographic Keys | 269 |
| Measuring the Effective Bit Size of a Password | 270 |
| Key Management Issues | 272 |
| Long-Term and Short-Term Keys | 274 |
| Use Appropriate Key Lengths to Protect Data | 274 |
| Keep Keys Close to the Source | 276 |
| Key Exchange Issues | 279 |
| Creating Your Own Cryptographic Functions | 281 |
| Using the Same Stream-Cipher Encryption Key | 283 |
| Why People Use Stream Ciphers | 284 |
| The Pitfalls of Stream Ciphers | 284 |
| What If You Must Use the Same Key? | 287 |
| Bit-Flipping Attacks Against Stream Ciphers | 289 |
| Solving Bit-Flipping Attacks | 290 |
| When to Use a Hash, Keyed Hash, or Digital Signature | 290 |
| Reusing a Buffer for Plaintext and Ciphertext | 296 |
| Using Crypto to Mitigate Threats | 297 |
| Document Your Use of Cryptography | 298 |
| 9 Protecting Secret Data | 299 |
| Attacking Secret Data | 300 |
| Sometimes You Don't Need to Store a Secret | 301 |
| Creating a Salted Hash | 302 |
| Using PKCS #5 to Make the Attacker's Job Harder | 303 |
| Getting the Secret from the User | 305 |
| Protecting Secrets in Windows 2000 and Later | 305 |
| A Special Case: Client Credentials in Windows XP | 309 |
| Protecting Secrets in Windows NT 4 | 311 |
| Protecting Secrets in Windows 95, Windows 98, Windows Me, and Windows CE | 315 |
| Getting Device Details Using PnP | 316 |
| Not Opting for a Least Common Denominator Solution | 320 |
| Managing Secrets in Memory | 321 |
| A Compiler Optimization Caveat | 322 |
| Encrypting Secret Data in Memory | 326 |
| Locking Memory to Prevent Paging Sensitive Data | 327 |
| Protecting Secret Data in Managed Code | 329 |
| Managing Secrets in Memory in Managed Code | 335 |
| Raising the Security Bar | 336 |
| Storing the Data in a File on a FAT File System | 337 |
| Using an Embedded Key and XOR to Encode the Data | 337 |
| Using an Embedded Key and 3DES to Encrypt the Data | 337 |
| Using 3DES to Encrypt the Data and Storing a Password in the Registry | 337 |
| Using 3DES to Encrypt the Data and Storing a Strong Key in the Registry | 337 |
| Using 3DES to Encrypt the Data, Storing a Strong Key in the Registry, and ACLing the File and the Registry Key | 338 |
| Using 3DES to Encrypt the Data, Storing a Strong Key in the Registry, Requiring the User to Enter a Password, and ACLing the File and the Registry Key | 338 |
| Trade-Offs When Protecting Secret Data | 338 |
| Summary | 339 |
| 10 All Input Is Evil! | 341 |
| The Issue | 342 |
| Misplaced Trust | 343 |
| A Strategy for Defending Against Input Attacks | 345 |
| How to Check Validity | 347 |
| Tainted Variables in Perl | 349 |
| Using Regular Expressions for Checking Input | 350 |
| Be Careful of What You FindDid You Mean to Validate? | 352 |
| Regular Expressions and Unicode | 353 |
| A Regular Expression Rosetta Stone | 358 |
| Regular Expressions in Perl | 358 |
| Regular Expressions in Managed Code | 359 |
| Regular Expressions in Script | 360 |
| Regular Expressions in C++ | 360 |
| A Best Practice That Does Not Use Regular Expressions | 361 |
| Summary | 362 |
| 11 Canonical Representation Issues | 363 |
| What Does Canonical Mean, and Why Is It a Problem? | 364 |
| Canonical Filename Issues | 364 |
| Bypassing Napster Name Filtering | 364 |
| Vulnerability in Apple Mac OS X and Apache | 365 |
| DOS Device Names Vulnerability | 365 |
| Sun Microsystems StarOffice /tmp Directory Symbolic-Link Vulnerability | 366 |
| Common Windows Canonical Filename Mistakes | 367 |
| Canonical Web-Based Issues | 373 |
| Bypassing AOL Parental Controls | 373 |
| Bypassing eEye's Security Checks | 374 |
| Zones and the Internet Explorer 4 "Dotless-IP Address" Bug | 374 |
| Internet Information Server 4.0 ::$DATA Vulnerability | 375 |
| When is a Line Really Two Lines? | 377 |
| Yet Another Web IssueEscaping | 378 |
| Visual Equivalence Attacks and the Homograph Attack | 382 |
| Preventing Canonicalization Mistakes | 383 |
| Don't Make Decisions Based on Names | 383 |
| Use a Regular Expression to Restrict What's Allowed in a Name | 383 |
| Stopping 8.3 Filename Generation | 385 |
| Don't Trust the PATHUse Full Path Names | 385 |
| Attempt to Canonicalize the Name | 386 |
| Calling CreateFile Safely | 390 |
| Web-Based Canonicalization Remedies | 391 |
| Restrict What Is Valid Input | 391 |
| Be Careful When Dealing with UTF-8 | 391 |
| ISAPIsBetween a Rock and a Hard Place | 392 |
| A Final Thought: Non-File-Based Canonicalization Issues | 393 |
| Server Names | 393 |
| Usernames | 394 |
| Summary | 396 |
| 12 Database Input Issues | 397 |
| The Issue | 398 |
| Pseudoremedy #1: Quoting the Input | 401 |
| Pseudoremedy #2: Use Stored Procedures | 402 |
| Remedy #1: Never Ever Connect as sysadmin | 403 |
| Remedy #2: Building SQL Statements Securely | 404 |
| Building SQL Stored Procedures Securely | 406 |
| An In-Depth Defense in Depth Example | 407 |
| Summary | 411 |
| 13 Web-Specific Input Issues | 413 |
| Cross-Site Scripting: When Output Turns Bad | 413 |
| Sometimes the Attacker Doesn't Need a <SCRIPT> Block | 417 |
| The Attacker Doesn't Need the User to Click a Link! | 418 |
| Other XSS-Related Attacks | 418 |
| XSS Attacks Against Local Files | 418 |
| XSS Attacks Against HTML Resources | 420 |
| XSS Remedies | 421 |
| Encoding Output | 422 |
| Adding Double Quotes Around All Tag Properties | 422 |
| Inserting Data in the innerText Property | 423 |
| Forcing the Codepage | 423 |
| The Internet Explorer 6.0 SP1 HttpOnly Cookie Option | 424 |
| Internet Explorer "Mark of the Web" | 425 |
| Internet Explorer <FRAME SECURITY> Attribute | 426 |
| ASP.NET 1.1 ValidateRequest configuration option | 427 |
| Don't Look for Insecure Constructs | 428 |
| But I Want Users to Post HTML to My Web Site! | 430 |
| How to Review Code for XSS Bugs | 431 |
| Other Web-Based Security Topics | 431 |
| eval() Can Be Bad | 431 |
| HTTP Trust Issues | 432 |
| ISAPI Applications and Filters | 433 |
| Be Wary of "Predictable Cookies" | 436 |
| SSL/TLS Client Issues | 437 |
| Summary | 438 |
| 14 Internationalization Issues | 439 |
| The Golden I18N Security Rules | 440 |
| Use Unicode in Your Application | 440 |
| Prevent I18N Buffer Overruns | 441 |
| Words and Bytes | 442 |
| Validate I18N | 443 |
| Visual Validation | 443 |
| Do Not Validate Strings with LCMapString | 443 |
| Use CreateFile to Validate Filenames | 443 |
| Character Set Conversion Issues | 444 |
| Use MultiByteToWideChar with MB_PRECOMPOSED and MB_ERR_INVALID_CHARS | 444 |
| Use WideCharToMultiByte with WC_NO_BEST_FIT_CHARS | 445 |
| Comparison and Sorting | 448 |
| Unicode Character Properties | 448 |
| Normalization | 450 |
| Summary | 451 |
| PART III EVEN MORE SECURE CODING TECHNIQUES | |
| 15 Socket Security | 455 |
| Avoiding Server Hijacking | 456 |
| TCP Window Attacks | 463 |
| Choosing Server Interfaces | 464 |
| Accepting Connections | 464 |
| Writing Firewall-Friendly Applications | 470 |
| Use One Connection to Do the Job | 471 |
| Don't Require the Server to Connect Back to the Client | 471 |
| Use Connection-Based Protocols | 472 |
| Don't Multiplex Your Application over Another Protocol | 472 |
| Don't Embed Host IP Addresses in Application-Layer Data | 473 |
| Make Your Application Configurable | 473 |
| Spoofing and Host-Based and Port-Based Trust | 473 |
| IPv6 Is Coming! | 474 |
| Summary | 476 |
| 16 Securing RPC, ActiveX Controls, and DCOM | 477 |
| An RPC Primer | 478 |
| What Is RPC? | 478 |
| Creating RPC Applications | 479 |
| How RPC Applications Communicate | 481 |
| Secure RPC Best Practices | 482 |
| Use the /robust MIDL Switch | 483 |
| Use the [range] Attribute | 483 |
| Require Authenticated Connections | 484 |
| Use Packet Privacy and Integrity | 489 |
| Use Strict Context Handles | 491 |
| Don't Rely on Context Handles for Access Checks | 492 |
| Be Wary of NULL Context Handles | 493 |
| Don't Trust Your Peer | 494 |
| Use Security Callbacks | 495 |
| Implications of Multiple RPC Servers in a Single Process | 497 |
| Use Mainstream Protocols | 499 |
| Secure DCOM Best Practices | 499 |
| DCOM Basics | 500 |
| Application-Level Security | 502 |
| DCOM User Contexts | 502 |
| Programmatic Security | 505 |
| Sources and Sinks | 508 |
| An ActiveX Primer | 509 |
| Secure ActiveX Best Practices | 509 |
| What ActiveX Components Are Safe for Initialization and Safe for Scripting? | 510 |
| Best Practices for Safe for Initialization and Scripting | 511 |
| Summary | 515 |
| 17 Protecting Against Denial of Service Attacks | 517 |
| Application Failure Attacks | 517 |
| CPU Starvation Attacks | 521 |
| Memory Starvation Attacks | 529 |
| Resource Starvation Attacks | 530 |
| Network Bandwidth Attacks | 532 |
| Summary | 533 |
| 18 Writing Secure .NET Code | 535 |
| Code Access Security: In Pictures | 537 |
| FxCop: A "Must-Have" Tool | 539 |
| Assemblies Should Be Strong-Named | 540 |
| Strong-Named Assemblies and ASP.NET | 542 |
| Specify Assembly Permission Requirements | 542 |
| Request Minimal Permission Set | 543 |
| Refuse Unneeded Permissions | 544 |
| Request Optional Permissions | 544 |
| Overzealous Use of Assert | 545 |
| Further Information Regarding Demand and Assert | 547 |
| Keep the Assertion Window Small | 549 |
| Demands and Link Demands | 550 |
| An Example LinkDemand Security Bug | 551 |
| Use SuppressUnmanagedCodeSecurityAttribute with Caution | 552 |
| Remoting Demands | 553 |
| Limit Who Uses Your Code | 554 |
| No Sensitive Data in XML or Configuration Files | 555 |
| Review Assemblies That Allow Partial Trust | 556 |
| Check Managed Wrappers to Unmanaged Code for Correctness | 557 |
| Issues with Delegates | 558 |
| Issues with Serialization | 558 |
| The Role of Isolated Storage | 559 |
| Disable Tracing and Debugging Before Deploying ASP.NET Applications | 561 |
| Do Not Issue Verbose Error Information Remotely | 561 |
| Deserializing Data from Untrusted Sources | 562 |
| Don't Tell the Attacker Too Much When You Fail | 562 |
| Summary | 564 |
| PART IV SPECIAL TOPICS | |
| 19 Security Testing | 567 |
| The Role of the Security Tester | 567 |
| Security Testing Is Different | 568 |
| Building Security Test Plans from a Threat Model | 569 |
| Decompose the Application | 570 |
| Identify the Component Interfaces | 570 |
| Rank the Interfaces by Potential Vulnerability | 572 |
| Ascertain the Data Structures Used by Each Interface | 573 |
| Attacking Applications with STRIDE | 573 |
| Attacking with Data Mutation | 575 |
| Before Testing | 587 |
| Building Tools to Find Flaws | 588 |
| Testing Clients with Rogue Servers | 606 |
| Should a User See or Modify That Data? | 607 |
| Testing with Security Templates | 607 |
| When You Find a Bug, You're Not Done! | 609 |
| Test Code Should Be of Great Quality | 610 |
| Test the End-to-End Solution | 611 |
| Determining Attack Surface | 611 |
| Determine Root Attack Vectors | 611 |
| Determine Bias For Attack Vectors | 612 |
| Count the Biased Vectors in the Product | 612 |
| Summary | 613 |
| 20 Performing a Security Code Review | 615 |
| Dealing with Large Applications | 617 |
| A Multiple-Pass Approach | 618 |
| Low-Hanging Fruit | 619 |
| Integer Overflows | 620 |
| A Related Issue: Integer Underflows | 624 |
| Checking Returns | 624 |
| Perform an Extra Review of Pointer Code | 625 |
| Never Trust the Data | 625 |
| Sumary | 626 |
| 21 Secure Software Installation | 627 |
| Principle of Least Privilege | 628 |
| Clean Up After Yourself! | 630 |
| Using the Security Configuration Editor | 630 |
| Low-Level Security APIs | 638 |
| Using the Windows Installer | 638 |
| Summary | 640 |
| 22 Building Privacy into Your Application | 641 |
| Malicious vs. Annoying Invasions of Privacy | 642 |
| Major Privacy Legislation | 643 |
| Personally Identifiable Information | 643 |
| The EU Directives on Data Protection | 643 |
| Safe Harbor Principles | 644 |
| Other Privacy Legislation | 646 |
| Privacy vs. Security | 646 |
| Building a Privacy Infrastructure | 647 |
| The Role of the Chief Privacy Officer | 648 |
| The Role of the Privacy Advocate | 648 |
| Designing Privacy-Aware Applications | 649 |
| Including Privacy in the Development Process | 649 |
| Exploring Privacy Features | 652 |
| Summary | 662 |
| 23 General Good Practices | 663 |
| Don't Tell the Attacker Anything | 663 |
| Service Best Practices | 663 |
| Security, Services, and the Interactive Desktop | 664 |
| Service Account Guidelines | 665 |
| Don't Leak Information in Banner Strings | 667 |
| Be Careful Changing Error Messages in Fixes | 668 |
| Double-Check Your Error Paths | 668 |
| Keep It Turned Off! | 668 |
| Kernel-Mode Mistakes | 668 |
| High-Level Security Issues | 669 |
| Handles | 670 |
| Symbolic Links | 670 |
| Quota | 670 |
| Serialization Primitives | 670 |
| Buffer-Handling Issues | 671 |
| IRP Cancellation | 673 |
| Add Security Comments to Code | 674 |
| Leverage the Operating System | 674 |
| Don't Rely on Users Making Good Decisions | 675 |
| Calling CreateProcess Securely | 675 |
| Do Not Pass NULL for lpApplicationName | 677 |
| Use Quotes Around the Path to Executable in lpCommandLine | 677 |
| Don't Create Shared/Writable Segments | 677 |
| Using Impersonation Functions Correctly | 678 |
| Don't Write User Files to \Program Files | 678 |
| Don't Write User Data to HKLM | 679 |
| Don't Open Objects for FULL_CONTROL or ALL_ACCESS | 679 |
| Object Creation Mistakes | 679 |
| Care and Feeding of CreateFile | 681 |
| Creating Temporary Files Securely | 682 |
| Implications of Setup Programs and EFS | 686 |
| File System Reparse Point Issues | 686 |
| Client-Side Security Is an Oxymoron | 687 |
| Samples Are Templates | 688 |
| Dogfood Your Stuff! | 688 |
| You Owe It to Your Users If. | 689 |
| Determining Access Based on an Administrator SID | 689 |
| Allow Long Passwords | 690 |
| Be Careful with _alloca | 691 |
| ATL Conversion Macros | 691 |
| Don't Embed Corporate Names | 692 |
| Move Strings to a Resource DLL | 693 |
| Application Logging | 693 |
| Migrate Dangerous C/C++ to Managed Code | 694 |
| 24 Writing Security Documentation and Error Messages | 695 |
| Security Issues in Documentation | 695 |
| The Basics | 696 |
| Threat Mitigation Through Documentation | 697 |
| Documenting Security Best Practices | 698 |
| Security Issues in Error Messages | 700 |
| A Typical Security Message | 700 |
| Information Disclosure Issues | 701 |
| Informed Consent | 702 |
| Progressive Disclosure | 704 |
| Be Specific | 705 |
| Consider Not Asking the Question | 706 |
| Usability Test Your Security Messages | 707 |
| A Note When Reviewing Product Specifications | 708 |
| Security Usability | 708 |
| Summary | 709 |
| PART V APPENDIXES | |
| A Dangerous APIs | 713 |
| B Ridiculous Excuses We've Heard | 723 |
| C A Designer's Security Checklist | 729 |
| D A Developer's Security Checklist | 731 |
| E A Tester's Security Checklist | 737 |
| A Final Thought | 739 |
| ANNOTATED BIBLIOGRAPHY | 741 |
| INDEX | 747 |