prevent creating, modifying, and deleting HttpOnly cookies from web content, and add unit tests to that effect. b=383181, r+sr=dveditz

This commit is contained in:
dwitte@stanford.edu 2007-06-26 01:36:50 -07:00
Родитель a7e74ff2ec
Коммит c9d4ef4c68
3 изменённых файлов: 74 добавлений и 17 удалений

Просмотреть файл

@ -855,7 +855,7 @@ nsCookieService::SetCookieValue(nsIURI *aHostURI,
attributes.isSecure = PR_FALSE;
aHostURI->SchemeIs("https", &attributes.isSecure);
CheckAndAdd(aHostURI, aChannel, attributes, EmptyCString());
CheckAndAdd(aHostURI, aChannel, attributes, EmptyCString(), PR_FALSE);
return NS_OK;
}
@ -874,7 +874,7 @@ nsCookieService::SetCookieString(nsIURI *aHostURI,
httpInternal->GetDocumentURI(getter_AddRefs(firstURI));
}
return SetCookieStringFromHttp(aHostURI, firstURI, aPrompt, aCookieHeader, nsnull, aChannel);
return SetCookieStringInternal(aHostURI, firstURI, aPrompt, aCookieHeader, nsnull, aChannel, PR_FALSE);
}
NS_IMETHODIMP
@ -884,6 +884,18 @@ nsCookieService::SetCookieStringFromHttp(nsIURI *aHostURI,
const char *aCookieHeader,
const char *aServerTime,
nsIChannel *aChannel)
{
return SetCookieStringInternal(aHostURI, aFirstURI, aPrompt, aCookieHeader, aServerTime, aChannel, PR_TRUE);
}
nsresult
nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
nsIURI *aFirstURI,
nsIPrompt *aPrompt,
const char *aCookieHeader,
const char *aServerTime,
nsIChannel *aChannel,
PRBool aFromHttp)
{
if (!aHostURI) {
COOKIE_LOGFAILURE(SET_COOKIE, nsnull, aCookieHeader, "host URI is null");
@ -919,7 +931,7 @@ nsCookieService::SetCookieStringFromHttp(nsIURI *aHostURI,
// switch to a nice string type now, and process each cookie in the header
nsDependentCString cookieHeader(aCookieHeader);
while (SetCookieInternal(aHostURI, aChannel, cookieHeader, serverTime));
while (SetCookieInternal(aHostURI, aChannel, cookieHeader, serverTime, aFromHttp));
return NS_OK;
}
@ -1030,7 +1042,7 @@ nsCookieService::Add(const nsACString &aDomain,
return NS_ERROR_OUT_OF_MEMORY;
}
AddInternal(cookie, currentTimeInUsec / PR_USEC_PER_SEC, nsnull, nsnull);
AddInternal(cookie, currentTimeInUsec / PR_USEC_PER_SEC, nsnull, nsnull, PR_TRUE);
return NS_OK;
}
@ -1284,7 +1296,8 @@ PRBool
nsCookieService::SetCookieInternal(nsIURI *aHostURI,
nsIChannel *aChannel,
nsDependentCString &aCookieHeader,
PRInt64 aServerTime)
PRInt64 aServerTime,
PRBool aFromHttp)
{
// create a stack-based nsCookieAttributes, to store all the
// attributes parsed from the cookie
@ -1309,7 +1322,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
cookieAttributes.isSession = GetExpiry(cookieAttributes, aServerTime,
currentTimeInUsec / PR_USEC_PER_SEC);
CheckAndAdd(aHostURI, aChannel, cookieAttributes, savedCookieHeader);
CheckAndAdd(aHostURI, aChannel, cookieAttributes, savedCookieHeader, aFromHttp);
return newCookie;
}
@ -1318,7 +1331,8 @@ void
nsCookieService::CheckAndAdd(nsIURI *aHostURI,
nsIChannel *aChannel,
nsCookieAttributes &aAttributes,
const nsAFlatCString &aCookieHeader)
const nsAFlatCString &aCookieHeader,
PRBool aFromHttp)
{
// reject cookie if it's over the size limit, per RFC2109
if ((aAttributes.name.Length() + aAttributes.value.Length()) > kMaxBytesPerCookie) {
@ -1381,7 +1395,7 @@ nsCookieService::CheckAndAdd(nsIURI *aHostURI,
// add the cookie to the list. AddInternal() takes care of logging.
// we get the current time again here, since it may have changed during prompting
AddInternal(cookie, PR_Now() / PR_USEC_PER_SEC, aHostURI, aCookieHeader.get());
AddInternal(cookie, PR_Now() / PR_USEC_PER_SEC, aHostURI, aCookieHeader.get(), aFromHttp);
}
// this is a backend function for adding a cookie to the list, via SetCookie.
@ -1393,7 +1407,8 @@ void
nsCookieService::AddInternal(nsCookie *aCookie,
PRInt64 aCurrentTime,
nsIURI *aHostURI,
const char *aCookieHeader)
const char *aCookieHeader,
PRBool aFromHttp)
{
// start a transaction on the storage db, to optimize deletions/insertions.
// transaction will automically commit on completion. if we already have a
@ -1407,6 +1422,13 @@ nsCookieService::AddInternal(nsCookie *aCookie,
nsRefPtr<nsCookie> oldCookie;
if (foundCookie) {
oldCookie = matchIter.current;
// if the old cookie is httponly, make sure we're not coming from script
if (!aFromHttp && oldCookie->IsHttpOnly()) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "previously stored cookie is httponly; coming from script");
return;
}
RemoveCookieFromList(matchIter);
// check if the cookie has expired
@ -1423,6 +1445,12 @@ nsCookieService::AddInternal(nsCookie *aCookie,
return;
}
// if the new cookie is httponly, make sure we're not coming from script
if (!aFromHttp && aCookie->IsHttpOnly()) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie is httponly; coming from script");
return;
}
// check if we have to delete an old cookie.
nsEnumerationData data(aCurrentTime, LL_MAXINT);
if (CountCookiesFromHostInternal(aCookie->RawHost(), data) >= mMaxCookiesPerHost) {

Просмотреть файл

@ -171,9 +171,10 @@ class nsCookieService : public nsICookieServiceInternal
nsresult Read();
void GetCookieList(nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, const nsACString *aName, PRBool isHttpBound, nsAutoVoidArray &aResult);
char* CookieStringFromArray(const nsAutoVoidArray& aCookieList, nsIURI *aHostURI);
PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, PRInt64 aServerTime);
void CheckAndAdd(nsIURI *aHostURI, nsIChannel *aChannel, nsCookieAttributes &aAttributes, const nsAFlatCString &aCookieHeader);
void AddInternal(nsCookie *aCookie, PRInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader);
nsresult SetCookieStringInternal(nsIURI *aHostURI, nsIURI *aFirstURI, nsIPrompt *aPrompt, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, PRBool aFromHttp);
PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
void CheckAndAdd(nsIURI *aHostURI, nsIChannel *aChannel, nsCookieAttributes &aAttributes, const nsAFlatCString &aCookieHeader, PRBool aFromHttp);
void AddInternal(nsCookie *aCookie, PRInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp);
void RemoveCookieFromList(nsListIter &aIter);
PRBool AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB = PR_TRUE);
static PRBool GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, PRBool &aEqualsFound);

Просмотреть файл

@ -642,19 +642,47 @@ main(PRInt32 argc, char *argv[])
GetACookie(cookieService, "http://multi.path.tests/one/two/three/four/five/six/", nsnull, getter_Copies(cookie));
rv[0] = CheckResult(cookie.get(), MUST_EQUAL, "test7=path; test6=path; test3=path; test1=path; test5=path; test4=path; test2=path; test8=path");
allTestsPassed = PrintResult(rv, 1) && allTestsPassed;
// *** httponly tests
printf("*** Beginning httponly tests...\n");
// Since this cookie is NOT set via http, setting it fails
SetACookieNoHttp(cookieService, "http://httponly.test/", "test=httponly; httponly");
GetACookie(cookieService, "http://httponly.test/", nsnull, getter_Copies(cookie));
rv[0] = CheckResult(cookie.get(), MUST_BE_NULL);
// Since this cookie is set via http, it can be retrieved
SetACookie(cookieService, "http://httponly.test/", nsnull, "test=httponly; httponly", nsnull);
GetACookie(cookieService, "http://httponly.test/", nsnull, getter_Copies(cookie));
rv[0] = CheckResult(cookie.get(), MUST_EQUAL, "test=httponly");
// Since this cookie is NOT set via http, it can NOT be retrieved
SetACookieNoHttp(cookieService, "http://httponly.test/", "test=httponly; httponly");
rv[1] = CheckResult(cookie.get(), MUST_EQUAL, "test=httponly");
// ... but not by web content
GetACookieNoHttp(cookieService, "http://httponly.test/", getter_Copies(cookie));
rv[1] = CheckResult(cookie.get(), MUST_NOT_EQUAL, "test=httponly");
rv[2] = CheckResult(cookie.get(), MUST_BE_NULL);
// Non-Http cookies should not replace HttpOnly cookies
SetACookie(cookieService, "http://httponly.test/", nsnull, "test=httponly; httponly", nsnull);
SetACookieNoHttp(cookieService, "http://httponly.test/", "test=not-httponly");
GetACookie(cookieService, "http://httponly.test/", nsnull, getter_Copies(cookie));
rv[3] = CheckResult(cookie.get(), MUST_EQUAL, "test=httponly");
// ... and, if an HttpOnly cookie already exists, should not be set at all
GetACookieNoHttp(cookieService, "http://httponly.test/", getter_Copies(cookie));
rv[4] = CheckResult(cookie.get(), MUST_BE_NULL);
// Non-Http cookies should not delete HttpOnly cookies
SetACookie(cookieService, "http://httponly.test/", nsnull, "test=httponly; httponly", nsnull);
SetACookieNoHttp(cookieService, "http://httponly.test/", "test=httponly; max-age=-1");
GetACookie(cookieService, "http://httponly.test/", nsnull, getter_Copies(cookie));
rv[5] = CheckResult(cookie.get(), MUST_EQUAL, "test=httponly");
// ... but HttpOnly cookies should
SetACookie(cookieService, "http://httponly.test/", nsnull, "test=httponly; httponly; max-age=-1", nsnull);
GetACookie(cookieService, "http://httponly.test/", nsnull, getter_Copies(cookie));
rv[6] = CheckResult(cookie.get(), MUST_BE_NULL);
// Non-Httponly cookies can replace HttpOnly cookies when set over http
SetACookie(cookieService, "http://httponly.test/", nsnull, "test=httponly; httponly", nsnull);
SetACookie(cookieService, "http://httponly.test/", nsnull, "test=not-httponly", nsnull);
GetACookieNoHttp(cookieService, "http://httponly.test/", getter_Copies(cookie));
rv[7] = CheckResult(cookie.get(), MUST_EQUAL, "test=not-httponly");
allTestsPassed = PrintResult(rv, 2) && allTestsPassed;
allTestsPassed = PrintResult(rv, 8) && allTestsPassed;
// *** nsICookieManager{2} interface tests