Bug 178993 -patch from robert sayre and others - r=bsmedberg, dveditz, mvl, sr=darin - add HTTPOnly support

This commit is contained in:
mkaply%us.ibm.com 2007-03-14 17:22:01 +00:00
Родитель cbafae509c
Коммит 921e9a8206
6 изменённых файлов: 100 добавлений и 27 удалений

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

@ -44,7 +44,7 @@
* access of cookie objects
*/
[scriptable, uuid(d3493503-7854-46ed-8284-8af54a847efb)]
[scriptable, uuid(1f5b06e1-8d52-46a0-a26d-762bd6a1Cf75)]
interface nsICookie2 : nsICookie
{
@ -71,4 +71,10 @@ interface nsICookie2 : nsICookie
*/
readonly attribute PRInt64 expiry;
/**
* true if the cookie is an http only cookie
*/
readonly attribute boolean httpOnly;
};

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

@ -94,10 +94,8 @@ interface nsICookieService : nsISupports
/*
* Get the complete cookie string associated with the URI.
*
* XXX this function is redundant and will most likely be removed in a future
* revision of this interface. GetCookieString will query the documentURI
* property off of nsIHttpChannelInternal if supported, so GetCookieString
* can be used in place of this method.
* This function is NOT redundant with getCookieString, as the result
* will be different based on httponly (see bug 178993)
*
* @param aURI
* the URI of the document for which cookies are being queried.
@ -135,11 +133,8 @@ interface nsICookieService : nsISupports
/*
* Set the cookie string and expires associated with the URI.
*
* XXX this function is redundant and will most likely be removed in a future
* revision of this interface. SetCookieString will query the documentURI
* property off of nsIHttpChannelInternal if supported, and SetCookieString
* could also query the Date header from the channel if aChannel supports
* nsIHttpChannel.
* This function is NOT redundant with setCookieString, as the result
* will be different based on httponly (see bug 178993)
*
* @param aURI
* the URI of the document for which cookies are being set.

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

@ -89,6 +89,7 @@ nsCookie::Create(const nsACString &aName,
nsInt64 aLastAccessed,
PRBool aIsSession,
PRBool aIsSecure,
PRBool aIsHttpOnly,
nsCookieStatus aStatus,
nsCookiePolicy aPolicy)
{
@ -111,7 +112,8 @@ nsCookie::Create(const nsACString &aName,
// construct the cookie. placement new, oh yeah!
return new (place) nsCookie(name, value, host, path, end,
aExpiry, aLastAccessed, ++gLastCreationTime,
aIsSession, aIsSecure, aStatus, aPolicy);
aIsSession, aIsSecure, aIsHttpOnly,
aStatus, aPolicy);
}
/******************************************************************************
@ -131,6 +133,7 @@ NS_IMETHODIMP nsCookie::GetIsDomain(PRBool *aIsDomain) { *aIsDomain = IsDoma
NS_IMETHODIMP nsCookie::GetIsSecure(PRBool *aIsSecure) { *aIsSecure = IsSecure(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetStatus(nsCookieStatus *aStatus) { *aStatus = Status(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetPolicy(nsCookiePolicy *aPolicy) { *aPolicy = Policy(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetHttpOnly(PRBool *aHttpOnly) { *aHttpOnly = IsHttpOnly(); return NS_OK; }
// compatibility method, for use with the legacy nsICookie interface.
// here, expires == 0 denotes a session cookie.

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

@ -80,6 +80,7 @@ class nsCookie : public nsICookie2
PRUint32 aCreationTime,
PRBool aIsSession,
PRBool aIsSecure,
PRBool aIsHttpOnly,
nsCookieStatus aStatus,
nsCookiePolicy aPolicy)
: mNext(nsnull)
@ -94,6 +95,7 @@ class nsCookie : public nsICookie2
, mRefCnt(0)
, mIsSession(aIsSession != PR_FALSE)
, mIsSecure(aIsSecure != PR_FALSE)
, mIsHttpOnly(aIsHttpOnly != PR_FALSE)
, mStatus(aStatus)
, mPolicy(aPolicy)
{
@ -110,6 +112,7 @@ class nsCookie : public nsICookie2
nsInt64 aLastAccessed,
PRBool aIsSession,
PRBool aIsSecure,
PRBool aIsHttpOnly,
nsCookieStatus aStatus,
nsCookiePolicy aPolicy);
@ -127,6 +130,7 @@ class nsCookie : public nsICookie2
inline PRBool IsSession() const { return mIsSession; }
inline PRBool IsDomain() const { return *mHost == '.'; }
inline PRBool IsSecure() const { return mIsSecure; }
inline PRBool IsHttpOnly() const { return mIsHttpOnly; }
inline nsCookieStatus Status() const { return mStatus; }
inline nsCookiePolicy Policy() const { return mPolicy; }
@ -158,6 +162,7 @@ class nsCookie : public nsICookie2
PRUint32 mRefCnt : 16;
PRUint32 mIsSession : 1;
PRUint32 mIsSecure : 1;
PRUint32 mIsHttpOnly: 1;
PRUint32 mStatus : 3;
PRUint32 mPolicy : 3;
};

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

@ -75,6 +75,11 @@
* useful types & constants
******************************************************************************/
// XXX_hack. See bug 178993.
// This is a hack to hide HttpOnly cookies from older browsers
//
static const char kHttpOnlyPrefix[] = "#HttpOnly_";
static const char kCookieFileName[] = "cookies.txt";
static const PRUint32 kLazyWriteTimeout = 5000; //msec
@ -123,6 +128,7 @@ struct nsCookieAttributes
nsInt64 expiryTime;
PRBool isSession;
PRBool isSecure;
PRBool isHttpOnly;
};
// stores linked list iteration state, and provides a rudimentary
@ -482,7 +488,7 @@ nsCookieService::Observe(nsISupports *aSubject,
return NS_OK;
}
// helper function for GetCookieStringFromHttp
// helper function for GetCookieList
static inline PRBool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; }
void
@ -490,6 +496,7 @@ nsCookieService::GetCookieList(nsIURI *aHostURI,
nsIURI *aFirstURI,
nsIChannel *aChannel,
const nsACString *aName,
PRBool aHttpBound,
nsAutoVoidArray &aResult)
{
if (!aHostURI) {
@ -552,6 +559,12 @@ nsCookieService::GetCookieList(nsIURI *aHostURI,
continue;
}
// if the cookie is httpOnly and it's not going directly to the HTTP
// connection, don't send it
if (cookie->IsHttpOnly() && !aHttpBound) {
continue;
}
// calculate cookie path length, excluding trailing '/'
PRUint32 cookiePathLen = cookie->Path().Length();
if (cookiePathLen > 0 && cookie->Path().Last() == '/') {
@ -615,7 +628,7 @@ nsCookieService::GetCookieValue(nsIURI *aHostURI,
}
nsAutoVoidArray foundCookieList;
GetCookieList(aHostURI, firstURI, aChannel, &aName,
GetCookieList(aHostURI, firstURI, aChannel, &aName, PR_FALSE,
foundCookieList);
if (!foundCookieList.Count())
@ -640,7 +653,12 @@ nsCookieService::GetCookieString(nsIURI *aHostURI,
httpInternal->GetDocumentURI(getter_AddRefs(firstURI));
}
return GetCookieStringFromHttp(aHostURI, firstURI, aChannel, aCookie);
nsAutoVoidArray foundCookieList;
GetCookieList(aHostURI, firstURI, aChannel, nsnull, PR_FALSE,
foundCookieList);
*aCookie = CookieStringFromArray(foundCookieList, aHostURI);
return NS_OK;
}
NS_IMETHODIMP
@ -649,16 +667,22 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
nsIChannel *aChannel,
char **aCookie)
{
*aCookie = nsnull;
nsAutoVoidArray foundCookieList;
GetCookieList(aHostURI, aFirstURI, aChannel, nsnull,
GetCookieList(aHostURI, aFirstURI, aChannel, nsnull, PR_TRUE,
foundCookieList);
*aCookie = CookieStringFromArray(foundCookieList, aHostURI);
return NS_OK;
}
char*
nsCookieService::CookieStringFromArray(const nsAutoVoidArray& aCookieList,
nsIURI *aHostURI)
{
nsCAutoString cookieData;
PRInt32 count = foundCookieList.Count();
PRInt32 count = aCookieList.Count();
for (PRInt32 i = 0; i < count; ++i) {
nsCookie *cookie = NS_STATIC_CAST(nsCookie*, foundCookieList.ElementAt(i));
nsCookie *cookie = NS_STATIC_CAST(nsCookie*, aCookieList.ElementAt(i));
// check if we have anything to write
if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
@ -682,10 +706,10 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
// fix the callers to use nsACStrings.
if (!cookieData.IsEmpty()) {
COOKIE_LOGSUCCESS(GET_COOKIE, aHostURI, cookieData, nsnull);
*aCookie = ToNewCString(cookieData);
return ToNewCString(cookieData);
}
return NS_OK;
return nsnull;
}
NS_IMETHODIMP
@ -943,6 +967,7 @@ nsCookieService::Add(const nsACString &aDomain,
currentTime,
aIsSession,
aIsSecure,
PR_FALSE,
nsICookie::STATUS_UNKNOWN,
nsICookie::POLICY_UNKNOWN);
if (!cookie) {
@ -1007,11 +1032,11 @@ nsCookieService::Read()
nsCAutoString buffer;
PRBool isMore = PR_TRUE;
PRInt32 hostIndex = 0, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex;
PRInt32 hostIndex, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex;
nsASingleFragmentCString::char_iterator iter;
PRInt32 numInts;
PRInt64 expires;
PRBool isDomain;
PRBool isDomain, isHttpOnly = PR_FALSE;
nsInt64 currentTime = NOW_IN_SECONDS;
// we use lastAccessedCounter to keep cookies in recently-used order,
// so we start by initializing to currentTime (somewhat arbitrary)
@ -1031,9 +1056,25 @@ nsCookieService::Read()
* most-recently used come first; least-recently-used come last.
*/
/*
* ...but due to bug 178933, we hide HttpOnly cookies from older code
* in a comment, so they don't expose HttpOnly cookies to JS.
*
* The format for HttpOnly cookies is
*
* #HttpOnly_host \t isDomain \t path \t secure \t expires \t name \t cookie
*
*/
while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
if (buffer.IsEmpty() || buffer.First() == '#') {
if (StringBeginsWith(buffer, NS_LITERAL_CSTRING(kHttpOnlyPrefix))) {
isHttpOnly = PR_TRUE;
hostIndex = sizeof(kHttpOnlyPrefix) - 1;
} else if (buffer.IsEmpty() || buffer.First() == '#') {
continue;
} else {
isHttpOnly = PR_FALSE;
hostIndex = 0;
}
// this is a cheap, cheesy way of parsing a tab-delimited line into
@ -1077,6 +1118,7 @@ nsCookieService::Read()
nsInt64(expires),
lastAccessedCounter,
PR_FALSE,
isHttpOnly,
Substring(buffer, secureIndex, expiresIndex - secureIndex - 1).EqualsLiteral(kTrue),
nsICookie::STATUS_UNKNOWN,
nsICookie::POLICY_UNKNOWN);
@ -1173,6 +1215,11 @@ nsCookieService::Write()
* note 2: cookies are written in order of lastAccessed time:
* most-recently used come first; least-recently-used come last.
*/
/*
* XXX but see above in ::Read for the HttpOnly hack
*/
nsCookie *cookie;
nsInt64 currentTime = NOW_IN_SECONDS;
char dateString[22];
@ -1185,6 +1232,10 @@ nsCookieService::Write()
continue;
}
// XXX hack for HttpOnly. see bug 178993.
if (cookie->IsHttpOnly()) {
bufferedOutputStream->Write(kHttpOnlyPrefix, sizeof(kHttpOnlyPrefix) - 1, &rv);
}
bufferedOutputStream->Write(cookie->Host().get(), cookie->Host().Length(), &rv);
if (cookie->IsDomain()) {
bufferedOutputStream->Write(kTrue, sizeof(kTrue) - 1, &rv);
@ -1300,6 +1351,7 @@ nsCookieService::CheckAndAdd(nsIURI *aHostURI,
currentTime,
aAttributes.isSession,
aAttributes.isSecure,
aAttributes.isHttpOnly,
aStatus,
aPolicy);
if (!cookie) {
@ -1442,6 +1494,9 @@ nsCookieService::AddInternal(nsCookie *aCookie,
5. cookie <NAME> is optional, where spec requires it. This is a fairly
trivial case, but allows the flexibility of setting only a cookie <VALUE>
with a blank <NAME> and is required by some sites (see bug 169091).
6. Attribute "HttpOnly", not covered in the RFCs, is supported
(see bug 178993).
** Begin BNF:
token = 1*<any allowed-chars except separators>
@ -1477,6 +1532,7 @@ nsCookieService::AddInternal(nsCookie *aCookie,
| "Comment" "=" value
| "Version" "=" value
| "Secure"
| "HttpOnly"
******************************************************************************/
@ -1583,6 +1639,7 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
static const char kExpires[] = "expires";
static const char kMaxage[] = "max-age";
static const char kSecure[] = "secure";
static const char kHttpOnly[] = "httponly";
nsASingleFragmentCString::const_char_iterator tempBegin, tempEnd;
nsASingleFragmentCString::const_char_iterator cookieStart, cookieEnd;
@ -1590,7 +1647,8 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
aCookieHeader.EndReading(cookieEnd);
aCookieAttributes.isSecure = PR_FALSE;
aCookieAttributes.isHttpOnly = PR_FALSE;
nsDependentCSubstring tokenString(cookieStart, cookieStart);
nsDependentCSubstring tokenValue (cookieStart, cookieStart);
PRBool newCookie, equalsFound;
@ -1637,6 +1695,11 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
// ignore any tokenValue for isSecure; just set the boolean
else if (tokenString.LowerCaseEqualsLiteral(kSecure))
aCookieAttributes.isSecure = PR_TRUE;
// ignore any tokenValue for isHttpOnly (see bug 178993);
// just set the boolean
else if (tokenString.LowerCaseEqualsLiteral(kHttpOnly))
aCookieAttributes.isHttpOnly = PR_TRUE;
}
// rebind aCookieHeader, in case we need to process another cookie

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

@ -175,7 +175,8 @@ class nsCookieService : public nsICookieServiceInternal
void PrefChanged(nsIPrefBranch *aPrefBranch);
nsresult Read();
nsresult Write();
void GetCookieList(nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, const nsACString *aName, nsAutoVoidArray &aResult);
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, nsInt64 aServerTime, nsCookieStatus aStatus, nsCookiePolicy aPolicy);
void CheckAndAdd(nsIURI *aHostURI, nsIChannel *aChannel, nsCookieAttributes &aAttributes, nsCookieStatus aStatus, nsCookiePolicy aPolicy, const nsAFlatCString &aCookieHeader);
void AddInternal(nsCookie *aCookie, nsInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader);