Python vs the OWASP Top 10

Python vs the OWASP Top 10

Given at HackerSchool.

2f5463832ccb768ccb4a1ca3607c27ef?s=128

Jacob Kaplan-Moss

April 24, 2013
Tweet

Transcript

  1. Building Secure Web Apps: Python versus the OWASP Top 10

    Jacob Kaplan-Moss jacob@jacobian.org
  2. The web is a scary place...

  3. None
  4. None
  5. None
  6. “Risk”

  7. Risk = Likelihood × Impact

  8. The OWASP Top 10 1. Injection 2. Broken authentication and

    session management 3. XSS 4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects https://www.owasp.org/index.php/Top_10_2013
  9. Injection

  10. http://xkcd.com/327

  11. db = psycopg2.connect() @app.route("/transactions/<user>") def show_transactions(user): c = db.cursor() query

    = "SELECT * FROM transactions WHERE user = '%s'" % username c.execute(query) return flask.render_template('txns.html', transactions=c.fetchall())
  12. None
  13. GET  /activity/jacob

  14. GET  /activity/jacob SELECT  *  FROM  activity  WHERE  user  =  'jacob'

  15. GET  /activity/jacob SELECT  *  FROM  activity  WHERE  user  =  'jacob'

    GET  /activity/%27+or+1+%3D+1
  16. GET  /activity/jacob SELECT  *  FROM  activity  WHERE  user  =  'jacob'

    GET  /activity/%27+or+1+%3D+1 SELECT  *  FROM  activity  WHERE  user  =  ''  or  1  =  1
  17. GET  /activity/jacob SELECT  *  FROM  activity  WHERE  user  =  'jacob'

    GET  /activity/%27+or+1+%3D+1 SELECT  *  FROM  activity  WHERE  user  =  ''  or  1  =  1 GET  /activity/%27%3B+DELETE+FROM+transactions%3B
  18. GET  /activity/jacob SELECT  *  FROM  activity  WHERE  user  =  'jacob'

    GET  /activity/%27+or+1+%3D+1 SELECT  *  FROM  activity  WHERE  user  =  ''  or  1  =  1 GET  /activity/%27%3B+DELETE+FROM+transactions%3B SELECT  *  FROM  activity  WHERE  user  =  '';  DELETE  FROM  activity;
  19. Preventing SQLi ‣ Use a database abstraction layer or ORM.

    ‣ Flask: SQLAlchemy ‣ Django: django.db.models ‣ If you must write raw SQL by hand, always use bind parameters: ‣ Good: cursor.execute(query, params) ‣ Bad: cursor.execute(query % params)
  20.  Broken authentication and session management

  21. Vulnerabilities in sessions/auth ‣ Insecure credential storage ‣ Weak account

    management functions (e.g. password recovery) ‣ Poor session security (stealing, forging, fixation, poisoning, etc.)
  22. Man-in-the-middle attacks

  23. SSL ALL THE THINGS! ‣ Flask: https://github.com/kennethreitz/flask-sslify ‣ Django: http://django-secure.rtfd.org/

  24. Session security ‣ Use your framework’s session’s APIs; don’t reinvent

    the wheel. ‣ Flask: ‣ Django: django.contrib.sessions ‣ Don’t store data in cookies directly. ‣ Watch your SECRET_KEY! ‣ Always consider session data insecure. Even if it’s stored in the database, it make have been set via a poisoned session.
  25. XSS (Cross-Site Scripting)

  26. @app.route("/sayhi/<user>") def sayhi(username): return "<h1>Hello, %s!</h1>" % username

  27. None
  28. GET  /sayhi/jacob

  29. GET  /sayhi/jacob <h1>Hello,  jacob!</h1>

  30. GET  /sayhi/jacob <h1>Hello,  jacob!</h1> GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E

  31. GET  /sayhi/jacob <h1>Hello,  jacob!</h1> GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E <h1>Hello,  <script>alert('hax0red')</script>!</h1>

  32. ‣ Use a template that automatically escapes HTML. ‣ No

    matter how smart you think you are, don’t turn it off! Preventing XSS
  33. @app.route("/sayhi/<user>") def sayhi(username): return flask.render_template("hello.html)", username=username) <h1>Hello, {{ username }}</h1>

    app.py hello.html
  34. None
  35. GET  /sayhi/jacob

  36. GET  /sayhi/jacob <h1>Hello,  jacob!</h1>

  37. GET  /sayhi/jacob <h1>Hello,  jacob!</h1> GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E

  38. GET  /sayhi/jacob <h1>Hello,  jacob!</h1> GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E <h1>Hello,  &lt;script&gt;alert(&quot;hax0red&quot;)&lt;/script&gt;!</h1>

  39.  Insecure direct object references

  40.  Insecure direct object references

  41.  Insecure direct object references Bad URLs

  42. class JobApplication(DeclarativeBase) __tablename__ = 'jobapps' id = Column(Integer, primary_key=True) ...

    @app.route("/jobs/application/<id>") def job_application(id): a = JobApplication.query.filter(JobApplication.id == id) return flask.render_template('application.html', application=a)
  43. Dear  Jacob  -­‐ Thanks  for  applying  to  work  at  Initech!

     If  you  need  to   make  changes  to  your  job  application,  you  can  do  so  at:        http://initech.com/jobs/application/6749 Good  luck, A.  Drone Director,  Human  Resources
  44. class JobApplication(DeclarativeBase) __tablename__ = 'jobapps' id = Column(Integer, primary_key=True) slug

    = Column(String(100)) ... @app.route("/jobs/application/<slug>") def job_application(id): a = JobApplication.query.filter(JobApplication.slug == slug) return flask.render_template('application.html', application=a)
  45. Dear  Jacob  -­‐ Thanks  for  applying  to  work  at  Initech!

     If  you  need  to  make   changes  to  your  job  application,  you  can  do  so  at:        http://example.com/jobs/application/jacobkaplanmoss-­‐2013 Good  luck, A.  Drone Director,  Human  Resources
  46.  Security misconfiguration

  47. No silver bullet ‣ Read the docs: ‣ Flask: http://flask.pocoo.org/docs/security/

    ‣ Django: http://django.me/security ‣ Have a deployment checklist: ‣ Django: http://django-secure.rtfd.org/ ‣ Don’t forget to turn debug mode off! ‣ Flask: app.debug = False ‣ Django: settings.py → DEBUG = False
  48.  Sensitive data exposure

  49. This  is  a  reminder,  sent  out  once  a  month,  about

     your list.example.com  mailing  list  memberships.    It  includes  your subscription  info  and  how  to  use  it  to  change  it  or  unsubscribe   from  a  list. ... Passwords  for  jacob@jacobian.org: List                                                            Password -­‐-­‐-­‐-­‐                                                            -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ mylist@list.example.com                      hunter2 otherlist@list.example.com                2hunter
  50. Password storage ‣ Use bcrypt. ‣ Flask: http://pythonhosted.org/passlib/ ‣ Django:

    http://django.me/bcrypt ‣ (PBKDF2 is an acceptable substitute, but use bcrypt anyway.) ‣ Properly hashed passwords are hard — but not impossible — to break. So keep them safe, and consider implementing password security rules.
  51.  Missing function-level access control

  52. @app.route("/jobs/application/<id>") def job_application(id): a = JobApplication.query.filter(JobApplication.id == id) return flask.render_template('application.html',

    application=a)
  53. from flask.ext.login import login_required, current_user @app.route("/jobs/application/<id>") @login_required def job_application(id): a

    = JobApplication.query.filter(JobApplication.id == id, JobApplication.applicant == current_user) return flask.render_template('application.html', application=a)
  54. ‣ Treat access control holistically, not piecemeal. ‣ Flask: https://flask-login.rtfd.org/

    ‣ Django: django.contrib.auth ‣ Don’t rely on security through obscurity. Handling access control
  55. CSRF (Cross-Site Request Forgery)

  56. <h1>Welcome, A. Random Stockholder</h1> <form action="/stocks/sell/" method="get"> <input type="submit" value="Sell

    Stock"> </form>
  57. <h1>Congratulations, you've just won an iPad!</h1> <a href="http://yoursever.com/stocks/sell/"> Click here

    to claim your free gift! </a>
  58. <h1>Welcome, A. Random Stockholder</h1> <form action="/stocks/sell/" method="post"> <input type="submit" value="Sell

    Stock"> </form>
  59. <h1>Congratulations, you've just won an iPad!</h1> <form action="http://yoursever.com/stocks/sell/" method="post"> <input

    type="submit"> Click here to claim your free gift! </input> </form>
  60. <h1>Welcome, A. Random Stockholder</h1> <form action="/stocks/sell/" method="post"> <input type="hidden" name="token"

    value="2b002b..."> <input type="submit" value="Sell Stock"> </form>
  61. ‣ Don’t allow GET requests to have side effects. ‣

    Protect POST requests with a CSRF token ‣ Flask: http://sjl.bitbucket.org/flask-csrf/ ‣ Flask: http://pythonhosted.org/Flask-WTF/ ‣ Django: (built in) ‣ Protect your SECRET_KEY! Preventing CSRF
  62.  Using components with known vulnerabilities

  63. $  pip  list  -­‐-­‐outdated Django  (Current:  1.5  Latest:  1.5.1) django-­‐contact-­‐form

     (Current:  0.3  Latest:  0.3.1) django-­‐secure  (Current:  0.1.2  Latest:  1.0) Fabric  (Current:  1.4.2  Latest:  1.6.0) psycopg2  (Current:  2.4.6  Latest:  2.5) Pygments  (Current:  1.5  Latest:  1.6) raven  (Current:  1.4.6  Latest:  3.3.3) simplejson  (Current:  2.5.2  Latest:  3.1.3) Sphinx  (Current:  1.1.3  Latest:  1.2b1) ssh  (Current:  1.7.14  Latest:  1.8.0) Unipath  (Current:  0.2.1  Latest:  1.0)
  64. ‣ [this is hard] ‣ Update dependencies often. ‣ A

    good test suite helps! ‣ Follow relevant mailing lists ‣ Flask: http://flask.pocoo.org/mailinglist/ ‣ Django: https://groups.google.com/group/django-announce ‣ https://bundlescout.com/ (non-free) Staying up-to-date
  65.  Unvalidated redirects and forwards

  66. None
  67. GET  /forgot-­‐my-­‐password/ Host:  good.com

  68. Dear  Jacob, You  can  reset  your  password  by  following  this

     link:  http://good.com/password/reset/a02be53ccf9245 Your  friends  at  good.com.
  69. GET  /forgot-­‐my-­‐password/ Host:  evil.com

  70. Dear  Jacob, You  can  reset  your  password  by  following  this

     link:  http://evil.com/password/reset/a02be53ccf9245 Your  friends  at  good.com.
  71. ‣ Whitelisting seems to be the only solid approach. ‣

    Django: ALLOWED_HOSTS ‣ Be very cautious when issuing redirects! ‣ Flask: flask.redirect is unsafe. ‣ Django: django.shortcuts.redirect is unsafe. Validating redirects/forwards
  72. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  73. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  74. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  75. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  76. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  77. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  78. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  79. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  80. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  81. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  82. 1. Injection 2. Broken authentication and session management 3. XSS

    4. Insecure direct object references 5. Security misconfiguration 6. Sensitive data exposure 7. Missing function-level access control 8. CSRF 9. Components with known vulnerabilities 10. Unvalidated redirects The Top 10 vs Flask/Django
  83. Thank you! jacob@jacobian.org http://speakerdeck.com/jacobian