Slide 1

Slide 1 text

Making Web Development “Secure By Default” ! Adam Goodman 2014-05-31

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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;! }!

Slide 7

Slide 7 text

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;! }!

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Best Practices • “Deprecate Unsafe Functions” - no more strcpy, strcat, … • Training • Code reviews • Automated enforcement (framework changes, analysis tools, …)

Slide 10

Slide 10 text

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)

Slide 11

Slide 11 text

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)

Slide 12

Slide 12 text

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!).

Slide 13

Slide 13 text

(We’re Not There Quite Yet) http://xkcd.com/1354/

Slide 14

Slide 14 text

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!

Slide 15

Slide 15 text

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?

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

XSRF Review 1. Alice logs into https://mybank.com, and gets back a session cookie:
 
 200 OK
 Set-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_funds
 Cookie: session-id=123-456789
 ...
 destination=evil_account_number&amount=100000¤cy=USD

Slide 18

Slide 18 text

1. https://mybank.com sends back another cookie with an “xsrf token”:
 
 200 OK
 Set-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: … 
 … XSRF Tokens

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

XSRF Tokens Elegant solution: • Requires no new server-side state • Can be added to most existing web applications with minor modifications • “Secure by default”

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

XSS - Review {% autoescape None %} !

Your Notes

{% for row in rows %}

{{ row.content }}

{% end %}

Slide 23

Slide 23 text

Threats • Annoy users (i.e. alert(‘hi’)) • Steal any data in the DOM • (Including XSRF tokens!) • Phish users’ credentials, even if it wasn’t a login page! XSS - Review

Slide 24

Slide 24 text

XSS - Escape All The Things {% autoescape None %} !

Your Notes

{% for row in rows %}

{{ escape(row.content) }}

{% end %}

Slide 25

Slide 25 text

XSS - Autoescape • Actually, Tornado does auto-escape by default (I had to disable it!) • But, naive auto-escaping is not good enough!

Slide 26

Slide 26 text

Different Contexts {% autoescape None %} ! Hello, World var qux = {{ json_encode(qux) }}; {{ escape(baz) }}

Slide 27

Slide 27 text

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!

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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? :)

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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('/')

Slide 32

Slide 32 text

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!)

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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?!

Slide 35

Slide 35 text

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('/')

Slide 36

Slide 36 text

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)

Slide 37

Slide 37 text

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)


Slide 38

Slide 38 text

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’)

Slide 39

Slide 39 text

Static Analysis: Commercial Solutions Powerful, but extremely expensive - e.g.: • Veracode • Coverity • Fortify

Slide 40

Slide 40 text

Static Analysis: Homegrown Hacks Example: make sure that we only ever use Python’s “SystemRandom” class to generate random values
 
 v1: 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

Slide 41

Slide 41 text

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))])"

Slide 42

Slide 42 text

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)

Slide 43

Slide 43 text

• 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

Slide 44

Slide 44 text

Thanks! [email protected] @akgood