Implement CSRF protection

This commit is contained in:
AAGaming
2022-08-05 21:16:29 -04:00
parent ab6ec98160
commit f21d34506d
7 changed files with 95 additions and 21 deletions
+13
View File
@@ -1,7 +1,20 @@
from aiohttp.web import middleware, Response
import ssl
import certifi
import uuid
ssl_ctx = ssl.create_default_context(cafile=certifi.where())
csrf_token = str(uuid.uuid4())
def get_ssl_context():
return ssl_ctx
def get_csrf_token():
return csrf_token
@middleware
async def csrf_middleware(request, handler):
if str(request.method) == "OPTIONS" or request.headers.get('Authentication') == csrf_token or str(request.rel_url) == "/auth/token" or str(request.rel_url).startswith("/plugins/load_main/") or str(request.rel_url).startswith("/static/") or str(request.rel_url).startswith("/legacy/") or str(request.rel_url).startswith("/steam_resource/"):
return await handler(request)
return Response(text='Forbidden', status='403')
+6
View File
@@ -8,10 +8,13 @@ window.addEventListener("message", function(evt) {
}, false);
async function call_server_method(method_name, arg_object={}) {
const token = await fetch("http://127.0.0.1:1337/auth/token").then(r => r.text());
const response = await fetch(`http://127.0.0.1:1337/methods/${method_name}`, {
method: 'POST',
credentials: "include",
headers: {
'Content-Type': 'application/json',
Authentication: token
},
body: JSON.stringify(arg_object),
});
@@ -40,10 +43,13 @@ async function fetch_nocors(url, request={}) {
async function call_plugin_method(method_name, arg_object={}) {
if (plugin_name == undefined)
throw new Error("Plugin methods can only be called from inside plugins (duh)");
const token = await fetch("http://127.0.0.1:1337/auth/token").then(r => r.text());
const response = await fetch(`http://127.0.0.1:1337/plugins/${plugin_name}/methods/${method_name}`, {
method: 'POST',
credentials: "include",
headers: {
'Content-Type': 'application/json',
Authentication: token
},
body: JSON.stringify({
args: arg_object,
+9 -2
View File
@@ -20,12 +20,13 @@ from os import path
from subprocess import call
import aiohttp_cors
from aiohttp.web import Application, run_app, static
from aiohttp.web import Application, run_app, static, get, Response
from aiohttp_jinja2 import setup as jinja_setup
from browser import PluginBrowser
from injector import inject_to_tab, tab_has_global_var
from loader import Loader
from helpers import csrf_middleware, get_csrf_token
from utilities import Utilities
from updater import Updater
@@ -41,9 +42,10 @@ class PluginManager:
def __init__(self) -> None:
self.loop = get_event_loop()
self.web_app = Application()
self.web_app.middlewares.append(csrf_middleware)
self.cors = aiohttp_cors.setup(self.web_app, defaults={
"https://steamloopback.host": aiohttp_cors.ResourceOptions(expose_headers="*",
allow_headers="*")
allow_headers="*", allow_credentials=True)
})
self.plugin_loader = Loader(self.web_app, CONFIG["plugin_path"], self.loop, CONFIG["live_reload"])
self.plugin_browser = PluginBrowser(CONFIG["plugin_path"], self.web_app, self.plugin_loader.plugins)
@@ -57,6 +59,8 @@ class PluginManager:
self.loop.create_task(self.loader_reinjector())
self.loop.create_task(self.load_plugins())
self.loop.set_exception_handler(self.exception_handler)
self.web_app.add_routes([get("/auth/token", self.get_auth_token)])
for route in list(self.web_app.router.routes()):
self.cors.add(route)
self.web_app.add_routes([static("/static", path.join(path.dirname(__file__), 'static'))])
@@ -67,6 +71,9 @@ class PluginManager:
return
loop.default_exception_handler(context)
async def get_auth_token(self, request):
return Response(text=get_csrf_token())
async def wait_for_server(self):
async with ClientSession() as web:
while True:
+26 -2
View File
@@ -35,6 +35,10 @@ export async function installFromURL(url: string) {
await fetch('http://localhost:1337/browser/install_plugin', {
method: 'POST',
body: formData,
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
});
}
@@ -50,6 +54,10 @@ export function requestLegacyPluginInstall(plugin: LegacyStorePlugin, selectedVe
fetch('http://localhost:1337/browser/install_plugin', {
method: 'POST',
body: formData,
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
});
}}
onCancel={() => {
@@ -75,6 +83,10 @@ export async function requestPluginInstall(plugin: StorePlugin, selectedVer: Sto
await fetch('http://localhost:1337/browser/install_plugin', {
method: 'POST',
body: formData,
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
});
}
@@ -84,12 +96,24 @@ const StorePage: FC<{}> = () => {
useEffect(() => {
(async () => {
const res = await fetch('https://beta.deckbrew.xyz/plugins', { method: 'GET' }).then((r) => r.json());
const res = await fetch('https://beta.deckbrew.xyz/plugins', {
method: 'GET',
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
}).then((r) => r.json());
console.log(res);
setData(res.filter((x: StorePlugin) => x.name !== 'Example Plugin'));
})();
(async () => {
const res = await fetch('https://plugins.deckbrew.xyz/get_plugins', { method: 'GET' }).then((r) => r.json());
const res = await fetch('https://plugins.deckbrew.xyz/get_plugins', {
method: 'GET',
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
}).then((r) => r.json());
console.log(res);
setLegacyData(res);
})();
+10 -1
View File
@@ -8,8 +8,11 @@ declare global {
importDeckyPlugin: Function;
syncDeckyPlugins: Function;
deckyHasLoaded: boolean;
deckyAuthToken: string;
}
}
(async () => {
window.deckyAuthToken = await fetch('http://127.0.0.1:1337/auth/token').then((r) => r.text());
window.DeckyPluginLoader?.dismountAll();
window.DeckyPluginLoader?.deinit();
@@ -20,7 +23,12 @@ window.importDeckyPlugin = function (name: string) {
};
window.syncDeckyPlugins = async function () {
const plugins = await (await fetch('http://127.0.0.1:1337/plugins')).json();
const plugins = await (
await fetch('http://127.0.0.1:1337/plugins', {
credentials: 'include',
headers: { Authentication: window.deckyAuthToken },
})
).json();
for (const plugin of plugins) {
if (!window.DeckyPluginLoader.hasPlugin(plugin)) window.DeckyPluginLoader?.importPlugin(plugin);
}
@@ -29,3 +37,4 @@ window.syncDeckyPlugins = async function () {
setTimeout(() => window.syncDeckyPlugins(), 5000);
window.deckyHasLoaded = true;
})();
+14 -1
View File
@@ -75,6 +75,10 @@ class PluginLoader extends Logger {
await fetch('http://localhost:1337/browser/uninstall_plugin', {
method: 'POST',
body: formData,
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
});
}}
onCancel={() => {
@@ -144,7 +148,12 @@ class PluginLoader extends Logger {
}
private async importReactPlugin(name: string) {
let res = await fetch(`http://127.0.0.1:1337/plugins/${name}/frontend_bundle`);
let res = await fetch(`http://127.0.0.1:1337/plugins/${name}/frontend_bundle`, {
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
});
if (res.ok) {
let plugin = await eval(await res.text())(this.createPluginAPI(name));
this.plugins.push({
@@ -166,8 +175,10 @@ class PluginLoader extends Logger {
async callServerMethod(methodName: string, args = {}) {
const response = await fetch(`http://127.0.0.1:1337/methods/${methodName}`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authentication: window.deckyAuthToken,
},
body: JSON.stringify(args),
});
@@ -182,8 +193,10 @@ class PluginLoader extends Logger {
async callPluginMethod(methodName: string, args = {}) {
const response = await fetch(`http://127.0.0.1:1337/plugins/${pluginName}/methods/${methodName}`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authentication: window.deckyAuthToken,
},
body: JSON.stringify({
args,
+2
View File
@@ -14,8 +14,10 @@ export interface DeckyUpdater {
export async function callUpdaterMethod(methodName: string, args = {}) {
const response = await fetch(`http://127.0.0.1:1337/updater/${methodName}`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Authentication: window.deckyAuthToken,
},
body: JSON.stringify(args),
});