1 of 17

Cyber Lab

Spring 2024 - Week 5

https://l.acmcyber.com/s24-w5-lab

2 of 17

πŸ“£ Announcements

  • πŸ€—Cyber Academy: 5/6
    • Hopefully things will have settled down
  • ⛳️PBR: Defcon Quals 5/4
    • Meet for lunch 12pm @ De Neve Plaza
    • CTF is 1-6pm @ Boelter Penthouse (8500)
  • 🚩CTF IRL! Friday, May 10th @ 6pm on IM Field
    • RSVP Required: https://l.acmcyber.com/ctfirl
  • πŸ’»CS Town Hall: Monday Week 7, 5/13
    • Mong Auditorium 6-7:30
    • https://l.acmcyber.com/s24-townhall
  • πŸ€Cyber Basketball: Friday 6-8pm @ Hitch courts

3 of 17

Security by Design

4 of 17

What is it?

  • Making it hard (preferably impossible) to write insecure code
  • This includes:
    • The way you structure your code
    • The libraries that you use
    • The programming language you use
    • The way you organize your infrastructure

5 of 17

What is my goal?

  • Provide a few of the insights that I've come to
    • Certainly not a comprehensive list
  • Get you thinking about securing code before it's even written
  • Show how cybersecurity pervades every field of computer science

6 of 17

Security via Types

Types aren't just how data is laid out in memory; they describe what a value means

By designing your types carefully, you can stop a whole range of invalid (and insecure) usage

7 of 17

Example Vulnerability

Chunked decryption with AES-GCM cipher in Node.js:

const cipher: Cipher = crypto.createCipheriv("aes-256-gcm", key, iv);

const chunks: Buffer[] = [];

for (const chunk of data) {

chunks.push(cipher.update(chunk));

}

chunks.push(cipher.final()); // crucial line

const decrypted: Buffer = Buffer.concat(chunks);

If you leave out the call to cipher.final(), your code won't break, but it'll be vulnerable to data forgery

8 of 17

Fixing it with types

What the types look like right now:

interface Cipher {

// decrypts a chunk of data

update(data: Buffer): Buffer;

// verifies the auth tag and returns leftover data

final(): Buffer;

}

Problem: There is nothing in the typesystem saying that final has to be called in order to use the data

9 of 17

// opaque wrapper around a buffer

class UnvalidatedData { private inner: Buffer; }

class Cipher {

// decrypts a chunk of data

update(data: Buffer): UnvalidatedData {

// wrap the decrypted data so that it's unusable

return new UnvalidatedData(this.oldUpdate(data));

}

// verifies the auth tag, appends remaining data, and concats data

final(chunks: UnvalidatedData[]): Buffer {

chunks.push(this.oldFinal());

// use some private method to unwrap the unvalidated data

return Buffer.concat(chunks.map(x => x.inner));

}

}

Now the data is unusable without calling final, forcing security in a way that's intuitive for the user

10 of 17

Takeaways

Security via types:

  • Has almost zero runtime cost (literally zero with compiler optimizations)
  • Forces the user to use your library correctly
  • Most of the time doesn't make code using the library any uglier or more annoying
  • Is possible in both dynamically-typed and statically-typed languages

11 of 17

Avoiding Invalid Values

Instead of allowing arbitrary values to be constructed then validating later, you should validate during construction

Allowing the creation of incomplete/invalid values means allowing the developer to forget to complete/validate it later

12 of 17

Example

Consider the following logic for saving a user in an ORM (suppose this is in a web server):

const user = User.create({username: blah, password: blah});

if (user.isValid()) {

user.save();

} else {

throw new Error("invalid user");

}

You need to remember to validate the user every single time you create one

Just put the validation in User.create instead

13 of 17

Especially Egregious Example

If you have an unusable "zero value", don't have one to begin with!

const user = new User();

user.username = blah;

user.password = blah;

user.age = blah;

Worsened by languages like C++ and Go which are designed around expecting a zero/default value to be meaningful when they frequently aren't

14 of 17

Topic Speedrun

Things I didn't cover but are nevertheless important:

  • Principle of least privilege
  • Avoiding "silent errors"
  • Avoiding unintuitive behavior
    • Instead of footnoting things in docs, rewrite code

15 of 17

Conclusion

  • Most security vulnerabilities arise from forgetting to take some security precaution
    • This is not a skill issue; security professionals also sometimes forget when writing code
  • We should design libraries, programming languages, and code that prevent using them in an unintended way
    • This isn't a new concept: people have been doing this for physical products forever

16 of 17

Questions?

17 of 17

Thanks for coming! ❀️