+ All Categories
Home > Technology > Making Web Development "Secure By Default"

Making Web Development "Secure By Default"

Date post: 06-May-2015
Category:
Upload: duo-security
View: 407 times
Download: 1 times
Share this document with a friend
Description:
By Adam Goodman, Principal Security Architect at Duo Security
44
Making Web Development “Secure By Default” Adam Goodman 2014-05-31
Transcript
Page 1: Making Web Development "Secure By Default"

Making Web Development “Secure By Default” !

Adam Goodman 2014-05-31

Page 2: Making Web Development "Secure By Default"

The OWASP Top 10

2004: • Unvalidated Input

• Broken Access Control

• Broken Authentication and Session Management

• Cross Site Scripting

• Buffer Overflow

• Injection

• Improper Error Handling

• Insecure Storage

• Application Denial of Service

• Insecure Configuration Management

Page 3: Making Web Development "Secure By Default"

The OWASP Top 10

2004: • Unvalidated Input

• Broken Access Control

• Broken Authentication and Session Management

• Cross Site Scripting

• Buffer Overflow

• Injection

• Improper Error Handling

• Insecure Storage

• Application Denial of Service

• Insecure Configuration Management

2013: • Injection

• Broken Authentication and Session Management

• Cross Site Scripting

• Insecure Direct Object References

• Security Misconfiguration

• Sensitive Data Exposure

• Missing Function Level Access Control

• Cross-Site Request Forgery

• Using Components with Known Vulnerabilities

• Unvalidated Redirects and Forwards

Page 4: Making Web Development "Secure By Default"
Page 5: Making Web Development "Secure By Default"

2004: • Unvalidated Input

• Broken Access Control

• Broken Authentication and Session Management

• Cross Site Scripting

• Buffer Overflow

• Injection

• Improper Error Handling

• Insecure Storage

• Application Denial of Service

• Insecure Configuration Management

2013: • Injection

• Broken Authentication and Session Management

• Cross Site Scripting

• Insecure Direct Object References

• Security Misconfiguration

• Sensitive Data Exposure

• Missing Function Level Access Control

• Cross-Site Request Forgery

• Using Components with Known Vulnerabilities

• Unvalidated Redirects and Forwards

Success Story: Buffer Overflow

Page 6: Making Web Development "Secure By Default"

Buffer Overflow - Review

void bad_idea(const char *input) {! char buf[10];! strcpy(buf, input);! /* ... */!}!!

int main(void) {! bad_idea("This is a longish string");! return 0;!}!

Page 7: Making Web Development "Secure By Default"

Buffer Overflow - Review

void less_bad_idea(const char *input) {! char buf[10];! strlcpy(buf, input, sizeof(buf));! /* ... */!}!!

int main(void) {! less_bad_idea(“This is a longish string");! return 0;!}!

Page 8: Making Web Development "Secure By Default"

Microsoft SDL

http://blogs.msdn.com/b/bryang/archive/2011/04/01/security-development-lifecycle.aspx

Page 9: Making Web Development "Secure By Default"

Best Practices

• “Deprecate Unsafe Functions” - no more strcpy, strcat, …

• Training

• Code reviews

• Automated enforcement (framework changes, analysis tools, …)

Page 10: Making Web Development "Secure By Default"

Compiler Smarts

void less_bad_idea(const char *input) {! char buf[10];! /* MSVC 2005 and newer; C++ only */! strcpy_s(buf, input);! /* ... */!}!

!

!

(Similar: FORTIFY_SOURCE in gcc)

Page 11: Making Web Development "Secure By Default"

Exploit Mitigation

Make it less feasible to exploit bugs (i.e. turn “security bugs” back into “ordinary bugs”):

• Stack Smashing Protection (SSP)

• Data Execution Prevention (DEP / NX)

• Address Space Layout Randomization (ASLR)

Page 12: Making Web Development "Secure By Default"

Encapsulate Hazardous Code

We don’t write web apps in C/C++ anymore.

!

Most of our high-level languages and web servers are still built on C, but these are carefully-curated components written by skilled developers with lots of review (we hope!).

Page 13: Making Web Development "Secure By Default"

(We’re Not There Quite Yet)

http://xkcd.com/1354/

Page 14: Making Web Development "Secure By Default"

To Review

Hypothesis: Buffer overflows fell off the OWASP Top 10 thanks to

• Concerted efforts to define and (automatically!) detect anti-patterns

• Better tooling to simplify code / limit human error

• Catch-all exploit mitigation technologies

• The simple fact that we don’t build web apps in C/C++ anymore!

Page 15: Making Web Development "Secure By Default"

To Review

Hypothesis: Buffer overflows fell off the OWASP Top 10 thanks to:

• Concerted efforts to define and (automatically!) detect anti-patterns

• Better tooling to simplify code / limit human error

• Catch-all exploit mitigation technologies

• The simple fact that we don’t build web apps in C/C++ anymore!

!

How can we apply these ideas to other classes of bugs?

Page 16: Making Web Development "Secure By Default"

2004: • Unvalidated Input

• Broken Access Control

• Broken Authentication and Session Management

• Cross Site Scripting

• Buffer Overflow

• Injection

• Improper Error Handling

• Insecure Storage

• Application Denial of Service

• Insecure Configuration Management

2013: • Injection

• Broken Authentication and Session Management

• Cross Site Scripting

• Insecure Direct Object References

• Security Misconfiguration

• Sensitive Data Exposure

• Missing Function Level Access Control

• Cross-Site Request Forgery

• Using Components with Known Vulnerabilities

• Unvalidated Redirects and Forwards

XSRF

Page 17: Making Web Development "Secure By Default"

XSRF Review

1. Alice logs into https://mybank.com, and gets back a session cookie:200 OKSet-Cookie: session-id=123-456789; path=/; domain=.mybank.com; Secure; HttpOnly;

2. Alice is tricked into opening https://evilsite.com, whose JavaScript code sends a POST to mybank.com: POST /transfer_fundsCookie: session-id=123-456789...destination=evil_account_number&amount=100000&currency=USD

Page 18: Making Web Development "Secure By Default"

1. https://mybank.com sends back another cookie with an “xsrf token”:200 OKSet-Cookie: session-id=123-456789; path=/; domain=.mybank.com; Secure; HttpOnly;Set-Cookie: _xsrf=SOMESECRETVALUE; path=/; domain=.mybank.com; Secure; HttpOnly;

2. On any page with a form, https://mybank.com includes the same token in an input field to be POST-ed: … <input type='hidden' name='_xsrf' value='SOMESECRETVALUE'> …

XSRF Tokens

Page 19: Making Web Development "Secure By Default"

3. https://mybank.com rejects any POST that without an XSRF token, or in which the token doesn’t match the Cookie

XSRF Tokens

Page 20: Making Web Development "Secure By Default"

XSRF Tokens

Elegant solution:

• Requires no new server-side state

• Can be added to most existing web applications with minor modifications

• “Secure by default”

Page 21: Making Web Development "Secure By Default"

2004: • Unvalidated Input

• Broken Access Control

• Broken Authentication and Session Management

• Cross Site Scripting

• Buffer Overflow

• Injection

• Improper Error Handling

• Insecure Storage

• Application Denial of Service

• Insecure Configuration Management

2013: • Injection

• Broken Authentication and Session Management

• Cross Site Scripting

• Insecure Direct Object References

• Security Misconfiguration

• Sensitive Data Exposure

• Missing Function Level Access Control

• Cross-Site Request Forgery

• Using Components with Known Vulnerabilities

• Unvalidated Redirects and Forwards

XSS

Page 22: Making Web Development "Secure By Default"

XSS - Review

{% autoescape None %} !<html> <body> <h1>Your Notes</h1> {% for row in rows %} <hr> <p> {{ row.content }} </p> {% end %} </body> </html>

Page 23: Making Web Development "Secure By Default"

Threats

• Annoy users (i.e. <script>alert(‘hi’)</script>)

• Steal any data in the DOM

• (Including XSRF tokens!)

• Phish users’ credentials, even if it wasn’t a login page!

XSS - Review

Page 24: Making Web Development "Secure By Default"

XSS - Escape All The Things

{% autoescape None %} !<html> <body> <h1>Your Notes</h1> {% for row in rows %} <hr> <p> {{ escape(row.content) }} </p> {% end %} </body> </html>

Page 25: Making Web Development "Secure By Default"

XSS - Autoescape

• Actually, Tornado does auto-escape by default (I had to disable it!)

• But, naive auto-escaping is not good enough!

Page 26: Making Web Development "Secure By Default"

Different Contexts

{% autoescape None %} !<html> <head> <title>Hello, World</title> <script> var qux = {{ json_encode(qux) }}; </script> </head> <body> <input type="hidden" name="foo" value="{{ escape_attr(foo) }}" /> <a href="/{{ url_escape(bar) }}">{{ escape(baz) }}</a> </body> </head>

Page 27: Making Web Development "Secure By Default"

Context-Aware Auto-Escaping

Basic idea: as you’re generating template output, feed it back through an HTML parser. When you hit a template directive, figure out what context you’re in, and call the appropriate escaping function!

Page 28: Making Web Development "Secure By Default"

Mitigation: Content-Security-Policy (CSP)

HTTP Header that will tell the browser from what sources it’s allowed to load (and in the case of scripts, execute) content.

• Content-Security-Policy: default-src ‘self' - load scripts/images/etc. only from the same domain (and do not run inline scripts or process inline CSS!)

• Content-Security-Policy: default-src 'self'; img-src * - same, except allow loading images from any host

For more, see: http://cspisawesome.com

Page 29: Making Web Development "Secure By Default"

Mitigation: Content-Security-Policy (CSP)

• Turns security vulnerabilities back into “ordinary bugs”…

• (… if your users are using supported browsers!)

• Eliminating inline scripts usually requires some restructuring

• but separating code, data, and presentation is a good pattern anyway, right? :)

Page 30: Making Web Development "Secure By Default"

2004: • Unvalidated Input

• Broken Access Control

• Broken Authentication and Session Management

• Cross Site Scripting

• Buffer Overflow

• Injection

• Improper Error Handling

• Insecure Storage

• Application Denial of Service

• Insecure Configuration Management

2013: • Injection

• Broken Authentication and Session Management

• Cross Site Scripting

• Insecure Direct Object References

• Security Misconfiguration

• Sensitive Data Exposure

• Missing Function Level Access Control

• Cross-Site Request Forgery

• Using Components with Known Vulnerabilities

• Unvalidated Redirects and Forwards

SQL Injection

Page 31: Making Web Development "Secure By Default"

SQL Injection - Review

class LoginHandler(tornado.web.RequestHandler): def post(self): user = self.get_argument('username') password = self.get_argument('password') ! pwhash = hashlib.sha1(password).hexdigest(); row = self.application.db.get( 'SELECT uid FROM users WHERE uname=\'%s\' AND password=\'%s\'' % (user, pwhash)) if row: self.set_secure_cookie('user', str(row.uid)) self.redirect('/')

Page 32: Making Web Development "Secure By Default"

SQL Injection - Review

class LoginHandler(tornado.web.RequestHandler): def post(self): user = self.get_argument('username') password = self.get_argument('password') ! pwhash = hashlib.sha1(password).hexdigest(); row = self.application.db.get( 'SELECT uid FROM users WHERE uname=\'%s\' AND password=\'%s\'' % (user, pwhash)) if row: self.set_secure_cookie('user', str(row.uid)) self.redirect('/') !!

(By the way, DO NOT store your passwords like this!)

Page 33: Making Web Development "Secure By Default"

Fun things to submit for ‘user’:

• akgood' OR '1' = '1

• akgood'; DROP TABLE users; SELECT …

• or just point a tool like sqlmap (http://sqlmap.org/) at it!

SQL Injection - Review

Page 34: Making Web Development "Secure By Default"

Parameterized Queries

class LoginHandler(tornado.web.RequestHandler): def post(self): user = self.get_argument('username') password = self.get_argument('password') ! pwhash = hashlib.sha1(password).hexdigest(); row = self.application.db.get( 'SELECT uid FROM users WHERE uname=%s AND password=%s', user, pwhash) if row: self.set_secure_cookie('user', str(row.uid)) self.redirect('/') !!

Can you see the difference?!

Page 35: Making Web Development "Secure By Default"

ORM

class LoginHandler(tornado.web.RequestHandler): def post(self): username = self.get_argument('username') password = self.get_argument('password') ! pwhash = hashlib.sha1(password).hexdigest(); rows = self.application.session.query( User).filter_by(uname=username, password=pwhash) if rows: row = rows[0] self.set_secure_cookie('user', str(row.uid)) self.redirect('/')

Page 36: Making Web Development "Secure By Default"

ORM Magic

from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String !Base = declarative_base() class User(Base): __tablename__ = 'users' ! uid = Column(Integer, primary_key=True) uname = Column(String) password = Column(String)

Page 37: Making Web Development "Secure By Default"

Middle Ground: SQL Expression API

class LoginHandler(tornado.web.RequestHandler): def post(self): username = self.get_argument('username') password = self.get_argument('password') ! pwhash = hashlib.sha1(password).hexdigest(); s = select([users]).where( (users.c.uname == username) & (users.c.password == pwhash)) rows = self.application.conn.execute(s) if rows: row = rows[0] self.set_secure_cookie('user', str(row['uid'])) self.redirect(‘/') !… !users = Table('users', meta, autoload=True, autoload_with=engine)

Page 38: Making Web Development "Secure By Default"

Static Analysis

If you really must write raw SQL:

• basic: a check to ensure that developers never use the string interpolation operator (‘%’) in a database function call

• better: dataflow analysis to trace the construction of a query string and ensure no untrusted inputs were used (a.k.a. ‘taint analysis’)

Page 39: Making Web Development "Secure By Default"

Static Analysis: Commercial Solutions

Powerful, but extremely expensive - e.g.:

• Veracode

• Coverity

• Fortify

Page 40: Making Web Development "Secure By Default"

Static Analysis: Homegrown Hacks

Example: make sure that we only ever use Python’s “SystemRandom” class to generate random valuesv1: basically, grep for instances of:

• ‘random\.\w+’ (other than ‘random.SystemRandom)

• ‘from random import .*’(other than ‘from random import SystemRandom)

v2: use the python AST

Page 41: Making Web Development "Secure By Default"

Abstract Syntax Tree

>>> import ast >>> m = ast.parse("from random import SystemRandom") >>> ast.dump(m) "Module(body=[ImportFrom(module='random', names=[alias(name='SystemRandom', asname=None)], level=0)])" >>> m.body[0].module ‘random' !>>> m2 = ast.parse("self.db.execute('SELECT * FROM users WHERE uname=%s' % (uname))") >>> ast.dump(m2) "Module(body=[Expr(value=Call(func=Attribute(value=Attribute(value=Name(id='self', ctx=Load()), attr='db', ctx=Load()), attr='execute', ctx=Load()), args=[BinOp(left=Str(s='SELECT * FROM users WHERE uname=%s'), op=Mod(), right=Name(id='uname', ctx=Load()))], keywords=[], starargs=None, kwargs=None))])"

Page 42: Making Web Development "Secure By Default"

Checking SystemRandom with the AST

class RandomVisitor(ast.NodeVisitor): def visit_Attribute(self, node): if (isinstance(node.value, ast.Name) and node.value.id == 'random' and node.attr != 'SystemRandom'): raise BadRandomGenerator(node.lineno) ! def visit_ImportFrom(self, node): if (node.module == 'random' and any(alias.name != 'SystemRandom' for alias in node.names)): raise BadRandomGenerator(node.lineno) !with open(some_python_module, 'r') as fp: m = ast.parse(fp.read()) RandomVisitor().visit(m)

Page 43: Making Web Development "Secure By Default"

• Use frameworks and tools that prevent entire classes of bugs by default - either by intentionally mitigating vulnerabilities or simply by encapsulating dangerous code so you don’t have to deal with it.

• If you see an anti-pattern, write a script to enforce it!

• Can be quite basic, especially if you pair it with peer code reviews and consistent coding norms

• Don’t forget about the rest of the SDL

Conclusions


Recommended