"Secure" setting is preventing cookie from being set - cookies

I am fairly new to cookies and had been tasked with figuring out why a cookie warning kept popping up even after the user clicked accept. It turned out that the cookie wasn't being set in some browsers (mostly Webkit, but also one time in Firefox).
After some exploring I discovered that by removing the setting "Secure" in the cookie string I could make it work as intended, but I don't have enough understanding of cookies to determine why that would prevent the cookie from being set in the first place. Can anyone explain?
This is my cookie for reference:
acceptCookies() {
let expiry = new Date();
let months = 1;
let d = expiry.getDate();
expiry.setMonth( expiry.getMonth() + months );
if ( expiry.getDate() !== d ) {
expiry.setDate( 0 );
}
document.cookie = 'acceptsCookies=1 expires=' + expiry.toUTCString() + '; SameSite=lax; Secure';
}

Answering my own question: It turned out that the cookie was being set on https, but not on http, which makes sense since the "Secure" setting only allows it to be sent over HTTPS.
More info here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies

Related

400 Bad Request Request Header Or Cookie Too Large using Sustainsys.Saml2

I'm getting a browser error when using SustainSys.Saml2 library with my app:
400 Bad Request
Request Header Or Cookie Too Large
nginx/1.14.0
I think that reducing my cookie size might help and I only really need the email from the claim data, so I thought that if I could just save the email claim and remove the other claims, that it might reduce my cookie size and fix this error.
I read the response to a similar question (SustainSys.Saml2 Request length header too long) and looked for some information on how to implement AcsCommandResultCreated to remove unused claims (and hopefully reduce cookie size). I didn't find a lot of documentation, but did piece together some ideas and code to try and take a stab at it.
I've tried this code in my global.asax as well as in a controller action (that I made the "returnUrl" after Saml2/Acs). It doesn't look like my FedAuth cookie (set by Saml2/Acs) is any smaller. Any comments or suggestions? Thank you.
// Check if email claim exists
var principal = ClaimsPrincipal.Current;
var userEmail = principal.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.Email)?.Value;
// Create new command result that only contains the email claim
if (userEmail != null)
{
var emailClaim = principal.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.Email);
Sustainsys.Saml2.Configuration.Options.FromConfiguration.Notifications.AcsCommandResultCreated =
(commandResult, response) =>
{
var newCommandResult = new Sustainsys.Saml2.WebSso.CommandResult();
newCommandResult.Principal.Claims.Append(emailClaim);
commandResult = newCommandResult;
};
}
UPDATE:
It turned out that the test environment that I was using (which used nginx) needed to increase the request header buffer size. Adding these cookies increased the size to around 9500 bytes and nginx by default has a request header buffer size that is lower than that (I think 8000). Contacting the code owners of the test server running nginx, and increasing this solved my problem, without me having to reduce my cookie size.
Do you have a lot of failed authentication attempts? That can leave a lot of Saml2.XYZ correlation cookies around on the domain. Try checking the browser dev tools and clean those up.
The "headers too large" is usually something that happens when a user has tried signing in several times with a failure and those cookies get stuck. The real issue is usually something else - causing the authentication to fail and those correlation cookies to be accumulating.

AWS Cloudfront remove particular cookie before sending to origin

I want to remove particular cookie in aws cloudfront before sending to the origin server.
I have to send all cookies to origin except cookie named "_x_ad_zone".
I could not find any option to remove particular cookie in cloud front configuration. I believe that we have to achieve with lambda, but I have no clue how to do it.
Please let me know how can I achieve the same.
[EDIT]
Based on the answer, I wrote the following lambda#edge to solve my issue.
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const cookieName = '__a_x_id';
/*
* Lambda at the Edge headers are array objects.
*
* Client may send multiple Cookie headers, i.e.:
* > GET /viewerRes/test HTTP/1.1
* > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
* > Cookie: First=1; Second=2
* > Cookie: ClientCode=abc
* > Host: example.com
*
* You can access the first Cookie header at headers["cookie"][0].value
* and the second at headers["cookie"][1].value.
*
* Header values are not parsed. In the example above,
* headers["cookie"][0].value is equal to "First=1; Second=2"
*/
console.log(headers.cookie);
// Delete the cookie if found
if (headers.cookie) {
for (let i = 0; i < headers.cookie.length; i++) {
console.log(headers.cookie[i].value);
if (headers.cookie[i].value.indexOf(cookieName) >= 0) {
console.log('Adblocker cookie found and delete: '+headers.cookie[i].value);
headers.cookie[i].value = "0";
break;
}
}
request.headers = headers;
}
callback(null, request);
};
Quick disclaimer: When cookies are forwarded to the origin server, CloudFront caches the response against not only the URI and the headers (and the query string, if configured to do so), but also against the unique combination of cookie values (or missing cookies) presented by the browser -- so a response can only be served from cache when they contain (or are missing) exactly the same combination of cookies and values. This is not good for your cache hit rate, but of course, it also is completely correct design on the part of CloudFront -- if a different cookie is presented, CloudFront has no option but to assume that the cookie will potentially modify the response returned from the origin, so cookies must become components of the cache key.
It you must forward cookies, it's best to forward specific cookies.
However, CloudFront has a number of applications that are unrelated to caching, so there likely are valid use cases for a solution like this.
Your solution will only pass simplistic and optimistic tests. There are a number of edge cases it does not handle correctly. The example script for cookie manipulation is only a simple illustration, and includes something of a disclaimer to that effect:
* Header values are not parsed.
The first problem is that browsers are free to combine multiple cookies in a single Cookie: header, and your test of headers.cookie[i].value.indexOf(cookieName) will not only match a header with the cookie you want, it will match headers with that cookie plus others... and remove all the cookies in that particular header entry.
If used in a Viewer Request trigger, there's a significant risk of removing too many cookies with this solution. In an Origin Request trigger, it's even higher, since the cookies have already been stripped and re-canonicalized by the cookie forwarding configuration of the matched Cache Behavior, and CloudFront does combine multiple cookies on a single header row, at least under some conditions.
The second problem is related to the first: the simple string matching of indexOf() will match cookie values, as well as cookie names, so it's possible to get a false match on a cookie value -- which you don't want to be examining.
The third problem is that you're not really generating a valid replacement value. CloudFront seems to accept that, for now, but since it's technically invalid, there's a possibility that this could be "fixed" in the future.
I've written a Lambda#Edge script that I believe fully handles cookie semantics and will remove only and exactly the cookies you want to remove, and leave the data structures clean. Because I find this to be an interesting use case, I wrote it so that it will match as many cookies as you like -- not just one cookie -- with an exact-string, case-sensitive match, on the cookie name only.
The cookies are configured in an array near the top.
In your case, with a cookie named __a_x_id would look like this:
const discard = [ '__a_x_id' ];
Adding multiple cookie names to the array will block all of them.
This uses Node.js 6.10 and works with either a Viewer Request trigger or an Origin Request trigger. If you are doing any caching at all, you'll probably want to use it as an Origin Request trigger, since that means it fires less often.
I'm also pleased to report that in spite of looking a little complicated and doing a fair amount of string splitting and having multiple nested loops, this code has a Lambda execution time in a warm container of consistently less than 1 millisecond, whether or not any cookies are matched and removed.
'use strict';
// source: https://stackoverflow.com/a/45970883/1695906
// iterate through all Cookie: headers in a request trigger,
// removing any cookies on the "discard" list, while preserving
// the integrity of any other cookies, including those appearing on the same
// header line, and confirm the resulting "cookie" array to CloudFront
// requirements by removing any now-empty elements, or the entire array
// if no cookies remain
// configure with one or more cookies to be removed from all requests;
// cookie names are case-sensitive
const discard = [ 'grover', 'big_bird' ]; // friends of cookie monster
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
// does the request have any cookies? skip to the end, if not
if(headers.cookie)
{
const cookies = headers.cookie;
// iterate each Cookie: header, from last to first;
// last-to-first makes it simple to splice-out an array element
// because we then need not keep track of the reduced array length
for (var n = cookies.length; n--;)
{
// there may be multiple cookies per header line; examine them all
const cval = cookies[n].value.split(/;\ /);
const vlen = cval.length; // how many we started with
// check individual cookies on this line, backwards
for (var m = vlen; m--;)
{
// cookie name is to the left of "="
const cookie_kv = cval[m].split('=')[0];
// run though each member of "discard" array,
// removing the cookie if it's a match,
// again last to first but for no particular reason, here
for(var di = discard.length; di--;)
{
if(cookie_kv == discard[di])
{
cval.splice(m,1); // cookie removed!
break; // no need to check any other matches, already gone
}
}
} // for m
// if our array of cookies for this header line has now changed in size,
// we must have deleted some or all of it, so we need to reassemble
// what remains, or eliminate the entire line
if(cval.length != vlen)
{
if(cval.length === 0) // did we remove everything?
{
// yes? we can eliminate this entire line
cookies.splice(n,1);
}
else
{
// no? reassemble the remaining cookies
headers.cookie[n].value = cval.join('; ');
}
}
} // for n
// if the only cookies present in the request were cookies we removed,
// we now have a completely empty array in headers.cookie, which
// CloudFront should consider invalid; clean it up
if(cookies.length === 0)
{
delete headers.cookie;
}
}
// return control to CloudFront, possibly with our modified request
return callback(null, request);
};
For this you need to write an Lambda#Edge function to conditionally filter the cookie in CloudFront.
Check this example to get an insight on required operations. Also note that you need to alter the request header in origin request event, using Lambda#Edge.

cgi.SERVER_NAME reverts origin

I have two versions of a site, one for spanish, one for english. The spanish subdomain is set via IIS and a C Name (network admin told me, I'm not sure how or what that means), it's not a separate subdomain.
es.website.com
en.website.com
Now, when I use CGI.SERVER_NAME on my development server, everything works nicely. However, in production, when I'm on es.website.com, despite my Application.cfc settings, it thinks the origin is en.website.com, which throws off my <cfheader name="Access-Control-Allow-Origin" value="#application.site#">.
Here is how I differentiate the domains and sites to determine which content must be in spanish:
application.subdomain = ListFirst(cgi.SERVER_NAME, ".");
if (application.test) {
if (application.subdomain == "en") {
application.site = "http://en.dev.website.com/";
} else {
application.site = "http://es.dev.website.com/";
}
} else {
if (application.subdomain == "en") {
application.site = "http://en.website.com/";
} else {
application.site = "http://es.website.com/";
}
}
I cannot figure out why when on other pages, application.sites is clearly es.website.com, yet on some pages, the cgi.server_name reverts to en.website.com. Any insight?
If you are storing it in an application scoped variable then users can change the variable mid request. You don't see this on your dev server because you don't have any concurrent users.
Assume you have a request to en.website.com then 1 millisecond later a request to es.website.com both requests will share the same application scope, the second request will change the value of application.site to the ES version.
A better solution would be to use a request scoped variable for this since the value differs by request.
Another less elegant solution would be to make sure each site has a different application name, for example:
this.name = LCase(cgi.server_name) & "_website";
That would cause each domain to have its own application scope, which depending on how your web server is setup could lead to a denial of service condition (if you allow any domain to hit the application).

Peoplecode - how to create cookies?

We are trying to create a cookie in the PeopleSoft Peoplecode by using the %Response object.
However, the code we tried is failing.
&YourCookie = %Response.AddCookie("YourCookieName", "LR");
Another snippet we tried to create the cookie
Local object &Response = %Response;
Local object &YourCookie;
&YourCookie = &Response.CreateCookie("YourCookieName");
&YourCookie.Domain = %Request.AuthTokenDomain;
&YourCookie.MaxAge = -1; /* Makes this a session cookie (default) */
&YourCookie.Path = "/";
&YourCookie.Secure = True; /* Set to true if using https (will still work with http) */
&YourCookie.Value = "Set the cookie value here. Encrypt sensitive information.";
The document reference points to IScript functions called CreateCookie methods etc.
http://docs.oracle.com/cd/E15645_01/pt850pbr0/eng/psbooks/tpcr/chapter.htm?File=tpcr/htm/tpcr21.htm
However, these don't work in Peoplecode. We don't have the knowledge to create IScript or use it. Any insight with the People code API for cookies or IScript is much appreciated.
I just tested on PeopleTools 8.54.11 and was able to create a cookie using the snippet you provided above.
I did find I had an issue if I set
&YourCookie.Secure = True;
in an environment where I was using HTTP.
If you set Secure to False the cookie will be available in both HTTP and HTTPS
if you set Secure to True the cookie is only available in HTTPS
PeopleTools 8.54 Documentation showing the CreateCookie method
I have been trying to do this (same code snippet) from within signon peoplecode, tools release is 8.54.09. I can execute the first two lines of code, but as soon as the line of code executing the CreateCookie() method executes, I get tossed out / end up on the signon error page.
This seems to support the previous answer saying that the API has removed the method, but the answer before that says it has been successful on tools 8.54.11 -- does that mean they removed it, then put it back, and I happen to be stuck with a release where it was removed? :-/

gSoap: How to set or change cookies on client in Qt?

I'am use next code for authorization on server by service, and get other service'methods using cookie identifer for authorizathion.
TerminalControllerBinding soapObj;
soap_init1(soapObj.soap, SOAP_C_UTFSTRING);
soapObj.endpoint = "http://192.168.*.*/path/to/service";
ns1__getTemplatesResponse *response = new ns1__getTemplatesResponse;
std::string auth_res = "";
soapObj.ns1__auth("user", "password", auth_res);
QString sessid = QString::fromStdString(auth_res);
qDebug() << sessid;
soapObj.soap->cookies = soap_cookie(soapObj.soap, "sessid", sessid.toAscii().data(), ".");
Server not getting cookie "sessid"
I am kind of confused by the code you posted: You allocate memory for ns1__getTemplatesResponse, then do some apparently unrelated stuff; in fact you do not reference it again at all. Furthermore soap_cookie is a struct and soap->cookies is basically a list. So there is no magic that transfers the cookies to the server here.
I think what you want is soap_set_cookie. You can find a little more information on client side cookies here, but there isn't any example code. Much more helpful however is actually the server side documentation (the handling of cookies doesn't differ much).
Also notice that you either need to compile with -DWITH_COOKIES or define the macro yourself in stdsoap.h if you haven't done so already.