Slide 1

Slide 1 text

Building Secure Web Apps: Python versus the OWASP Top 10 Jacob Kaplan-Moss [email protected]

Slide 2

Slide 2 text

The web is a scary place...

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

“Risk”

Slide 7

Slide 7 text

Risk = Likelihood × Impact

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Injection

Slide 10

Slide 10 text

http://xkcd.com/327

Slide 11

Slide 11 text

db = psycopg2.connect() @app.route("/transactions/") 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())

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

GET  /activity/jacob

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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;

Slide 19

Slide 19 text

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)

Slide 20

Slide 20 text

 Broken authentication and session management

Slide 21

Slide 21 text

Vulnerabilities in sessions/auth ‣ Insecure credential storage ‣ Weak account management functions (e.g. password recovery) ‣ Poor session security (stealing, forging, fixation, poisoning, etc.)

Slide 22

Slide 22 text

Man-in-the-middle attacks

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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.

Slide 25

Slide 25 text

XSS (Cross-Site Scripting)

Slide 26

Slide 26 text

@app.route("/sayhi/") def sayhi(username): return "

Hello, %s!

" % username

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

GET  /sayhi/jacob

Slide 29

Slide 29 text

GET  /sayhi/jacob

Hello,  jacob!

Slide 30

Slide 30 text

GET  /sayhi/jacob

Hello,  jacob!

GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E

Slide 31

Slide 31 text

GET  /sayhi/jacob

Hello,  jacob!

GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E

Hello,  alert('hax0red')!

Slide 32

Slide 32 text

‣ Use a template that automatically escapes HTML. ‣ No matter how smart you think you are, don’t turn it off! Preventing XSS

Slide 33

Slide 33 text

@app.route("/sayhi/") def sayhi(username): return flask.render_template("hello.html)", username=username)

Hello, {{ username }}

app.py hello.html

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

GET  /sayhi/jacob

Slide 36

Slide 36 text

GET  /sayhi/jacob

Hello,  jacob!

Slide 37

Slide 37 text

GET  /sayhi/jacob

Hello,  jacob!

GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E

Slide 38

Slide 38 text

GET  /sayhi/jacob

Hello,  jacob!

GET  /sayhi/%3Cscript%3Ealert%28%27hax0red%27%29%3C%2Fscript%3E

Hello,  <script>alert("hax0red")</script>!

Slide 39

Slide 39 text

 Insecure direct object references

Slide 40

Slide 40 text

 Insecure direct object references

Slide 41

Slide 41 text

 Insecure direct object references Bad URLs

Slide 42

Slide 42 text

class JobApplication(DeclarativeBase) __tablename__ = 'jobapps' id = Column(Integer, primary_key=True) ... @app.route("/jobs/application/") def job_application(id): a = JobApplication.query.filter(JobApplication.id == id) return flask.render_template('application.html', application=a)

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

class JobApplication(DeclarativeBase) __tablename__ = 'jobapps' id = Column(Integer, primary_key=True) slug = Column(String(100)) ... @app.route("/jobs/application/") def job_application(id): a = JobApplication.query.filter(JobApplication.slug == slug) return flask.render_template('application.html', application=a)

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

 Security misconfiguration

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

 Sensitive data exposure

Slide 49

Slide 49 text

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  [email protected]: List                                                            Password -­‐-­‐-­‐-­‐                                                            -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ [email protected]                      hunter2 [email protected]                2hunter

Slide 50

Slide 50 text

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.

Slide 51

Slide 51 text

 Missing function-level access control

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

from flask.ext.login import login_required, current_user @app.route("/jobs/application/") @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)

Slide 54

Slide 54 text

‣ 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

Slide 55

Slide 55 text

CSRF (Cross-Site Request Forgery)

Slide 56

Slide 56 text

Welcome, A. Random Stockholder

Slide 57

Slide 57 text

Congratulations, you've just won an iPad!

Click here to claim your free gift!

Slide 58

Slide 58 text

Welcome, A. Random Stockholder

Slide 59

Slide 59 text

Congratulations, you've just won an iPad!

Click here to claim your free gift!

Slide 60

Slide 60 text

Welcome, A. Random Stockholder

Slide 61

Slide 61 text

‣ 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

Slide 62

Slide 62 text

 Using components with known vulnerabilities

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

‣ [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

Slide 65

Slide 65 text

 Unvalidated redirects and forwards

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Dear  Jacob, You  can  reset  your  password  by  following  this  link:  http://good.com/password/reset/a02be53ccf9245 Your  friends  at  good.com.

Slide 69

Slide 69 text

GET  /forgot-­‐my-­‐password/ Host:  evil.com

Slide 70

Slide 70 text

Dear  Jacob, You  can  reset  your  password  by  following  this  link:  http://evil.com/password/reset/a02be53ccf9245 Your  friends  at  good.com.

Slide 71

Slide 71 text

‣ 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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Thank you! [email protected] http://speakerdeck.com/jacobian