Simple AND Secure?

Simple AND Secure?

Clojure is great for programming simple, elegant web applications. But is it possible to actually maintain a simple Clojure application without sacrificing web security? We may not be security experts, but we still need to protect our application against common attacks and take care of user authentication and authorization. Security should not be an afterthought but rather a fundamental part of the application architecture. This talk will cover basic principles to follow and introduce the main libraries available in the Clojure world for developing secure web applications.

Aa3555d7ac4b01847ec9f60a930f5ccd?s=128

Joy Heron

July 21, 2017
Tweet

Transcript

  1. Simple AND Secure? EuroClojure 2017 Joy Clark

  2. Caution: Code on Slides Oscar Sutton

  3. Simplicity Imani Clovis

  4. Clojure Ring Java HTTP Server Ring Server Adapter request response

    fn request response https://github.com/ring-clojure/ring/blob/master/SPEC
  5. Ring Requests & Responses (def request {:uri "/" :request-method :get

    :headers {"Accept" "text/plain"}}) (defn example-app [req] {:status 200 :body (str "Hello " (:uri req) "!")}) (ring.adapter.jetty/run-jetty example-app {:port 4000}) webapp Java HTTP Server Ring Server Adapter
  6. Ring Middleware modi ed request (i.e. added session info) modi

    ed response (i.e. added headers) request response middleware modi ed request modi ed response response modi ed request response request condition? true false (defn middleware [handler] (fn [request] ;; Modify request before sending it to the web app (let [response (handler request)] ;; modify response before returning it to the user response)))
  7. Routing with Compojure routing fn route 1 route 2 route

    3 not-found request response (defroutes app-routes (GET "/" request (list-users request)) (POST "/" {params :params :as request} (add-user request params)) (GET "/:username" [username :as request] (get-user request username)) (PUT "/:username" [username email password :as request] (update-user request username email password)) (DELETE "/:username" [username] (delete-user username)))
  8. Putting it all together (def webapp (-> app-routes auth-middleware (wrap-defaults

    secure-site-defaults))) (ring.adapter.jetty/run-jetty webapp {:port 4000}) auth? handle no auth ring secure defaults GET /:user POST / GET / PUT /:user DELETE /:user not-found list users add user get user update user delete user handle not found Java HTTP Server Ring Server Adapter
  9. Where is the security? Scott Webb

  10. Security ≠ Magical Frameworks (public-domain Vasnetsov - belygorod.ru)

  11. Where is the security? auth? handle no auth ring secure

    defaults GET /:user POST / GET / PUT /:user DELETE /:user not-found list users add user get user update user delete user handle not found Java HTTP Server Ring Server Adapter
  12. We have to add the last piece! Hans‑Peter Gauster

  13. Security = Teamwork + Review (cc-by Kevin Dooley - flic.kr/p/dxCnzT)

  14. How to Write a Secure Web Application Maintain your application

    Stay informed! Register for Security Advisories KISS Know what you are doing Monitor your Application
  15. OWASP Top 10 2017 Injection Broken Authentication / Session Management

    XSS Broken Access Control Security Misconfigurations Sensitive Data Exposure Insufficient Attack Protection Cross Site Request Forgery Using Components with Known Vulnerabilities Underprotected APIs
  16. HTTPS Just a friendly reminder to use SSL! (def options

    {:port 3000 :join? false :ssl? true :ssl-port 4000 :keystore "ssl/keystore" :key-password "somesecret" :host "example.com"}) (ring.adapter.jetty/run-jetty webapp options)
  17. XSS - Cross-site Scripting

  18. XSS - Cross-site Scripting (defn error-login [username] (str "<div>Hi "

    username "! Please try logging in again!</div>")) (error-login "<script>alert('Evil XSS!');</script>") > "<div>Hi <script>alert('Evil XSS!');</script>! Please try logging in again!</div>"
  19. Validate Input - Escape Output Ensure that the user's input

    is what you expect. Libraries: clojure.spec Bouncer
  20. Validate Input - Escape Output (selmer.parser/render "Hello {{name}}!" {:name "<script>alert('hi!');</script>"})

    > "Hello &lt;script&gt;alert(&#39;hi!&#39;);&lt;/script&gt;!"
  21. Validate Input - Escape Output Templating Language Escaping by default?

    # GitHub References Last Updated >2.0.0-alpha1 19481 15 Jan 2017 Yes 7412 21 Sep 2015 Yes 6465 28 Jun 2017 Yes 1177 17 Jun 2017 hiccup enlive Selmer hoplon Statistics taken on 17 July 2017
  22. CSRF - Cross-site Request Forgery

  23. Protect Against CSRF Don't use GET requests to change state!

    <img src="some http get request"> SameSite=Strict cookie attribute Ring-Anti-Forgery
  24. CSRF Protection

  25. ring.middleware.anti-forgery (require '[ring.middleware.anti-forgery :refer [*anti-forgery-token*]]) (selmer.parser/render-file "index.html" {:antiforgery *anti-forgery-token*}) index.html:

    <form ...> <input name="__anti-forgery-token" type="hidden" value="{{antiforgery}}" /> ... </form>
  26. Injection (defn query-user [username] (sql/query db-conn [(str "SELECT * FROM

    Users WHERE Username = '" username "';")])) (query-user "fred' or 'a'='a")
  27. Parameterize Queries db/users.sql: -- name: get-user SELECT * FROM Users

    WHERE Username = :username ; (yesql.core/defqueries "db/users.sql" {:connection db-conn}) (get-user {:username "fred"}) > {:username "fred" :email "mr.froggy@quackers.com" :userid 4 :password "...some bycrypt hash..."}
  28. Authentication & Authorization How do I know if the user

    is who he says he is? How do I know if the user is allowed to access a resource?
  29. Introducing Buddy

  30. Buddy-Auth Ring Middleware Integration Authentication information saved in :identity in

    the request map Different Backends: HTTP Basic, Session, Token, Signed JWT, Encrypted JWT Possibility to implement custom authentication backend
  31. Simple Auth Example (require '[buddy.auth.backends :as backends]) (defn ldap-auth [request

    auth] (let [username (:username auth) password (:password auth)] ; do magic ldap authentiation ; return logical true username)) (def backend (backends/basic {:realm "myAPI" :authfn ldap-auth}))
  32. Authorization with Buddy (def access-rules [{:uris ["/users" "/users/:username"] :handler buddy.auth/authenticated?

    :request-method :get} {:uris ["/users/:username" "/users/:username/edit"] :handler is-user?}]) (defn is-user? [request] (when-let [user (:identity request)] (= user (get-in request [:match-params :username]))))
  33. Auth Middleware (defn error-redirect [request _value] (redirect-to-login (request-url request))) (defn

    auth-middleware [handler] (let [backend (auth-backend secret)] (-> handler (wrap-access-rules {:rules access-rules :on-error error-redirect}) (wrap-authentication backend))))
  34. Protect Your APIs! Rita Morais

  35. HTTP Security Headers (def middleware-settings {:session {:cookie-attrs {:http-only true :secure

    true :same-site :strict}} ;; not default :security {:anti-forgery true :xss-protection {:enable? true, :mode :block} :frame-options :sameorigin :content-type-options :nosniff :ssl-redirect true :hsts true}}) "api-defaults", "site-defaults", "secure-site- defaults", ...
  36. In Summary... Use HTTPS! Validate user input and escape output

    Templating library with automatic HTML escaping!!! Use site-defaults or secure-site- defaults for for authentication and authorization ring defaults Buddy
  37. Example Web Application: (WIP) https://github.com/innoq/quackers Joy Clark joy.clark@innoq.com @iamjoyclark