Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
finagle-base-http: add SameSite attribute to Cookie
Summary: Problem Finagle's Cookies do not support the SameSite attribute. Solution Allow the attribute to be added via the Cookie constructor. Differential Revision: https://phabricator.twitter.biz/D157942
- Loading branch information
Stefan Lance
authored and
jenkins
committed
Apr 14, 2018
1 parent
3160a81
commit 4b0a58b
Showing
14 changed files
with
618 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
finagle-base-http/src/main/scala/com/twitter/finagle/http/cookie/SameSite.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.twitter.finagle.http.cookie | ||
|
||
/** | ||
* SameSite cookie attribute. The server may set this in the Set-Cookie to | ||
* ensure that the cookie is not sent with cross-site requests. | ||
* | ||
* As of April 2018, support for this attribute is not implemented by | ||
* all browsers. See [0] for more details. | ||
* | ||
* [0] https://tools.ietf.org/html/draft-west-first-party-cookies-07 | ||
*/ | ||
sealed trait SameSite | ||
|
||
object SameSite { | ||
|
||
/** | ||
* See [1] for the distinction between the Lax and Strict types. | ||
* [1] https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1.1 | ||
*/ | ||
case object Lax extends SameSite | ||
|
||
case object Strict extends SameSite | ||
|
||
/** | ||
* Represents the attribute not being set on the Cookie. | ||
*/ | ||
case object Unset extends SameSite | ||
|
||
} |
103 changes: 103 additions & 0 deletions
103
finagle-base-http/src/main/scala/com/twitter/finagle/http/cookie/SameSiteCodec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package com.twitter.finagle.http.cookie | ||
|
||
import com.twitter.finagle.http.Cookie | ||
import com.twitter.finagle.stats.LoadedStatsReceiver | ||
import com.twitter.logging.Logger | ||
import io.netty.handler.codec.http.CookieDecoder | ||
import java.lang.reflect.Method | ||
import java.util.{ArrayList => AList, List => JList} | ||
|
||
/** | ||
* Encodes and decodes the SameSite Set-Cookie attribute to Strings and to | ||
* Cookies, respectively. | ||
*/ | ||
private[http] object SameSiteCodec { | ||
|
||
private val log: Logger = Logger() | ||
private val cookieDecoderClass = classOf[CookieDecoder] | ||
private val initFailureCounter = | ||
LoadedStatsReceiver.scope("http").scope("cookie").counter("samesite_failures") | ||
|
||
private val _extractKeyValuePairs: Option[Method] = try { | ||
val method = cookieDecoderClass.getDeclaredMethod( | ||
"extractKeyValuePairs", | ||
classOf[String], | ||
classOf[JList[String]], | ||
classOf[JList[String]] | ||
) | ||
method.setAccessible(true) | ||
Some(method) | ||
} catch { | ||
case t: Throwable => | ||
log.error(t, "Failed to initialize `_extractKeyValuePairs`.") | ||
None | ||
} | ||
|
||
/** | ||
* Provides access to `CookieDecoder.extractKeyValuePairs` within Netty. It is | ||
* a private static method which we change the visibility on in order to reuse | ||
* for parsing the cookie header again. Cookie header parsing is error prone and | ||
* not code we wish to rewrite. | ||
*/ | ||
private def extractKeyValuePairs( | ||
header: String, | ||
names: JList[String], | ||
values: JList[String] | ||
): Unit = { | ||
_extractKeyValuePairs match { | ||
case Some(method) => method.invoke(null /* static method */, header, names, values) | ||
case None => () | ||
} | ||
} | ||
|
||
/** | ||
* Adds the `SameSite` cookie attribute to a header value which has already been encoded | ||
* from an existing Finagle `Cookie`. | ||
*/ | ||
def encodeSameSite(cookie: Cookie, encoded: String): String = cookie.sameSite match { | ||
case SameSite.Lax => encoded + "; SameSite=Lax" | ||
case SameSite.Strict => encoded + "; SameSite=Strict" | ||
case _ => encoded | ||
} | ||
|
||
/** | ||
* Decodes the `SameSite` cookie attribute and adds it to an existing Finagle | ||
* `Cookie` value. | ||
*/ | ||
def decodeSameSite(header: String, cookie: Cookie): Cookie = { | ||
val names: AList[String] = new AList[String]() | ||
val values: AList[String] = new AList[String]() | ||
|
||
try { | ||
extractKeyValuePairs(header, names, values) | ||
} catch { | ||
case t: Throwable => | ||
log.warning(t, "Failed to extract attributes from header.") | ||
initFailureCounter.incr() | ||
return cookie | ||
} | ||
|
||
var index: Int = 0 | ||
val len: Int = names.size() | ||
var pos: Int = -1 | ||
|
||
while (index < len && pos == -1) { | ||
if (names.get(index).equalsIgnoreCase("samesite")) { | ||
pos = index | ||
} | ||
index += 1 | ||
} | ||
|
||
if (pos <= -1) { | ||
cookie | ||
} else { | ||
val sameSite = | ||
if (values.get(pos).equalsIgnoreCase("lax")) SameSite.Lax | ||
else if (values.get(pos).equalsIgnoreCase("strict")) SameSite.Strict | ||
else SameSite.Unset | ||
|
||
cookie.sameSite(sameSite) | ||
} | ||
} | ||
|
||
} |
11 changes: 11 additions & 0 deletions
11
...e-base-http/src/main/scala/com/twitter/finagle/http/cookie/exp/SupportSameSiteCodec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.twitter.finagle.http.cookie.exp | ||
|
||
import com.twitter.app.GlobalFlag | ||
|
||
/** | ||
* Enables / disables SameSite support in the CookieCodec. | ||
*/ | ||
object supportSameSiteCodec extends GlobalFlag[Boolean]( | ||
false, // disabled by default | ||
"Allow the SameSite attribute to be added to the Set-Cookie header on Responses" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.