the other dell

Safe harbour

The Rails Tutorial has me working over typical log in and log out patterns. I’m finding it tough wrapping my head around it, so here’s some notes to try and wrestle the information into my head better.

My approach to learning this material is follow along with the tutorial, creating the application it describes, just as it suggests. Then I re-work the suggestions, chapter by chapter, on my Rails rebuild of Three Word Poems.

By the book

These first set of notes are from doing the tutorial exactly as described.

  • Users’ right to be logged in granted by their having entered an email address and password that pass user.authenticate.
  • Initially, log-in performed by adding user’s id to session. If there’s a :user_id value on session, then the user is logged in, if it’s not there, user is not logged in.
  • session data is, by default, an encrypted cookie. I think I read that other stores are available, such as database entries or server-side key-stores. Right now, I’m not sure how a server-side store would work without also using a client-side token or two.
  • “remember me” functionality likely based around storing values in cookies and a corresponding value in a database entry to match it against. Cookies generated by cookies are not enrypted. Cookies have 4 main vulnerabilities 1. stealing the cookie (usually using a packet sniffer) 2. gaining access to the database - if it public facing, form injection attacks, on-site access, credential theft through social hacking 3. cross-site scripting - somehow injecting JS hosted on a remote server into an active page which tracks keyboard entry or intercepts outgoing requests 4. physical access to specific client

Solution

  1. add remember_digest to user model (via a migration ). This will store a hash digest version of the remember token, similar to the way passwords are stored. The remember_digest is not indexed, as it is not used to look up users.
  2. add a method to create a remember token. This is an action only users (or their model) need to take, but the action does not require a user instance, and so the method should be a Class Method.
  3. need a slot to store said remember token. Add access_attr remember_token to User definition. This creates instance variable and accessors. I guess this allows remember_token to be queried (and set) outside the User object, but I’m not sure why instance methods defined later make use of the accessors rather than tapping @remember_token directly. I wonder if scope/self come into the picture here? If accessors are called on an object, can we guarantee self (which is impled in any property or method access) will always point to the instance?
  4. Add a remember method to User. This has two things worth noting: 1. the aforementioned self.remember_token, calling the remember_token= method on the instance to set it to a User.new_token. It’s the “around the houses” use of self and accessors I’m unsure of. A quick look at Stack Overflow suggests it’s largely a stylistic preference. I don’t know why I’m hung up on it. 2. Using update_attribute to set :remember_digest to a new value. update_method turns out to be a Rails method (on ActiveRecord::Base, maybe; I should look it up). “All it does” is set an attribute on the model. In theory, the attribute could be set by other means (I think self[:remember_digest] would work, or perhaps the accessor self.remember_digest = 'foo'). However, update_attribute bypasses model validations and updates the persistant record directly. Constrast this to the other means, which would update the in-memory object and then cause validation errors when attempting to save the model.
    1. Add an authenticated? method to User, taking remember_token. This contains the line BCrypt::Password.new(remember_digest).is_password?(remember_token). I find this confusing: * it seems to create a new “password” from the User’s saved remember token, which is a hashed digest (as per its name). It them calls is_password? on the passed-in remember_token, taken from cookies[:remember_token]. To me this is implying that one or other step is encrypting or decrypting, but it seems like whichever way ‘round I imagine the process, none are correct.
    2. The SessionsHelper gains a remember method, which requests user.remember (thereby storing a remember_digest), and then stores the user’s (encrypted) id and the (unencrypted) remember_token in cookies.

    … MISSED A FEW STEPS

    1. (?) I’m foggy over which functions are chosen to be in User and which in SessionsHelper. I feel I need to work through that more/again.

I did it my way

These notes are drawn from attempting to apply the chapter’s material to my own project.

  • “It’s convenient to model sessions as a RESTful resource” - my first thought is “why is it convenient?” - because sessions are objects created by or due to the way HTTP works. The client can ask the server to begin a session by making an HTTP request. - previously I had imagined “RESTful resource” meant “objects useful on the wider web”, like poems, or tweets, or maps etc. I had not thought that “resources” meant application-specific resources (users, sessions). On the face of it, I wouldn’t assume a “session” was a resource we would want to share publicly over the internet - it’s specific to our application. On reflection, we’re building an app that uses both definitions - we use resources which are both public (poems) and private (sessions). Does this suggest the client-side is a separate application from the server-side? Modern SPAs are largely assumed to be like that. In working through basic Rails tutorials, I was beginning to think Rails wants me to treat the two sides as the same application - with the client-side being more like a rendering of the server-side. - “Users” as an object are fuzzy. Many projects model their users with a user object, or there is an implied user. On one hand “users” are people - humans going about their lives; on the other “users” are a model within a system. There’s some subtleties buried here I can’t quickly unearth. I need to review this whole thing more phenomenologically to better reach the depths of it. - the “convenience” here is that Rails already provides us mechanisms for apprehending reqests, allowing us to package just the code we want for our application neatly away from boilerplate code common to most applications - controllers. It also provides several mechanisms for persisting data and exposes those to the controllers and other parts of the code. That is very convenient!
  • Database storage and cookies are both mechanisms for persisting data. I tend to think of databases as “heavy” and “important”, compared with the “light” and “transitory” cookies. Really, they’re both as “important” as one another, only with different properties suggesting they’re appropriate for different situations. They are both, simply, means of storing data.
  • Having accepted “sessions” can be modelled as RESTful resources, one can see that there’s a neat pairing of log actions to session controller actions: - capture credentials (login form): new session - begin session (log in): create session - log out: destroy session I imagined the poems application would work slightly differently from the Rails Tutorial App. The tutorial app has a stand-alone log-in page. I want 3WP to allow new content created without being logged in; but that content would be anonymous and “owned” by the website. Poets should be able to log in at any point and the poems created in that session should be then owned by that user. I like those combined log-in/register forms - cut down the number of tasks Poets need to do to achieve anything within the application. Leave the non-essential details ‘til later. The form to capture credentials should be available anywhere it’s needed - e.g. on the home page, on the “successfully added a poem page” - perhaps even on the poem contribution page too (although that seems a technical challenge). If I put the log-in form itself into a partial, I can use it in many places.
  • Getting stuck with fixtures and seed data. This might be the time to solve the passwords in the seeds.rb problem. - the test environment has a separate db from the dev environment - thus I should be able to run tests with arbitrary poets and poems - after some tinkering, it seems I can use arbitrary poets and poems - That’s great for tests… I’d still like a way to seed my development database in such a way as to have more secure code base. I’m not willing to solve this problem now.
  • Whilst copying out the updated current_user method - or current_poet as it is now - in the section about logging out, I forgot to capitalise the class name Poet, and spent waaay longer than I should have failing to work out why I was getting “poet is undefined” type messages.
  • Concerning that same piece of code. I don’t like how the current_poet method will also attempt to authenticate and log in a returning poet as a side effect of finding their poet_id in the cookies. I don’t feel competent at security issues to comment on the security of this (the tutorial does a decent job of making it seem OK). What I dislike is the very significant side-effect (logging in) the function performs. I want to refactor it, but I’m uncertain where the best place is. My first thought is a before_action callback in the application_controller, but this a) seems extreme and b) might not work. As I type this, I’m tired and don’t feel like exploring.