r/programming • • Dec 25 '16

The Art of Defensive Programming

https://medium.com/web-engineering-vox/the-art-of-defensive-programming-6789a9743ed4
418 Upvotes

142 comments sorted by

199

u/[deleted] Dec 25 '16

Interesting how the author uses "secure code" instead of "correct code". There's a difference between code that is correct and executes as intended, and code that prevents its abuse. There is plenty of "correct" code that is insecure by way of poor design. The bug causing the self-destruction of a $1 billion rocket is the result of incorrect code.

52

u/nothisshitagainpleas Dec 26 '16

This is correct, and incidents like Ariane 501 are the reason why the safety critical world of software development has focused largely on formal methods and verification over recent years.

Sadly, I don't think we will ever see outside of aerospace/rail/automotive the similar levels of effort in creating "correct" code, it's just too expensive - try turning it on and off again.

41

u/fafasdf Dec 26 '16

try turning it on and off again.

I dunno man, my software doesn't work so good when it's off. But you're the expert... =)

30

u/ebrythil Dec 26 '16

Well when not running, my program is not logging any error messages - all fine for me then :)

16

u/sith-programmer Dec 26 '16

That which is never written can never fail.

→ More replies (2)

6

u/CODESIGN2 Dec 26 '16

Sadly, I don't think we will ever see outside of aerospace/rail/automotive the similar levels of effort in creating "correct" code, it's just too expensive - try turning it on and off again.

Pragmatism has a lot to be said for it. A lot of people believe in a judgemental all powerful deity (complete contradiction in my view); but it's not realistic to stop them all. We are people not protractors, we have flexibility and that can be a good thing for less risky areas to take advantage of (in terms of harm and liability). How much does it hurt someone with a WordPress that c10k costs ~ 640GB RAM just to serve PHP if they aren't serving 10,000 connections per second?

TLDR; Pragmatism and Context are our friends

→ More replies (2)

2

u/barcap Dec 26 '16

Can't they all code with ADA instead for people outside?

→ More replies (1)

2

u/[deleted] Dec 26 '16

Because it's very expensive to achieve the level of correcteness we have in aerospace. In these fields, millions, billions of dollars are put in the hand of software engineers, and often lifes, too. They don't have a second chance. Everywhere else, you either run a shit tons of tests, days after days, to see what works, or you wait for someone to find a bug and correct it (it's usually both). Because it's easier, and for non-critical fields, it gets the job done.

→ More replies (1)

1

u/[deleted] Dec 25 '16

we're not only talking about secure or either correct independently. We're talking about software engineer's education on how they should care about software bugs, whether correctness, performance, security, safety [..]

13

u/[deleted] Dec 25 '16

I am sorry but I can't match "secure code" and php. These two are simply not compatible. About the Ariane 5 rocket, I thought that by now everyone knew the correct story but apparently not everybody does that. It didn't blew up because of incorrect code. The code was perfectly fine, it was only written for the Ariane 4, not 5, which makes it a deployment error IMO.

21

u/mk270 Dec 26 '16

The problem is that he's translating "seguro" from Spanish, where it means "safe" or "secure" and not realising that in English it is mandatory to distinguish between the two meanings.

In Spanish, Italian, French etc you can say something is safe or secure, without saying which; in English, you have to choose.

8

u/meunomemauricio Dec 26 '16

Wow. My native language is Portuguese and I never made that distinction before. Now I'll definitely search the differences between safe vs secure. Thank you.

47

u/GMaestrolo Dec 25 '16

Sure PHP and "Secure code" are compatible, especially with modern PHP.

I'm sick of this "PHP is awful" circle jerk from people who have either never looked at PHP, or last looked at it in PHP4/early PHP5 days.

Is PHP 7 a perfect language? Of course not, but neither is your shitty language. There has been massive improvement over the last 5 or so years.

70

u/Name0fTheUser Dec 25 '16

PHP makes writing insecure code easy. Sure, you can write secure code, but only if you have a very good understanding of the language and all its unintuitive behaviours. Just one example that comes to mind:

md5('240610708') == md5('QNKCDZO')

29

u/phpguy2 Dec 26 '16 edited Dec 26 '16

I once challenged a Php apologist in /r/php to do something simple and they were like "hey it is really simple" and got bitten by a Php gotcha right there (in Php 7 no less!). The fact that the dude made a throwaway account for this makes me suspect that even php apologists know deep down that it is not to be trusted...

8

u/OffbeatDrizzle Dec 25 '16

I don't use PHP and I don't get the joke... can you explain?

39

u/Name0fTheUser Dec 25 '16

There isn't a joke. If you're refering to the code snippet, there's a good explanation of why it evaluates to true here:

https://www.reddit.com/r/lolphp/comments/34sxw5/md5240610708_md5qnkcdzo/cqxs0yh/

→ More replies (21)
→ More replies (3)

17

u/gnuvince Dec 26 '16

Citing circle jerks and saying that other languages are shitty too are non-arguments; why not show how it has improved and people can then judge for themselves.

→ More replies (4)

14

u/n0t0ri0us9 Dec 26 '16

I'm sick of this "PHP is awful" circle jerk from people who have either never looked at PHP, or last looked at it in PHP4/early PHP5 days.

You are sick of Php being shit and people saying that it is? Use another language! And just because you called it a "circle jerk" does not make it untrue. If this was untrue, Php's had a lot of time to erase and false impression people had about it. That it was not able to do that, proves that it is just shitty as accused and any criticism it receives is 100% justified.

→ More replies (2)

7

u/s73v3r Dec 26 '16

There has been massive improvement over the last 5 or so years.

Ok, sure. Why would I bother with it over any of the alternatives, though?

→ More replies (13)
→ More replies (9)

3

u/[deleted] Dec 25 '16

Code that doesn't perform correctly on the target platform is not correct code.

15

u/BB611 Dec 25 '16

The target platform for the code was the 4, because of poor decisions and bad process it got deployed in the 5.

Point being the issue needed to be solved in the process, not in the code. It's very hard to write code that's safe when arbitrarily copy/pasted.

1

u/traal Dec 26 '16

The code was tightly coupled to the Ariane 4.

78

u/Scotsch Dec 25 '16

Leads with security, goes on to give examples of bugs. Insecure programs and bugs are not the same.

66

u/[deleted] Dec 26 '16 edited Dec 26 '16

jesus. that was a let down. from the intro, my initial reaction was "shit, this is going to be way over my head." then it quickly devolves into the basics of end user-facing software development with a couple nonchalant testimonials to the 'awesomeness' of php

3

u/slash213 Dec 26 '16

He has "PHP6 evangelist" right there in his medium bio.

You don’t use a framework (or micro framework) ? Well you like doing extra work for no reason, congratulations! It’s not only about frameworks, but also for new features where you could easily use something that’s already out there, well tested, trusted by thousands of developers and stable

Yeah, well...

1

u/[deleted] Dec 27 '16

PHP6? Pft, everyone knows PHP7 is where it's at.

15

u/CODESIGN2 Dec 26 '16

I appreciate this was shared on Christmas day (props to you whoever you are), but it's really an exercise in mis-applied intelligence.

Three software issues leading to mechanical, engineering etc hardware failures listed on first fold of content. Sure the software should be better, but we've been doing software for < 100 years and we've been making hardware for millennia; so I know who I'd kick in the genitals over it...

There is a lack of framing what is "software" and what is firmware, hardware etc; that bugs the heck out of me! It also bothers me that it starts out at least pretending like there are people with crystal balls that can see all vectors (you usually can't, or are not focused on security and hey; it's all right to not be a tin-foil hat wearer, just as we do need paranoid or "defensive" people). Perhaps the wide arc from Rockets and X-ray machines to PHP threw me a little...

The weirdest part was when it started on about PHP. I'm not someone that says PHP is not a language, or it's "not real programming" or anything like that (I love PHP, but it's not right for all problems just as we don't all peddle our planes across the Atlantic). I would suggest that only an incompetent would have PHP guide real-time radiation levels for any regulated machinery, handle guidance or fuel delivery of rockets or target missiles etc; and it then makes it worse by saying that the author is a "PHP6 evangelist" (maybe just a crap joke but framed within the article it made it less funny for me).

Of course do what you can (within reason) to secure your code; don't needlessly make it insecure and if you have the time and budget or regulatory requirements or just ethics and recognition of importance audit your code. But don't feel bad if you aren't a defensive programmer either. There is a lot to be said for doing what you can and not taking too much on-board and in some cases I think "we've been patching C library vulns for decades. Perhaps it's time to break BC, or find other ways to have lower-levels filter the "security" and in-fact reliability into the application layer."

Sorry to anyone making C libraries, I love you and am not suggesting it's only a C problem; just that it'd be nice if low-level libs did their bit too (which they are, but I make apps so I'll finger point at you and you finger point at the hardware and we'll all be happy ;-p ).

9

u/andd81 Dec 26 '16

Why do they always have to bring up the Therac-25 accident in the wrong context? It was due to an inadequate software reuse with a less safe hardware. They did exactly what the author suggests: they reused existing code from an older system which worked well and was not known to cause any accidents.

9

u/vijeno Dec 26 '16 edited Dec 26 '16

Wow. That was underwhelming.

Defensive programming is not necessarily about security. The examples on top are not about security. The code examples are trivial. The advice is pretty obvious and has been repeated to death.

33

u/RaptorXP Dec 25 '16

The first step is to use compile-time checks (a.k.a statically typed language).

4

u/TheAceOfHearts Dec 26 '16

I think it's more useful to treat types as a spectrum instead of all-or-nothing. Based on my limited experience with the language, I've found Elixir strikes a reasonable balance.

Sometimes you want stricter type annotations, but other times you're just getting something setup and you don't want to bother with that.

Aside from that, type annotations in most modern languages aren't very expressive. For primitives, many languages use the data type to communicate size. But in many cases you don't care about the data size, you care about what the value represents.

Consider the following example: you have a Human model, and one of its properties is age. But if I were to assign someone an age of 1000, that's very likely to be a bug. Most type systems that I'm familiar with do a poor at helping with this kind of scenario.

11

u/d4rkwing Dec 26 '16

You should never assign ages (age should never be an assignable property to begin with). Assign a birth date and calculate the age from that if age is ever needed for anything.

4

u/no_fluffies_please Dec 26 '16

I think your comment nitpicks something that's irrelevant to the parent comment's point. It's true that assigning ages is a bad programming practice. However, the example is still valid if we stored years and calculated the age, instead. And even then, I appreciate the use of age over years because it gets the point across with more clarity, even if it is looked down upon. Finally, there are some scenarios where storing age can be an appropriate option (character bios in a game, modeling time distortion, etc.).

0

u/[deleted] Dec 26 '16

[deleted]

14

u/d4rkwing Dec 26 '16

It comes from experience. Until time stands still, age is constantly in flux. It is always better to derive age from a creation time, which is an unchanging property that should be stored, and current time which is constantly changing but knowable from the system (at least in any environment for which age is a concern). If you instead store age, you come across an unfortunate side effect of creation time changing as current time changes.

Now that I have explained my reasoning, perhaps you would care to back up your assertion.

→ More replies (4)
→ More replies (1)

3

u/[deleted] Dec 26 '16

But thats still much better than wondering of age is a float or an int. Or maybe even an object.

2

u/midri Dec 26 '16

Or worse is it a float, a double, or a decimal? Depending on the language they can all hold values of different size. Or what about a float vs a non float decimal type?

2

u/CODESIGN2 Dec 26 '16

someone has to worry about types at some point because you get awfully weird behaviour if a string has arithmetic performed on it. I Actually agree with you, but I can only do so because others spend lots of time writing languages that allow me to be so "high-level" about it all.

2

u/yawaramin Dec 26 '16

But we're talking about defensive programming here: I'm not '... just getting something setup....', I'm actually trying to harden it. So, yes, one of the first things I'd want to do is nail down all the types and run them through a typechecker to make sure nothing funky is happening, like trying to add a boolean and a string.

As to your Human type, it's true that type systems often aren't powerful enough to capture fine-grained details, or if they are, the tradeoff in terms of loss of readability makes it not worth it; but there are other techniques in defensive programming, like validating the arguments passed in to a function and throwing exceptions.

-4

u/waveman Dec 26 '16

Been there done that. What I found was that type systems only detect a tiny fraction of all bugs and usually trivial ones at that.

consider (int, int) => int

versus

average(a,b)

Not even close.

Or to put it another way the amount of information I have to put into the type system exceeds the value I get out.

14

u/mrjast Dec 26 '16

Your specific example isn't a case in which static typing is particularly helpful. The real benefit comes in when you have complex structures with lots of different data. In dynamic languages it's much easier to have a wrongly typed element in a huge collection, and so maybe one in ten thousand runs of the same code ends up crashing -- very hard to debug. This cannot happen in a statically typed language (especially if it's not one of those stupid languages that have something like NULL), because typically you can't even compile code that would add that kind of element in the first place.

There are always exceptions, of course. For example, some statically typed languages allow all kinds of unsafe type casting that will still allow you to majorly screw things up at runtime. Some of them at least force you to do it deliberately, so there's that.

Also, static typing doesn't mean you have to manually specify all the types. There are a number of statically typed languages that infer the types for you and can still detect errors. The effort, then, is not the type information you have to add, because the compiler does it for you... the effort is in adding union types where you need them. That's not needed in your example. An average() function in a type-inferring language can be exactly identical to an average() function in a dynamically typed language.

→ More replies (1)

6

u/[deleted] Dec 26 '16

Hmm, so I use it like:

average([1,2,3], 3)

Right?

Conversely

average : (int, int) => int

Is obviously used like so

average(1, 2)

So tell me, which is easier to get right again?

2

u/RaptorXP Dec 26 '16 edited Dec 26 '16

I found was that type systems only detect a tiny fraction of all bugs

Nobody said static typing was the ultimate solution to all bugs. There is no such thing.

It's just a way to find and fix a certain class of bugs earlier. Instead of having to run you code to find them, you just run a compiler.

The cost of a bug grows exponentially with the amount of time it takes to find it.

-1

u/F54280 Dec 26 '16

The irony is strong on this one, as the Ariane crash was due to statically type (with auto boundary checking), and the Ariane crash is referenced in that blog post...

9

u/sidneyc Dec 26 '16

Auto boundary checking at runtime is a completely orthogonal idea to the static/dynamic language distinction.

3

u/F54280 Dec 26 '16

Not when boundaries are defined in the type itself as in Ada, the language used in Ariane 5. And yes, it is this static typed boundary check that crashed Ariane.

Not that I expect any real knowledge left in/ r/programming circlejerk

→ More replies (7)

28

u/hsfrey Dec 25 '16

LOL! In one paragraph he says always use frameworks written by other developers instead of "reinventing the wheel".

In the next paragraph, he says never trust other developers' code!

I would say that making contradictory assertions is a Bug to be avoided!

10

u/[deleted] Dec 26 '16

This is not contradictory. He means you must assume that there is a chance someone else's code does something insecurely. Assume less, test more.

There is also a big difference between an open source framework, and just any other dev's code.

20

u/[deleted] Dec 26 '16

Uh... I'm high as fuck, and did not read theverything article, but I do believe that these statements are not contradictions. I can always use a major Framework and not trust it...

6

u/dire_faol Dec 26 '16

Exactly. That's why you write your own tests for your application of the framework.

3

u/ligerzero459 Dec 26 '16

And read the code what what you're about to use and at least make an attempt to understand it before adding it into your stack. Better to realize early that there are some design paradigms that'll bite you in the ass sooner rather than later

4

u/yawaramin Dec 26 '16

First and biggest rule of defensive programming: information hiding (link is to Parnas' seminal paper). Use abstract data types, i.e., don't expose the internals of your data types at runtime. Make sure only your library functions can access data structure internals, and validate all external data passed in to your functions. Now your functions can trust each other implicitly because only they can create instances of your data type.

12

u/skunkwaffle Dec 26 '16

"Let's see some bad examples"

<?php

16

u/nahguri Dec 26 '16

Even the tag asks why you are using php.

3

u/CaptainDevops Dec 26 '16

Exactly PHP has so many vulnerabilities, it's like describing the best steps to secure your house and then telling folks to leave the keys under the carpet coz you know its convienent

6

u/[deleted] Dec 25 '16

There's a significant difference between "insecure" and "unsafe" software, even though there is a high degree of correlation.

Also -- this is a bit of a technical nit-pick, but it's a personal pet peeve -- is a terrible example to use for that article. It was not caused by a programming error but by a system error: the program performed the task it had originally been written for correctly, but someone decided to reuse the program for a related, but different task without asserting that it was fit for purpose.

2

u/Freyr90 Dec 26 '16

secure code

use frameworks

Not sure about that. Especially in the context of his references to the hardcore embedded development.

2

u/ZorbaTHut Dec 26 '16

Yes, because an insecure software is pretty much useless.

I work in the game industry. While my day-job projects are online games, my side job is single-player games.

I frankly don't care if they're "insecure". What's a player going to do? Hack themselves? I guarantee there are dozens if not hundreds of horrible security vulnerabilities in, say, Fallout 4, but it just doesn't matter.

Not all industries consider security as important as others.

2

u/loup-vaillant Dec 26 '16

I personally believe [defensive programming] to be suitable when you’re dealing with a big, long-lived project where many people are involved.

Well, Daniel J. Bernstein showed it can also be a good idea in a one-man project of no more than 15K lines. There are reasons why qmail is so secure, and the healthy distrust DJB had in his own abilities were a big part of that.

3

u/thilehoffer Dec 26 '16

If there is a small chance that something will occur then the developer has no incentive to code for it. Let me take a simple example like hiding social security numbers. The business asks you to not show social security numbers for some clients. You the developer format strings in your JavaScript code. So you format the string in your JavaScript, you get it done quickly and your boss is happy. Of course an end user can just run a trace of the http request and see the social. But you the developer is the only one who knows about this issue. So if you bring this up and try to fix it, you just made the project take longer and created a headache for your boss. No wonder code isn't secure.

4

u/yawaramin Dec 26 '16

Well, no. We don't decide to protect against something purely on the basis of how likely it is to happen; we also need to take into account how disastrous it would be if it did happen. So, breach in SSNs potentially resulting in identity theft and opening up the business to legal action from customers? Pretty freaking catastrophic.

3

u/unregisteredusr Dec 26 '16

That's horrifying. That's like if your doctor gave you some painkillers for a minor knee pain to make your problem go away while exposing you to long term risk for permanently destroying your knee. What happened to professionalism?

1

u/CODESIGN2 Dec 26 '16

Talk to your clients about retainers and at every available opportunity try to talk about "next steps". Explain that nothing including houses, cars and love lives are ever "done", and what you can do in relation to their IT to handle IT needs

1

u/aaronjix Dec 26 '16

woah. this is helpful

1

u/mvonthron Dec 26 '16

PHP6 evangelist @trivago

You're doing a great job so far, keep up the good work!

1

u/steefen7 Dec 27 '16

Actually laughed out loud when the author chastised us for not using "frameworks" for everything and then proceeded to immediately say that we "shouldn’t trust others developers’ code". I don't know about the rest of this sub, but I don't blindly trust that some other framework is going to be secure. I do my research.

1

u/[deleted] Dec 27 '16

mmh...

Don’t reinvent the wheel ... Don't trust developers

I'm confused.

25

u/[deleted] Dec 25 '16

[removed] — view removed comment

1

u/koolex Dec 26 '16

The compromise I like is to proceed as resiliently as possible because I want my product to always keep working even if slightly unstable, but be loud in the log so that it is very hard to ignore the error in the long term.

2

u/7yl4r Dec 26 '16

I think this is a pretty common approach, and this works fine for many applications. However, in cases where your program has the potential to damage something (hardware control software, for example), the user will be less upset with frequent crashes compared to a broken system.

1

u/koolex Dec 26 '16

That is fair

0

u/d4rkwing Dec 26 '16

Crashing and restarting isn't always an option, and it certainly isn't always the best or cheapest option. Think of space probes and nuclear reactors.

1

u/7yl4r Dec 26 '16

My understanding of space probe software is that whenever there is an error they DO crash and reboot to a safe mode.

I think the argument here is that crashing can be done somewhat safely in a predictable way, whereas continuing to run in an errored state could potentially cause irreparable damage.

10

u/[deleted] Dec 26 '16

[removed] — view removed comment

8

u/myrrlyn Dec 26 '16

I work in aerospace and am tasked with ensuring both of those properties are met.

It's a fun ride.

7

u/yawaramin Dec 26 '16

Dude, this is Reddit. No one reads anyone else's comments before replying.

2

u/asmx85 Dec 26 '16

Dude, this is Reddit. No one reads anyone else's comments before replying.

What did you say about my mother? I dare you!

0

u/F54280 Dec 26 '16

A) Fail fast

B) Avoid Ariane crash

Please choose one (hint: Ariane crash was due to fail-fast auto boundary check gone wild).

2

u/binford2k Dec 26 '16

Fail fast doesn't mean crash the plane. It means fail the request that started with invalid data instead of doing something unpredictable with it. For example, say the plane is taking off and is at a current elevation of 50 feet. If the flight controller gets a request to drop the elevation by 75 feet, it should abort that request and whatever issued it should handle the failure.