Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set multiple cookies in ISAPI filter

I faced a problem to set multiple cookies in ISAPI filter. I want to add the HttpOnly flag into all cookies.

So, in my first attempt, I split the cookies value and add the HttpOnly flag, then I combine them into one string, invoke pResponse->SetHeader(pfc, "Set-Cookie:", szNewValue) in the end, the browser only get first cookie value.

Code of 1st attempt:

cbValue = sizeof(szValue) / sizeof(szValue[0]);
        if (pResponse->GetHeader(pfc, "Set-Cookie:", szValue, &cbValue))
        {
            char szNewValue[MAX_URI_SIZE] = "";
            char* token = NULL;
            char* context = NULL;
            char delim[] = ",";

            // szValue format like 
            // "Language=en; expires=Sat, 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly,Language=en; expires=Sat, 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly"
            // After first split
            // token = "Language=en; expires=Sat"
            // context = " 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly,Language=en; expires=Sat, 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly"
            token = strtok_s(szValue, delim, &context);
            while (token != NULL)
            {
                strcat_s(szNewValue, token);
                if (NULL != context)
                {
                    if (' ' != context[0] && !strstr(token, "HttpOnly"))
                    {
                        strcat_s(szNewValue, "; HttpOnly");
                    }

                    // context[0] = ' ' means it split the one whole cookie, not an entire cookie, we need append ","
                    // context[0] != '\0' means other cookies after, we need append delimiter ","
                    if (' ' == context[0] || '\0' != context[0])
                    {
                        strcat_s(szNewValue, ",");
                    }
                }
                // NULL, function just re-uses the context after the first read.
                token = strtok_s(NULL, delim, &context);
            }
            if (!pResponse->SetHeader(pfc, "Set-Cookie:", szNewValue))
            {
                // Fail securely - send no cookie!
                pResponse->SetHeader(pfc,"Set-Cookie:","");
            }

In the second attempt, I split the the cookie value, and invoke pResponse->SetHeader(pfc, "Set-Cookie:", szNewValue) for every cookie, but the browser only get the last cookie in this case.

Code of 2nd attempt:

cbValue = sizeof(szValue) / sizeof(szValue[0]);
        if (pResponse->GetHeader(pfc, "Set-Cookie:", szValue, &cbValue))
        {
            char szNewValue[MAX_URI_SIZE] = "";
            char* token = NULL;
            char* context = NULL;
            char delim[] = ",";

            // szValue format like 
            // "Language=en; expires=Sat, 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly,Language=en; expires=Sat, 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly"
            // After first split
            // token = "Language=en; expires=Sat"
            // context = " 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly,Language=en; expires=Sat, 15-Jul-2113 02:46:27 GMT; path=/; HttpOnly"
            token = strtok_s(szValue, delim, &context);
            while (token != NULL)
            {
                strcat_s(szNewValue, token);
                if (NULL != context)
                {
                    if (' ' != context[0] && !strstr(token, "HttpOnly"))
                    {
                        strcat_s(szNewValue, "; HttpOnly");
                    }

                    // context[0] = ' ' means it split the one whole cookie, not an entire cookie, we need append ","
                    // context[0] != '\0' means other cookies after, we need append delimiter ","
                    if (' ' == context[0])// || '\0' != context[0])
                    {
                        strcat_s(szNewValue, ",");
                    }
                    if (' ' != context[0])
                    {
                        pResponse->SetHeader(pfc, "Set-Cookie:", szNewValue);
                        strcpy(szNewValue, "");
                    }
                }
                // NULL, function just re-uses the context after the first read.
                token = strtok_s(NULL, delim, &context);
            }

I do this in IE10+Win2008 R2. In both two cases, the result cookie strings are in correct format. Does anyone have any clue about this?

This problem exists basically because when you invoke GetHeader, you receive all cookies in one comma delimited string. What would be the best way to use SetHeader method to set all cookies back to the response?

like image 289
user2582596 Avatar asked Oct 03 '22 13:10

user2582596


2 Answers

I was searching for a solution to this issue and found a lot of incorrect responses.

This post was what got me to the solution.

The original posted solution didn't work because it was using SetHeader for each cookie. SetHeader replaces the "Set-Cookie:" header each time it is called so only the last Cookie was set. Instead of using SetHeader, what I did was to use AddHeader for each cookie. But, before using AddHeader for the first time I used SetHeader with "" to "empty" the "Set-Cookie:" header.

This worked for me using IIS5.1 and IIS7.0

This solution works for ASP Session Id cookie too.

I know Classic ASP is an old technology, but it is still in use and we need solutions like this.

Here is my complete code:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <httpfilt.h>

BOOL WINAPI __stdcall GetFilterVersion(HTTP_FILTER_VERSION *pVer)
{
    pVer->dwFlags = SF_NOTIFY_SEND_RESPONSE | SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT;

    pVer->dwFilterVersion = HTTP_FILTER_REVISION;

    strcpy_s(pVer->lpszFilterDesc, sizeof(pVer->lpszFilterDesc), "httpOnly Filter, Version 1.0. JCGalvezV.");

    return TRUE;
}

DWORD WINAPI __stdcall HttpFilterProc(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType, VOID *pvData)
{
    DWORD   cbBuffer;
  char lszBuffer[2000], lszNewBuffer[2000];
  HTTP_FILTER_PREPROC_HEADERS *pFPH = (HTTP_FILTER_PREPROC_HEADERS *)pvData;

  switch (NotificationType)
  {
    case SF_NOTIFY_SEND_RESPONSE :
      cbBuffer = sizeof(lszBuffer);
      if (pFPH->GetHeader(pfc, "Set-Cookie:", lszBuffer, &cbBuffer))
      {
        char* token = NULL;
        char* context = NULL;
        char delim[] = ",";

        // Delete previous cookies

        pFPH->SetHeader(pfc, "Set-Cookie:", "");

        token = strtok_s(lszBuffer, delim, &context);
        while (token != NULL)
        {
          strcpy_s(lszNewBuffer, sizeof(lszNewBuffer), token);
          if (!strstr(token, "httpOnly"))
            strcat_s(lszNewBuffer, sizeof(lszNewBuffer), "; httpOnly");

          // AddHeader instead of SetHeader.

          pFPH->AddHeader(pfc, "Set-Cookie:", lszNewBuffer);

          // next token
          token = strtok_s(NULL, delim, &context);
        }

      }
      break;
    default :
      break;                
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
like image 74
Juan C. Galvez Avatar answered Oct 07 '22 19:10

Juan C. Galvez


I had the same problem and I needed to contact Microsoft to solve this issue. When you receive multiple cookies, you will receive a full string with all the cookies separated by commas. The work consists of separating each cookie and then call the SetHeader method for each separately.

The important thing is that each cookie must have a unique name-value pair (http://www.quirksmode.org/js/cookies.html) so each change can be properly mapped.

The solution in pseudocode

pResponse->GetHeader(pfc, "Set-Cookie:", szValue, &cbValue)

// split cookies here

foreach separated cookie
    pResponse->SetHeader(pfc, "Set-Cookie:", oneCookie)

That way, you don't need to clean all cookies to add them again.

like image 35
Paulo Pires Avatar answered Oct 07 '22 20:10

Paulo Pires