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

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.

Joy Heron

July 21, 2017
Tweet

More Decks by Joy Heron

Other Decks in Technology

Transcript

  1. Simple AND Secure?
    EuroClojure 2017
    Joy Clark

    View Slide

  2. Caution: Code on Slides
    Oscar Sutton

    View Slide

  3. Simplicity
    Imani Clovis

    View Slide

  4. Clojure Ring
    Java HTTP Server
    Ring Server Adapter
    request
    response
    fn
    request
    response
    https://github.com/ring-clojure/ring/blob/master/SPEC

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  9. Where is the security?
    Scott Webb

    View Slide

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

    View Slide

  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

    View Slide

  12. We have to add the last piece!
    Hans‑Peter Gauster

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  17. XSS - Cross-site Scripting

    View Slide

  18. XSS - Cross-site Scripting
    (defn error-login [username]
    (str "Hi "
    username
    "! Please try logging in again!"))
    (error-login "alert('Evil XSS!');")
    > "Hi alert('Evil XSS!');!
    Please try logging in again!"

    View Slide

  19. Validate Input - Escape Output
    Ensure that the user's input is what you expect.
    Libraries:
    clojure.spec
    Bouncer

    View Slide

  20. Validate Input - Escape Output
    (selmer.parser/render "Hello {{name}}!"
    {:name "alert('hi!');"})
    > "Hello <script>alert('hi!');</script>!"

    View Slide

  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

    View Slide

  22. CSRF - Cross-site Request Forgery

    View Slide

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

    SameSite=Strict cookie attribute
    Ring-Anti-Forgery

    View Slide

  24. CSRF Protection

    View Slide

  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:

    value="{{antiforgery}}" />
    ...

    View Slide

  26. Injection
    (defn query-user [username]
    (sql/query db-conn
    [(str "SELECT * FROM Users WHERE Username = '"
    username "';")]))
    (query-user "fred' or 'a'='a")

    View Slide

  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 "[email protected]"
    :userid 4 :password "...some bycrypt hash..."}

    View Slide

  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?

    View Slide

  29. Introducing Buddy

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. Protect Your APIs!
    Rita Morais

    View Slide

  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", ...

    View Slide

  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

    View Slide

  37. Example Web Application:
    (WIP)
    https://github.com/innoq/quackers
    Joy Clark
    [email protected]
    @iamjoyclark

    View Slide