How to create cookie without quotes around value? - http

I need to create cookie with e-mail address as value - but when I try to - then I have result:
"someone#example.com"
but I would like to have:
someone#example.com
The cookie should be created without double quoted marks - because other application uses it in such format. How to force java to not to add double quoted? Java adds them because there is special char "at".
I create the cookie that way:
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
Cookie cookie = new Cookie("login", "someone#example.com");
cookie.setMaxAge(2592000);
cookie.setDomain("domain.com");
cookie.setVersion(1);
response.addCookie(cookie);
Thanks for any help.

It's indeed caused by the # sign. This is not allowed in version 0 cookies. The container will implicitly force it to become a version 1 cookie (which breaks in MSIE browsers). You'd like to URL-encode the cookie value on cookie's creation
Cookie cookie = new Cookie("login", URLEncoder.encode("someone#example.com", "UTF-8"));
cookie.setMaxAge(2592000);
cookie.setDomain("domain.com");
response.addCookie(cookie);
and URL-decode it on cookie reading
String value = URLDecoder.decode(cookie.getValue(), "UTF-8");
Note that you should for sure not explicitly set the cookie version to 1.
See also:
Why do cookie values with whitespace arrive at the client side with quotes?
Unrelated to the concrete problem, cookies are visible and manipulatable by the enduser or man-in-the-middle. Carrying the email address around in a cookie is a bad smell. What if the enduser changes it to a different address? Whatever functional requirement (remembering the login?) you thought to solve with carrying the email address around in a cookie should most likely be solved differently.
See also:
How do I keep a user logged into my site for months?

Related

Using Datazen in an iframe with external authentication

I was able to successfully use external authentication with datazen via HTTPWEBREQUEST from code-behind with VB.NET, but I am unclear how to use this with an iframe or even a div. I'm thinking maybe the authorization cookies/token isn't following the iframe around? The datazen starts to load correctly, but then it redirects back to the login page as if it's not being authenticated. Not sure how to do that part, this stuff is pretty new to me and any help would be greatly appreciated!!
Web page errors include:
-OPTIONS url send # jquery.min.js:19b.extend.ajax # jquery.min.js:19Viewer.Controls.List.ajax # Scripts?page=list:35Viewer.Controls.List.load # Scripts?page=list:35h.callback # Scripts?page=list:35
VM11664 about:srcdoc:1
XMLHttpRequest cannot load http://datazenserver.com/viewer/jsondata. Response for preflight has invalid HTTP status code 405Scripts?page=list:35
load(): Failed to load JSON data. V…r.C…s.List {version: "2.0", description: "KPI & dashboard list loader & controller", url: "/viewer/jsondata", index: "/viewer/", json: null…}(anonymous function) # Scripts?page=list:35c # jquery.min.js:4p.fireWith # jquery.min.js:4k # jquery.min.js:19r # jquery.min.js:19
Scripts?page=list:35
GET http://datazenserver.com/viewer/login 403 (Forbidden)(anonymous function) # Scripts?page=list:35c # jquery.min.js:4p.fireWith # jquery.min.js:4k # jquery.min.js:19r # jquery.min.js:19
' ''//////////////////////////////////
Dim myHttpWebRequest As HttpWebRequest = CType(WebRequest.Create("http://datazenserver.com/"), HttpWebRequest)
myHttpWebRequest.CookieContainer = New System.Net.CookieContainer()
Dim authInfo As String = Session("Email")
myHttpWebRequest.AllowAutoRedirect = False
myHttpWebRequest.Headers.Add("headerkey", authInfo)
myHttpWebRequest.Headers.Add("Access-Control-Allow-Origin", "*")
myHttpWebRequest.Headers.Add("Access-Control-Allow-Headers", "Accept, Content-Type, Origin")
myHttpWebRequest.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
Dim myHttpWebResponse As HttpWebResponse = CType(myHttpWebRequest.GetResponse(), HttpWebResponse)
Response.AppendHeader("Access-Control-Allow-Origin", "*")
' Create a new 'HttpWebRequest' Object to the mentioned URL.
' Assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable.
Dim streamResponse As Stream = myHttpWebResponse.GetResponseStream()
Dim streamRead As New StreamReader(streamResponse)
frame1.Page.Response.AppendHeader("Access-Control-Allow-Origin", "*")
frame1.Page.Response.AppendHeader("headerkey", authInfo)
frame1.Attributes("srcdoc") = "<head><base href='http://datazenserver.com/viewer/' target='_blank'/></head>" & streamRead.ReadToEnd()
You might have to do more of this client-side, and I don't know whether you'll be able to because of security concerns.
External authentication in Datazen looks something like this:
User-Agent | Proxy | Server
-------------------|----------------------|------------------------------------
1. /viewer/home --> 2. Append header --> 3. Check cookie (not present)
<-- 5. Forward <-- 4. Redirect to /viewer/login
6. /viewer/login --> 7. Append header --> 8. Append cookie
<-- 10. Forward <-- 9. Redirect to /viewer/home
11. /viewer/home --> 12. Append header --> 13. Check cookie (valid)
<-- 15. Forward <-- 14. Give content
16. .................. Whatever the user wanted ..........................
So even though you're working off a proxy with a header, you're still getting a cookie back that it uses.
Now, that's just context.
My guess, from your description of the symptoms, is that myHttpWebResponse should have a cookie set (DATAZEN_AUTH_TOKEN, I believe), but it's essentially getting thrown out--you aren't using it anywhere.
You would need to tell your browser client to append that cookie to any subsequent (iframe-based) requests to the domain of your Datazen server, but I don't believe that's possible due to security restrictions. I don't know a whole lot about CORS, though, so there might be a way to permit it.
I don't know whether there's any good way to do what you're looking to do here. At best, I can maybe think of a start to a hack that would work, but I can't even find a good way to make that work, and you really wouldn't want to go there.
Essentially, if you're looking to embed Datazen in an iframe, I would shy away from external authentication. I'd shy away from it regardless, but especially there.
But, if you're absolutely sure you need it over something like ADFS, you'll need some way to get that cookie into your iframe requests.
The only way I can think to make this work would be to put everything on the same domain:
www.example.com
datazen.example.com (which is probably your proxy)
You could then set a cookie from your response that stores some encrypted (and likely expiring) form of Session("Email"), and passes it back down in your html.
That makes your iframe relatively simple, because you can just tell it to load the viewer home. Something to the effect of:
<iframe src="//datazen.example.com/viewer/home"></iframe>
In your proxy, you'll detect the cookie set by your web server, decrypt the email token, ensure it isn't expired, then set a header on the subsequent request onto the Datazen server.
This could be simplified at a couple places, but this should hold as true as possible to your original implementation, as long as you can mess with DNS settings.
I suppose another version of this could involve passing a parameter to your proxy, and sharing some common encryption key. That would get you past having to be on the same domain.
So if you had something like:
var emailEncrypted = encrypt(Session("Email") + ":somesalt:" + DateTime.UtcNow.ToString("O"));
Then used whatever templating language you want to set your iframe up with:
<iframe src="//{{ customDomain }}/viewer/home?emailkey={{ emailEncrypted }}"></iframe>
Then your proxy detected that emailkey parameter, decrypted it, and checked for expiration, that could work.
Now you'd have a choice to make on how to handle this, because Datazen will give you a 302 to /viewer/login to get a cookie, and you need to make sure to pass the correct emailkey on through that.
What I would do, you could accept that emailkey parameter in your proxy, set a completely new cookie yourself, then watch for that cookie on subsequent requests.
Although at that point, it would probably be reasonable to switch your external authentication mode to just use cookies. That's probably a better version of this anyway, assuming this is the only place you use Datazen, and you'd be safe to change something so fundamental. That would substantially reduce your business logic.
But, you wouldn't have to. If you didn't want to change that, you could just check for the cookie, and turn it into a header.
You should do (1), but just for good measure, one thing I'm not sure on, is whether you can pass users directly to /viewer/login to get a cookie from Datazen. Normally you wouldn't, but it seems like you should be able to.
Assuming it works as expected, you could just swap that URL out for that. As far as I know (although I'd have to double-check this), the header is actually only necessary once, to set up the cookie. So if you did that, you should get the cookie, then not need the URL parameter anymore, so the forced navigation would be no concern.
You'll, of course, want to make sure you've got a good form of encryption there, and the expiration pattern is important. But you should be able to secure that if you do it right.
I ended up just grabbing the username and password fields and entering them in with javascript. But this piece helped me a ton. You have to make sure you set the
document.domain ='basedomain.com';
in javascript on both sites in order to access the iframe contents else you'll run into the cross-domain issues.

Cookie encoding in BASE64 cannot be sent correctly to server

I use BASE64 to encode GUID value and add them to cookie. For example, an ecoded guid value is vClFwpDbWE6JPUlnlBXMWg==. When the server sends response, it will add this cookie. I check with Chrome, this value is correctly received by the browser. But when the browser sends another request, the cookie value is changed to "vClFwpDbWE6JPUlnlBXMWg" from HttpRequestMessage's cookies, why some characters are removed?
I use WebAPI2, MVC5 with IIS7.5.
ASP.NET sees the '=' character in the cookie and assumes it's a multi-value cookie (see related question Storing multiple values in cookies).
Your best bet is to store the GUID in the cookie as-is, e.g., by using Guid.ToString() to turn the GUID into a hex string and new Guid(string) to turn the hex string back into a GUID. Alternatively, if you really need to condense it down to BASE64, consider using HttpServerUtility's UrlTokenEncode and UrlTokenDecode methods. Those methods use an encoding which is very similar to BASE64 but which doesn't use characters like '+' and '=' which are treated specially by ASP.NET.

asp.net and cookies special characters

Have found very interesting issue in asp.net with cookies:
when adding cookie with value like test&
using
HttpCookie cookie = new HttpCookie("test", "test&");
Response.Cookies.Add(cookie);
and then trying to retrieve value Request.Cookies["test"] trailing ampersand is lost. If it is not trailing it is not lost. In firebug or javascript data is correct so it is asp.net specific I think.
Of course mostly could say just use UrlEncode. But is it really necessary? Is there any list of disallowed charters for cookies (because I think it is smaller than for URLs)?
I have found similar topic but there is no & symbol in restricted list:
Allowed characters in cookies
The ampersand is not an allowed character in a cookie. It's necessary to encode the cookie data with the UrlEncode method.
System.Web.HttpUtility.UrlEncode(cookie);
See also these SO questions/answers:
Broken string in cookie after ampersand (javascript)
How do you use an Ampersand in an HTTPCookie in VB.NET?

Storing multiple values in cookies

I have very large website which uses a lot of cookies. There are approx. 14 different cookies are there. I have different cookies for each item. When a user surfs the site they will have 14 cookies in their browser. I do not want this.
I want a single cookie for my site that will have 14 items and I can add,edit and delete them. I tried many ways but I am not able to do this.
I need to put some run time cookies as well save the user name in cookie. After the user logs in I want to save their personal site address in it. Eventually I want both the user name and personal site address both. I want to save user name before and then when user goes to his personal site then i will store personal site name run time.
Does any one have an idea how I could do this?
Matthew beat me to it, but yes, see the ASP.NET Cookies Overview...
To write and read a single cookie with multiple key/values, it would look something like this:
HttpCookie cookie = new HttpCookie("mybigcookie");
cookie.Values.Add("name", name);
cookie.Values.Add("address", address);
//get the values out
string name = Request.Cookies["mybigcookie"]["name"];
string address = Request.Cookies["mybigcookie"]["address"];
There is a section in the ASP.NET Cookies Overview that discusses how to implement multiple name-value pairs (called subkeys) in a single cookie. I think this is what you mean.
The example from that page, in C#:
Response.Cookies["userInfo"]["userName"] = "patrick"; //userInfo is the cookie, userName is the subkey
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString(); //now lastVisit is the subkey
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);
HttpCookie aCookie = new HttpCookie("userInfo");
aCookie.Values["userName"] = "patrick";
aCookie.Values["lastVisit"] = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);
EDIT: From the Cookies Overview (emphasis added):
Modifying and Deleting Cookies:
You
cannot directly modify a cookie.
Instead, changing a cookie consists of
creating a new cookie with new values
and then sending the cookie to the
browser to overwrite the old version
on the client.
Modifying and Deleting Cookies: You cannot directly modify a cookie. Instead, changing a cookie consists of creating a new cookie with new values and then sending the cookie to the browser to overwrite the old version on the client.

Why do cookie values with whitespace arrive at the client side with quotes?

I'm a .NET developer starting to dabble in Java.
In .NET, I can set the value of a cookie to a string with white space in it:
new HttpCookie("myCookieName", "my value") - and when I read that value on the client side (JavaScript), I get the value I expected (my value).
If I do the same thing in a Java servlet - new Cookie("myCookieName", "my value"), I get the value including the double quotes ("my value").
Why the difference? Am I missing something? How do people handle this in the Java world? Do you encode the value and then you decode on the client side?
When you set a cookie value with one of the following values as mentioned in Cookie#setValue(),
With Version 0 cookies, values should not contain white space, brackets, parentheses, equals signs, commas, double quotes, slashes, question marks, at signs, colons, and semicolons. Empty values may not behave the same way on all browsers.
then the average container will implicitly set the cookie to version 1 (RFC 2109 spec) instead of the default version 0 (Netscape spec). The behaviour is not specified by the Servlet API, the container is free to implement it (it may for example throw some IllegalArgumentException). As far as I know, Tomcat, JBoss AS and Glassfish behave all the same with regard to implicitly changing the cookie version. For at least Tomcat and JBoss AS this is the consequence of fixes for this security issue.
A version 1 cookie look like this:
name="value with spaces";Max-Age=3600;Path=/;Version=1
while a version 0 compatible cookie look like this:
name=value%20with%20spaces;Expires=Mon, 29-Aug-2011 14:30:00 GMT;Path=/
(note that an URL-encoded value is valid for version 0)
Important note is that Microsoft Internet Explorer doesn't support version 1 cookies. Even not the current IE 11 release. It'll interpret the quotes being part of the whole cookie value and will treat and return that accordingly. It does not support the Max-Age attribute and it'll ignore it altogether which causes that the cookie's lifetime defaults to the browser session. You was apparently using IE to test the cookie handling of your webapp.
To support MSIE as well, you really need to URL-encode and URL-decode the cookie value yourself if it contains possibly characters which are invalid for version 0.
Cookie cookie = new Cookie(name, URLEncoder.encode(value, "UTF-8"));
// ...
and
String value = URLDecoder.decode(cookie.getValue(), "UTF-8"));
// ...
In order to support version 1 cookies for the worldwide audience, you'll really wait for Microsoft to fix the lack of MSIE support and that the browser with the fix has become mainstream. In other words, it'll take ages (update: as of now, 5+ years later, it doesn't seem to ever going to happen). In the meanwhile you'd best stick to version 0 compatible cookies.
As far as I know, spaces must be encoded in cookies. Different browsers react differently to un-encoded cookies. You should URL-encode your cookie before setting it.
String cookieval = "my value";
String cookieenc = URLEncoder.encode(cookieval, "UTF-8");
res.addCookie(new Cookie("myCookieName", cookieenc));
ASP.NET does the encoding automatically, in Java you have to do it yourself. I suspect the quotes you see are added by the user agent.
It probably has to do with the way Java encodes the cookie. I suggest you try calling setVersion(1) on the new cookie and see if that works for you.
Try using setVersion(0).
HttpCookie cookie = new HttpCookie("name", "multi word value");
System.out.println(cookie.toString());
prints:
name="several word value"
But after setting
cookie.setVersion(0);
System.out.println(cookie.toString());
prints:
name=several word value
Encoding is a good idea too, but the quotes around the value look to be an independent issue.

Resources