We are using Spring webflow + ThymeLeaf and trying to access session.getAttribute() in html page.
Bit new to Thymeleaf and I understand Thymeleaf has 2 ways to address session viz. ${session.something} and ${#session.getAttribute('something')}.
Code we are using is something like below which fails occasionally.
<div th:if="${(#session.getAttribute('booleanAttribute'))}">
...
</div>
In local environment I never see the failures, and it works as expected. In production this fails appox. 200 times in 30 minutes with following error -
org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "(#session.getAttribute('booleanAttribute'))" (template: "base" - line 80, col 10)
I am little reluctant to place null checks to see if (#session) is null or not without understanding why working locally fine. So I have this question -
What might be wrong above and how can I reproduce locally so that I can confirm fix I am placing will work across all environments?
According to the docs:
#session : direct access to the javax.servlet.http.HttpSession object associated with the current request.
In my testing, #session is null when the session expires. In cases where a user has their session expired, using #session would throw a null point exception (Method call: Attempted to call method getAttribute(java.lang.String) on null context object). You should be able to test this by deleting your JSESSIONID cookie.
${session} on the other hand is a SessionAttributesMap, which appears to never be null -- even when there isn't a valid session. In that case, the expression ${session.booleanAttribute} would still work, just evaluate to false.
Related
I have an exception handler (registered with rescue_from) which sometimes causes Double Render errors because the real action has already rendered/redirected before the exception is thrown.
To prevent this exception, I'm looking for the modern equivalent of erase_results. It cleared any rendering/redirecting activity in the current request. Not sure why it was deprecated as it seems useful. Anyway, I've tried to reconstruct it by digging into the original source, but some of the detail has changed too, so it would be a hack and I'd rather do it cleanly.
Rails uses controller#response_body to decide if the request is already rendered or redirected. Just set response_body to nil to avoid this error. Note: arguably better way is to prevent multiple rendering or redirecting at first place.
See: #render
Occasionally this code returns a null user object (line 1), even though the springSecurityService.principal.id is a valid id. Line 2 throws a null pointer exception because the user object is null. Most of the time the code works, but I can't figure out what causes the null load.
User user = User.load(springSecurityService.principal.id)
def contactInstance = BasicProfile.findByUser(user)
Attached is a screenshot of the user object after the call in line1.
Could it be due to a timeout in the session? And if so why wouldn't the controller get redirected to the login page if that were the case?
I ran into a similar issue with Tomcat caching some data incorrectly.
Here is what I did:
Stopped Tomcat
Deleted all the files in Tomcat's Temp directory
Restarted Tomcat
After that, it worked fine.
Let me know if this helps.
Background
I'm an experienced web developer (mostly with Python and CherryPy) who has implemented secure session management from scratch before, and is now learning Rails. I'm investigating the behavior of Rails sessions as exposed by the session object that is available in the ActionController instance and view contexts.
Question/Problem
I have read that the default implementation of sessions in Rails 4 uses an encrypted and tamper-proof cookie. Cool, I guess that means I can use it to hold a user ID for user sessions without worrying about session forging (tamper-proof) or anyone being able to find out what their ID is (encrypted). I wanted to test this and see what rails would do if the session cookie was altered.
So, I went and altered the content of the session cookie attribute using a browser add-on, and when I reload the page with the new cookie value, Rails just happily gives me different new values for session_id and _csrf_token.
What happened to session cookie integrity!?
Shouldn't rails detect (via HMAC signature) that the cookie was altered and then tell me about it somehow?
I'm terrified that I'm missing something obscenely obvious, but I've been having no luck searching for an answer on the web, and the source code isn't giving it up easily either (I'm new to ruby). Thanks in advance.
The Experiment
I created a new app and generated a controller with an index action:
$ rails new my_app
$ cd my_app; rails g controller home index
Then I added these two lines to the app/views/layouts/application.html.erb file:
<%= session.keys %><br/>
<%= session.values %>
I started up the dev server and navigated my browser to "localhost:3000/home/index". As expected, the page has the following lines at the bottom:
["session_id", "_csrf_token"]
["8c1558cabe6c86cfb37d6191f2e03bf8", "S8i8/++8t6v8W8RMeyvnNu3Pjvj+KkMo2UEcm1oVVZg="]
Reloading the page gives me the same values, although the app sets a new value of the _my_app_session cookie attribute every time. That seems weird to me, but I'm getting the same session hash values, so I guess it's cool.
Then, I used a cookie editing add-on for Chrome to alter the value of the _my_app_session cookie attribute (replacing the first character of the attribute value). Reloading the page shows completely different values without anything happening. WAT?
I can't claim a really thorough understanding of the code here. But I can tell you this much:
I followed your steps exactly (using Ruby 2.0.0-p247 & Rails 4.0), with one exception -- I also added the 'byebug' gem to my Gemfile and inserted a debugging breakpoint in the HomeController#index action.
From the byebug console, at that breakpoint, I could see the unedited session cookie via:
(byebug) cookies["_my_app_session"]
"cmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"
And I could see the actual encrypted values with
(byebug) cookies.encrypted["_my_app_session"]
{"session_id"=>"13a95fb545a1e3a2d4e9b4c22debc260", "_csrf_token"=>"FXb8pZgmoK0ui0qCW8W75t3sN2KLRpkiFBmLbHSfnhc="}
Now, I edit the cookie by changing the first letter to "A" and refresh the page:
(byebug) cookies["_my_app_session"]
"AmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"
(byebug) cookies.encrypted["_my_app_session"]
nil
So the session is nil at this point in the request:
(byebug) session
#<ActionDispatch::Request::Session:0x7ff41ace4bc0 not yet loaded>
I can force loading the session with
(byebug) session.send(:load!)
and when I do, I see that the resulting session id is
"f6be13fd646962de676985ec9bb4a8d3"
and sure enough, when I let the request finish, that's what I see in the view:
["session_id", "_csrf_token"] ["f6be13fd646962de676985ec9bb4a8d3", "qJ/aHzovZYpbrelGpRFec/cNlJyWjonXDoOMlDHbWzg="]
I also have a new cookie value now, unrelated to the one I edited.
So from this I think we can conclude is that what's happening is that since the cookie signature could not be verified, the session was nullified and regenerated. I now have a new session, with a different csrf_token.
The relevant code appears at actionpack/lib/action_dispatch/middleware/cookies.rb:460-464, in the EncryptedCookieJar class:
def decrypt_and_verify(encrypted_message)
#encryptor.decrypt_and_verify(encrypted_message)
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
nil
end
Rather than decrypting a message with an invalid signature, we just treat it as nil. So the unverifiable cookie that stores the session id and csrf token is not used to load the session, and anything that depends on the values in the cookie will fail.
So why didn't we get an error rather than just a new session? That's because we didn't try anything that depends on the encrypted values. In particular, although we have
protect_from_forgery with: :exception
(as opposed to :null_session) in ApplicationController, Rails does not verify the csrf token on GET or HEAD requests -- it relies on the developer to implement these actions according to spec, so that they're non-destructive. If you tried the same thing on a POST request, you'd get an ActionController::InvalidAuthenticityToken error (as you can easily verify for yourself).
I am noticing a pattern of rails acting as if a line of code is still written once it has been deleted, and I think it may have something to do with changing its defaults too much. I have two examples.
In the first, I set config.force_ssl => true in my config file (a major mistake) and immediately got an error on a page where I was introducing an api via a script tag:
My server gave me an error because the response length of the input wasn't known. I tried enabling streaming in my controller, and it failed. I even tried setting config.force_ssl => false, but this too was useless. So, I deleted the config.force_ssl => true line, but in Firefox, the page with the error continued to route to an "https://" url and then give me the same error. Chromium did not, so I switched to using that, but to this day, I still cannot load the page in Firefox without an error.
Now for the second issue. More recently, I created a model where I wanted to create a custom initialize method with four parameters.
association.rb
def initialize(tag_index, relative_index, type, relevance)
#assigning variables
end
In my controller, I assigned these accordingly.
tags_controller.rb
a = Association.new(id, tag_two.id, type, relevance)
Immediately, I get an error that I have 4 for 2 parameters. Thinking it's just rails being picky, I take away "type" and "relevance." Now, though, I get an error message telling me there is no method 'check_validity!' for class 30:Fixnum. So, I remove the initialize function altogether, and just as before, Rails refuses to recognize that the lines of code have been deleted, giving me errors when I enter parameters for Association.new, and telling me I'm missing parameters when I don't enter any at all.
If anyone can help with the little pieces such as how to fix a response length error with ssl, or how to deal with the 'check_validity!' method, that would be great. Better, though, would be if someone could explain why Rails refuses to let old pieces of code be deleted. This is something that has frustrated me to no end, and I can't find anything on any of these forums about how to fix it.
Thanks so much!
I am trying to make use of session data in my application and for some reason I don't have something setup right.
The code:
session[:key] = some_value
Generates the following error:
The error occurred while evaluating nil.[]
Other controllers don't have an issue with the session, so I am guessing I missed some basic configuration thing somewhere.
Ok, I think I got it figured out now. I had a slightly more complex situation that my example. I actually had the following:
session[:chat_history][chat.from.id] ||= []
So I had an error with double array. I added the following:
session[:chat_history] ||= []
Problem was the first time I did this, I put it in a before_filter method. Apparently the session object is nil in the before_filter method, at least the way I have my application setup.
So I moved the initializer to the methods that actually access the session and life is good again.
It looks like the session variable is nil which makes me think the framework couldn't set it for one of these reasons:
Browser passed in no cookie for the session
Browser passed in a cookie but it didn't match anything the server expected
It was stated that some controllers work. Did something have the opportunity to create a session for the user before those controllers ran?