Andrew Steinmetz
07/20/2022, 12:16 AM{}$$ {
// valid json object
}
essentially trying to figure out how to remove the {} $$
before the json parsing happens in the pluginBig Chungus
07/20/2022, 12:21 AMAndrew Steinmetz
07/20/2022, 12:23 AMephemient
07/20/2022, 12:25 AMAndrew Steinmetz
07/20/2022, 4:08 AMephemient
07/20/2022, 4:46 AMmy.website/me
which serves up some private data based on the user's cookies.
suppose there is some evil-third-party.website
which some of your users may be tricked into visiting.
• evil-third-party.website
can make a XHR or Fetch request to my.website/me
and retrieve and read data.
◦ you can stop this with CORS.
• evil-third-party.website
can use <script src="my.website/me">
to cause the browser to retrieve the content itself.
◦ you might think this wouldn't do anything useful, since it's just a JSON object that disappears as soon as it's done executing.
◦ but you'd be wrong, because evil-third-party.website
can override the Object
and Array
constructors before the script in order to to exfiltrate data while the script is constructing the JSON object.
◦ putting intentionally bad JS in front can prevent this by stopping the script execution before anything else happens. this requires careful thought and awareness of browser/JS features in order to actually make airtight. common prefixes include things like )]}'
and for(;;);
. even then, you still need to be careful - avoiding the vulnerability in browsers that implement E4X may require an additional </x>
or similar to be safe, for example.Andromadus Naruto
07/20/2022, 6:13 AMephemient
07/20/2022, 6:24 AMAleksei Tirman [JB]
07/20/2022, 8:18 AMval client = HttpClient(Apache)
val specialString = "{}\$\$ "
client.responsePipeline.intercept(HttpResponsePipeline.Receive) { (type, body) ->
// Use `context.request` to narrow this behavior to a specific endpoint or host
if (body !is ByteReadChannel) return@intercept
val cleanBody = context.writer {
val maybeSpecialBytes = ByteArray(specialString.length)
body.readFully(maybeSpecialBytes) // Tries to read a probable special string from the start of a body
if (String(maybeSpecialBytes) != specialString) { // Write previously read bytes only if they aren't a special string
channel.writeFully(maybeSpecialBytes)
}
body.copyTo(channel) // Leave the rest of a body as is
}.channel
proceedWith(HttpResponseContainer(type, cleanBody))
}
Andrew Steinmetz
07/20/2022, 5:16 PMclass MyJsonPlugin private constructor() {
@KtorDsl
public class Config {
}
public companion object Plugin : HttpClientPlugin<Config, MyJsonPlugin> {
override val key: AttributeKey<MyJsonPlugin> = AttributeKey("RFJsonPlugin")
override fun install(plugin: MyJsonPlugin, scope: HttpClient) {
scope.responsePipeline.intercept(HttpResponsePipeline.Receive) { (type, body) ->
if (body !is ByteReadChannel) return@intercept
val cleanBody = context.writer {
val maybeSpecialBytes = ByteArray(SUPER_SECURE_JSON_OBFUSCATOR.length)
body.readFully(maybeSpecialBytes)
if (io.ktor.utils.io.core.String(maybeSpecialBytes) != SUPER_SECURE_JSON_OBFUSCATOR) {
channel.writeFully(maybeSpecialBytes)
}
body.copyTo(channel)
}.channel
proceedWith(HttpResponseContainer(type, cleanBody))
}
}
override fun prepare(block: Config.() -> Unit): MyJsonPlugin =
MyJsonPlugin()
}
}
Then can use it with the client like so:
private val client = HttpClient(engine) {
install(MyJsonPlugin)
install(ContentNegotiation) {
json()
}
}
Aleksei Tirman [JB]
07/20/2022, 5:26 PM