$30 off During Our Annual Pro Sale. View Details »

Making Web Development "Secure By Default"

Making Web Development "Secure By Default"

Adam Goodman, Principal Security Architect at Duo Security

Duo Security

June 02, 2014
Tweet

More Decks by Duo Security

Other Decks in Technology

Transcript

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

    View Slide

  2. 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

    View Slide

  3. 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

    View Slide

  4. View Slide

  5. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  15. 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?

    View Slide

  16. 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

    View Slide

  17. 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&currency=USD

    View Slide

  18. 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

    View Slide

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

    View Slide

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

    View Slide

  21. 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

    View Slide

  22. XSS - Review
    {% autoescape None %}
    !


    Your Notes
    {% for row in rows %}


    {{ row.content }}

    {% end %}


    View Slide

  23. 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

    View Slide

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


    Your Notes
    {% for row in rows %}


    {{ escape(row.content) }}

    {% end %}


    View Slide

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

    View Slide

  26. Different Contexts
    {% autoescape None %}
    !


    Hello, World
    <br/>var qux = {{ json_encode(qux) }};<br/>



    {{ escape(baz) }}


    View Slide

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

    View Slide

  28. 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

    View Slide

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

    View Slide

  30. 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

    View Slide

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

    View Slide

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

    View Slide

  33. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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


    View Slide

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

    View Slide

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

    View Slide

  40. 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

    View Slide

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

    View Slide

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

    View Slide

  43. • 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

    View Slide

  44. Thanks!
    [email protected]
    @akgood

    View Slide