Compare commits
9 commits
4a1187c3a9
...
fb1e39ed1c
Author | SHA1 | Date | |
---|---|---|---|
|
fb1e39ed1c | ||
|
e58620fc42 | ||
|
b677352f4f | ||
|
9caedb4480 | ||
|
59832d8e11 | ||
|
601a7acb16 | ||
|
9bee70c106 | ||
|
b2250966fb | ||
|
43510fb367 |
10
README.md
10
README.md
|
@ -1,5 +1,15 @@
|
||||||
# CSPJ Application
|
# CSPJ Application
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
User to be added into `docker` group.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
```
|
||||||
|
|
||||||
## Services
|
## Services
|
||||||
|
|
||||||
- 3331: Apache + ModSecurity
|
- 3331: Apache + ModSecurity
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { Box, Stack } from "@mui/material";
|
||||||
import { WindowButtons } from "./WindowButtons";
|
import { WindowButtons } from "./WindowButtons";
|
||||||
import { NavigationButtons } from "./NavigationButtons";
|
import { NavigationButtons } from "./NavigationButtons";
|
||||||
import { RouteDisplay } from "./RouteDisplay";
|
import { RouteDisplay } from "./RouteDisplay";
|
||||||
import { Testing } from "../Testing/Testing";
|
|
||||||
import { ServerStatus } from "./ServerStatus";
|
import { ServerStatus } from "./ServerStatus";
|
||||||
|
|
||||||
export const HeaderBar = () => {
|
export const HeaderBar = () => {
|
||||||
|
@ -73,7 +72,6 @@ export const HeaderBar = () => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ServerStatus />
|
<ServerStatus />
|
||||||
<Testing />
|
|
||||||
<WindowButtons />
|
<WindowButtons />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Box, Button, Chip, CircularProgress, Popover, Stack } from "@mui/material";
|
import { Box, Button, Chip, CircularProgress, Popover, Stack } from "@mui/material";
|
||||||
import { fetch } from "@tauri-apps/plugin-http";
|
import { fetch } from "@tauri-apps/plugin-http";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { MouseEvent, useEffect, useState } from "react";
|
import { MouseEvent, useCallback, useEffect, useState } from "react";
|
||||||
import { serverConnectionAtom, serverUrlAtom } from "../../lib/jotai";
|
import { serverConnectionAtom, serverUrlAtom } from "../../lib/jotai";
|
||||||
import { defaultSettings } from "../../lib/settings";
|
import { defaultSettings } from "../../lib/settings";
|
||||||
import { ServerUrlInput } from "./ServerUrlInput";
|
import { ServerUrlInput } from "./ServerUrlInput";
|
||||||
|
@ -24,7 +24,7 @@ export const ServerStatus = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// function to check server health
|
// function to check server health
|
||||||
const checkServerConnection = async () => {
|
const checkServerConnection = useCallback(async () => {
|
||||||
setServerConnection("connecting");
|
setServerConnection("connecting");
|
||||||
|
|
||||||
// remove trailing slash
|
// remove trailing slash
|
||||||
|
@ -42,7 +42,7 @@ export const ServerStatus = () => {
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
setServerConnection("disconnected");
|
setServerConnection("disconnected");
|
||||||
}
|
}
|
||||||
};
|
}, [serverUrl, setServerConnection, setServerUrl]);
|
||||||
|
|
||||||
const chipProps = {
|
const chipProps = {
|
||||||
color: serverConnection === "connected" ? "success" : serverConnection === "disconnected" ? "error" : "warning",
|
color: serverConnection === "connected" ? "success" : serverConnection === "disconnected" ? "error" : "warning",
|
||||||
|
@ -64,7 +64,7 @@ export const ServerStatus = () => {
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [serverConnection, serverUrl]);
|
}, [checkServerConnection, serverConnection, serverUrl]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
|
28
client/src/components/Pages/SqlInjection/SetupLogItem.tsx
Normal file
28
client/src/components/Pages/SqlInjection/SetupLogItem.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Grid2 } from "@mui/material";
|
||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
type SetupLogItemProps = {
|
||||||
|
children?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SetupLogItem: FC<SetupLogItemProps> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<Grid2
|
||||||
|
color="lightgreen"
|
||||||
|
container
|
||||||
|
fontFamily="JetBrainsMono"
|
||||||
|
fontSize="0.9rem"
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
<Grid2 size="auto">></Grid2>
|
||||||
|
<Grid2
|
||||||
|
size="grow"
|
||||||
|
sx={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
);
|
||||||
|
};
|
|
@ -3,13 +3,14 @@ import { Box, Divider, Tab, useTheme } from "@mui/material";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { SqlInjectionLogin } from "./SqlInjectionLogin";
|
import { SqlInjectionLogin } from "./SqlInjectionLogin";
|
||||||
import { SqlInjectionRegister } from "./SqlInjectionRegister";
|
import { SqlInjectionRegister } from "./SqlInjectionRegister";
|
||||||
|
import { SqlInjectionSetup } from "./SqlInjectionSetup";
|
||||||
|
|
||||||
export const SqlInjection = () => {
|
export const SqlInjection = () => {
|
||||||
// contexts
|
// contexts
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
// states
|
// states
|
||||||
const [subTabValue, setSubTabValue] = useState("register");
|
const [subTabValue, setSubTabValue] = useState("setup");
|
||||||
|
|
||||||
// logic for switching tabs
|
// logic for switching tabs
|
||||||
const subTabChangeEvent = (newTabValue: string) => {
|
const subTabChangeEvent = (newTabValue: string) => {
|
||||||
|
@ -37,7 +38,7 @@ export const SqlInjection = () => {
|
||||||
>
|
>
|
||||||
<TabContext value={subTabValue}>
|
<TabContext value={subTabValue}>
|
||||||
<TabList
|
<TabList
|
||||||
onChange={(e, value) => {
|
onChange={(_, value) => {
|
||||||
subTabChangeEvent(value);
|
subTabChangeEvent(value);
|
||||||
}}
|
}}
|
||||||
scrollButtons={true}
|
scrollButtons={true}
|
||||||
|
@ -46,6 +47,10 @@ export const SqlInjection = () => {
|
||||||
}}
|
}}
|
||||||
variant="standard"
|
variant="standard"
|
||||||
>
|
>
|
||||||
|
<Tab
|
||||||
|
label="Setup"
|
||||||
|
value="setup"
|
||||||
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="Register"
|
label="Register"
|
||||||
value="register"
|
value="register"
|
||||||
|
@ -66,6 +71,12 @@ export const SqlInjection = () => {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<TabPanel
|
||||||
|
sx={{ p: 2 }}
|
||||||
|
value="setup"
|
||||||
|
>
|
||||||
|
<SqlInjectionSetup />
|
||||||
|
</TabPanel>
|
||||||
<TabPanel
|
<TabPanel
|
||||||
sx={{ p: 2 }}
|
sx={{ p: 2 }}
|
||||||
value="register"
|
value="register"
|
||||||
|
|
120
client/src/components/Pages/SqlInjection/SqlInjectionSetup.tsx
Normal file
120
client/src/components/Pages/SqlInjection/SqlInjectionSetup.tsx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import { LoadingButton } from "@mui/lab";
|
||||||
|
import { Box, FormControlLabel, Grid, Grid2, Switch, TextField, Typography } from "@mui/material";
|
||||||
|
import { fetch } from "@tauri-apps/plugin-http";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useNotification } from "../../../contexts/NotificationContext";
|
||||||
|
import { serverUrlAtom } from "../../../lib/jotai";
|
||||||
|
import { HeaderLogo } from "../../Generic/HeaderLogo";
|
||||||
|
import { SetupLogItem } from "./SetupLogItem";
|
||||||
|
|
||||||
|
export const SqlInjectionSetup = () => {
|
||||||
|
// contexts
|
||||||
|
const { openNotification } = useNotification();
|
||||||
|
|
||||||
|
// atoms
|
||||||
|
const [serverUrl, setServerUrl] = useAtom(serverUrlAtom);
|
||||||
|
|
||||||
|
// states
|
||||||
|
const [logs, setLogs] = useState<string[]>([]);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const setupClickEvent = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(serverUrl + "/nuke-db", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
const responseText = await response.text();
|
||||||
|
if (!response.ok) {
|
||||||
|
openNotification("Nuke failed: " + responseText);
|
||||||
|
} else {
|
||||||
|
openNotification("Nuke successful");
|
||||||
|
}
|
||||||
|
|
||||||
|
setLogs((prevLogs) => [...prevLogs.slice(-5), responseText]);
|
||||||
|
|
||||||
|
const response2 = await fetch(serverUrl + "/setup-demo-db", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
const responseText2 = await response2.text();
|
||||||
|
if (!response.ok) {
|
||||||
|
openNotification("Setup demo DB failed: " + responseText2);
|
||||||
|
} else {
|
||||||
|
openNotification("Demo database setup successful");
|
||||||
|
}
|
||||||
|
|
||||||
|
setLogs((prevLogs) => [...prevLogs.slice(-5), responseText2]);
|
||||||
|
} catch (e) {
|
||||||
|
// log the error and handle failure
|
||||||
|
console.log("Request failed", e);
|
||||||
|
} finally {
|
||||||
|
// stop loading indicator regardless of success/failure
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
alignItems="center"
|
||||||
|
display="flex"
|
||||||
|
flexDirection="row"
|
||||||
|
sx={{
|
||||||
|
mb: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HeaderLogo />
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
color: "grey",
|
||||||
|
}}
|
||||||
|
variant="h6"
|
||||||
|
>
|
||||||
|
Setup
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
maxHeight: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2">
|
||||||
|
Click on the 'Setup/reset DB' button below to initialize or reset your database.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">
|
||||||
|
If you get an error, make sure you have set the correct server URL (check the chip at the top right).
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">
|
||||||
|
Demo email:{" "}
|
||||||
|
<b>
|
||||||
|
<code style={{ fontFamily: "JetBrainsMono" }}>asdf@gmail.com</code>
|
||||||
|
</b>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">
|
||||||
|
Demo password:{" "}
|
||||||
|
<b>
|
||||||
|
<code style={{ fontFamily: "JetBrainsMono" }}>asdf</code>
|
||||||
|
</b>
|
||||||
|
</Typography>
|
||||||
|
<LoadingButton
|
||||||
|
loading={loading}
|
||||||
|
onClick={setupClickEvent}
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
my: 2,
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Setup/reset DB
|
||||||
|
</LoadingButton>
|
||||||
|
{logs.map((log, index) => (
|
||||||
|
<SetupLogItem key={index}>{log}</SetupLogItem>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,10 +0,0 @@
|
||||||
import { Box } from "@mui/material";
|
|
||||||
|
|
||||||
export const Xss = () => {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<h1>XSS</h1>
|
|
||||||
<p>Cross-site scripting (XSS) is a type of security vulnerability</p>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,74 +0,0 @@
|
||||||
import { BugReportOutlined } from "@mui/icons-material";
|
|
||||||
import { Box, Button, IconButton, useTheme } from "@mui/material";
|
|
||||||
import { fetch } from "@tauri-apps/plugin-http";
|
|
||||||
import { useAtom } from "jotai";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { serverUrlAtom } from "../../lib/jotai";
|
|
||||||
import { defaultSettings } from "../../lib/settings";
|
|
||||||
import { FloatingDialog } from "../Generic/FloatingDialog";
|
|
||||||
|
|
||||||
export const Testing = () => {
|
|
||||||
// contexts
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
// atoms
|
|
||||||
const [serverUrl, setServerUrl] = useAtom(serverUrlAtom);
|
|
||||||
|
|
||||||
// states
|
|
||||||
const [openState, setOpenState] = useState(false);
|
|
||||||
const [maximisedState, setMaximisedState] = useState(false);
|
|
||||||
|
|
||||||
// functions
|
|
||||||
const close = () => setOpenState(false);
|
|
||||||
|
|
||||||
const testing = () => {
|
|
||||||
fetch(serverUrl + "/nuke-db").then((response) => {
|
|
||||||
console.log(response);
|
|
||||||
});
|
|
||||||
fetch(serverUrl + "/setup-demo-db").then((response) => {
|
|
||||||
console.log(response);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FloatingDialog
|
|
||||||
body={
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
border: "1px solid " + theme.palette.grey[700],
|
|
||||||
borderRadius: defaultSettings.style.radius + "px",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
flexGrow: 1,
|
|
||||||
my: 2,
|
|
||||||
overflow: "hidden",
|
|
||||||
p: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
testing();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
test
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
close={close}
|
|
||||||
maximisedState={maximisedState}
|
|
||||||
openButton={
|
|
||||||
<IconButton
|
|
||||||
onClick={() => setOpenState(true)}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<BugReportOutlined />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
openState={openState}
|
|
||||||
setMaximisedState={setMaximisedState}
|
|
||||||
title="Testing"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -2,6 +2,7 @@ services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
container_name: postgres_db
|
container_name: postgres_db
|
||||||
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: asdfuser
|
POSTGRES_USER: asdfuser
|
||||||
POSTGRES_PASSWORD: asdfpassword
|
POSTGRES_PASSWORD: asdfpassword
|
||||||
|
@ -14,7 +15,7 @@ services:
|
||||||
modsecurity:
|
modsecurity:
|
||||||
image: owasp/modsecurity-crs:apache-alpine
|
image: owasp/modsecurity-crs:apache-alpine
|
||||||
container_name: modsecurity
|
container_name: modsecurity
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
BACKEND: "http://localhost:80"
|
BACKEND: "http://localhost:80"
|
||||||
SERVER_NAME: "localhost"
|
SERVER_NAME: "localhost"
|
||||||
|
@ -36,9 +37,27 @@ services:
|
||||||
dvwa:
|
dvwa:
|
||||||
image: vulnerables/web-dvwa
|
image: vulnerables/web-dvwa
|
||||||
container_name: dvwa
|
container_name: dvwa
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
|
|
||||||
|
cspj-server:
|
||||||
|
image: cspj-server
|
||||||
|
container_name: cspj-server
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
volumes:
|
||||||
|
- "/home/vomitblood/build/cspj-application/docker/chungus/logs/:/tmp"
|
||||||
|
command: ["server", "-l", "/tmp"]
|
||||||
|
|
||||||
|
cspj-ml-server:
|
||||||
|
image: cspj-ml-server
|
||||||
|
container_name: cspj-ml-server
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
|
1
docker/chungus/remove.sh
Normal file
1
docker/chungus/remove.sh
Normal file
|
@ -0,0 +1 @@
|
||||||
|
docker container rm modsecurity suricata dvwa postgres_db
|
|
@ -1,7 +1,11 @@
|
||||||
mkdir logs
|
mkdir logs
|
||||||
|
|
||||||
touch ./logs/host-fs-auditlog.log
|
touch ./logs/host-fs-auditlog.log
|
||||||
touch ./logs/host-fs-errorlog.log
|
touch ./logs/host-fs-errorlog.log
|
||||||
touch ./logs/host-fs-accesslog.log
|
touch ./logs/host-fs-accesslog.log
|
||||||
|
|
||||||
chmod 777 ./logs/host-fs-auditlog.log
|
chmod 777 ./logs/host-fs-auditlog.log
|
||||||
chmod 777 ./logs/host-fs-errorlog.log
|
chmod 777 ./logs/host-fs-errorlog.log
|
||||||
chmod 777 ./logs/host-fs-accesslog.log
|
chmod 777 ./logs/host-fs-accesslog.log
|
||||||
|
|
||||||
|
docker compose up
|
|
@ -14,5 +14,5 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ./suricata/etc:/etc/suricata
|
- ./suricata/etc:/etc/suricata
|
||||||
- ./suricata/logs:/var/log/suricata
|
- ./suricata/logs:/var/log/suricata
|
||||||
- ./suricata/lib:/var/lib/suricata
|
# - ./suricata/lib:/var/lib/suricata
|
||||||
command: ["-c", "/etc/suricata/suricata.yaml", "--af-packet"]
|
command: ["-c", "/etc/suricata/suricata.yaml", "--af-packet"]
|
||||||
|
|
11
server-ml/Dockerfile
Normal file
11
server-ml/Dockerfile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
FROM python:3.11.6-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY main.py .
|
||||||
|
|
||||||
|
CMD ["python", "main.py"]
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Test Cases
|
||||||
|
|
||||||
|
## 1. Basic test cases
|
||||||
|
|
||||||
|
[Good] `SELECT _ FROM users WHERE id = 1`
|
||||||
|
[Bad] `SELECT _ FROM users WHERE id = 1 OR 1=1`
|
||||||
|
|
||||||
|
## 2. Authentication bypass cases
|
||||||
|
|
||||||
|
[Bad] `SELECT _ FROM users WHERE username = 'admin' --`
|
||||||
|
[Bad] `SELECT _ FROM users WHERE username = 'admin' #`
|
||||||
|
[Bad] `SELECT \* FROM users WHERE username = 'admin' OR '1'='1'`
|
||||||
|
|
||||||
|
## 3. Union based injection cases
|
||||||
|
|
||||||
|
[Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT null, 'hacker'`
|
||||||
|
[Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT 1, 'hacked' FROM dual`
|
||||||
|
[Bad] `SELECT database() UNION SELECT 1`
|
||||||
|
|
||||||
|
## 4. Error based injection cases
|
||||||
|
|
||||||
|
[Bad] `SELECT _ FROM users WHERE id = 1 AND (SELECT 1 FROM users WHERE id=2)=1`
|
||||||
|
[Bad] `SELECT _ FROM users WHERE id = (SELECT COUNT(\*) FROM users)`
|
||||||
|
|
||||||
|
## 5. Blind SQL injection cases
|
||||||
|
|
||||||
|
[Bad] `SELECT _ FROM users WHERE id = 1; WAITFOR DELAY '00:00:10' --`
|
||||||
|
[Bad] `SELECT _ FROM users WHERE username = 'admin' AND 1=1`
|
||||||
|
|
||||||
|
## 6. Hex and Base64 encoded injection cases
|
||||||
|
|
||||||
|
[Bad] `SELECT _ FROM users WHERE username = 0x61646D696E`
|
||||||
|
[Bad] `SELECT _ FROM users WHERE username = 'YWRtaW4='`
|
||||||
|
|
||||||
|
## 7. False positives cases
|
||||||
|
|
||||||
|
[Good] `SELECT _ FROM users WHERE id = 5`
|
||||||
|
[Good] `SELECT users.name, orders.amount FROM users JOIN orders ON users.id = orders.user_id`
|
||||||
|
[Good] `SELECT _ FROM users WHERE username = ? AND password = ?`
|
||||||
|
|
||||||
|
## 8. Edge cases
|
||||||
|
|
||||||
|
[Good] `""`
|
||||||
|
[Bad] `'; --`
|
||||||
|
[Good] `12345`
|
||||||
|
[Good] `asdkjhasdkjh`
|
7
server-ml/docker-compose.yml
Normal file
7
server-ml/docker-compose.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
services:
|
||||||
|
cspj-ml-server:
|
||||||
|
image: cspj-ml-server
|
||||||
|
container_name: cspj-ml-server
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
|
@ -2,19 +2,24 @@ from flask import Flask, request, jsonify
|
||||||
import torch
|
import torch
|
||||||
from transformers import MobileBertTokenizer, MobileBertForSequenceClassification
|
from transformers import MobileBertTokenizer, MobileBertForSequenceClassification
|
||||||
|
|
||||||
# Initialize Flask app
|
print("Starting server...")
|
||||||
|
|
||||||
|
# initialize Flask app
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# Set device (GPU if available, otherwise CPU)
|
# set device, use gpu if available
|
||||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
device = torch.device("cpu")
|
||||||
|
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||||
|
|
||||||
# Load tokenizer and model
|
# load tokenizer and model
|
||||||
|
print("Loading model...")
|
||||||
tokenizer = MobileBertTokenizer.from_pretrained("google/mobilebert-uncased")
|
tokenizer = MobileBertTokenizer.from_pretrained("google/mobilebert-uncased")
|
||||||
model = MobileBertForSequenceClassification.from_pretrained("cssupport/mobilebert-sql-injection-detect")
|
model = MobileBertForSequenceClassification.from_pretrained("cssupport/mobilebert-sql-injection-detect")
|
||||||
model.to(device)
|
model.to(device)
|
||||||
model.eval()
|
model.eval()
|
||||||
|
print("Model loaded")
|
||||||
|
|
||||||
# Function to predict SQL injection
|
# function for model to predict sql injection
|
||||||
def predict(text):
|
def predict(text):
|
||||||
inputs = tokenizer(text, padding=False, truncation=True, return_tensors="pt", max_length=512)
|
inputs = tokenizer(text, padding=False, truncation=True, return_tensors="pt", max_length=512)
|
||||||
input_ids = inputs["input_ids"].to(device)
|
input_ids = inputs["input_ids"].to(device)
|
||||||
|
@ -30,7 +35,7 @@ def predict(text):
|
||||||
|
|
||||||
return predicted_class, confidence
|
return predicted_class, confidence
|
||||||
|
|
||||||
# Define API endpoint
|
# the api endpoint
|
||||||
@app.route("/predict", methods=["POST"])
|
@app.route("/predict", methods=["POST"])
|
||||||
def classify_query():
|
def classify_query():
|
||||||
data = request.json
|
data = request.json
|
||||||
|
@ -40,16 +45,16 @@ def classify_query():
|
||||||
query = data["query"]
|
query = data["query"]
|
||||||
predicted_class, confidence = predict(query)
|
predicted_class, confidence = predict(query)
|
||||||
|
|
||||||
# Thresholding (if confidence > 0.7, mark as SQL Injection)
|
# if >0.7, then mark as bad
|
||||||
is_vulnerable = predicted_class == 1 and confidence > 0.7
|
is_vulnerable = predicted_class == 1 and confidence > 0.7
|
||||||
result = {
|
result = {
|
||||||
"query": query,
|
"query": query,
|
||||||
"classification": "SQL Injection Detected" if is_vulnerable else "No SQL Injection Detected",
|
"result": "fail" if is_vulnerable else "pass",
|
||||||
"confidence": round(confidence, 2)
|
"confidence": round(confidence, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonify(result)
|
return jsonify(result)
|
||||||
|
|
||||||
# Run Flask server
|
# run the flask server
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
app.run(host="0.0.0.0", port=5000, debug=False)
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "server-ml"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Add your description here"
|
description = "Add your description here"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctransformers>=0.2.27",
|
"ctransformers>=0.2.27",
|
||||||
"flask>=3.1.0",
|
"flask>=3.1.0",
|
||||||
|
|
|
@ -1,3 +1,48 @@
|
||||||
flask
|
accelerate==1.3.0
|
||||||
scikit-learn
|
blinker==1.9.0
|
||||||
joblib
|
certifi==2025.1.31
|
||||||
|
charset-normalizer==3.4.1
|
||||||
|
click==8.1.8
|
||||||
|
ctransformers==0.2.27
|
||||||
|
filelock==3.17.0
|
||||||
|
flask==3.1.0
|
||||||
|
fsspec==2025.2.0
|
||||||
|
huggingface-hub==0.28.1
|
||||||
|
idna==3.10
|
||||||
|
itsdangerous==2.2.0
|
||||||
|
jinja2==3.1.5
|
||||||
|
markupsafe==3.0.2
|
||||||
|
mpmath==1.3.0
|
||||||
|
networkx==3.4.2
|
||||||
|
numpy==2.2.2
|
||||||
|
nvidia-cublas-cu12==12.4.5.8
|
||||||
|
nvidia-cuda-cupti-cu12==12.4.127
|
||||||
|
nvidia-cuda-nvrtc-cu12==12.4.127
|
||||||
|
nvidia-cuda-runtime-cu12==12.4.127
|
||||||
|
nvidia-cudnn-cu12==9.1.0.70
|
||||||
|
nvidia-cufft-cu12==11.2.1.3
|
||||||
|
nvidia-curand-cu12==10.3.5.147
|
||||||
|
nvidia-cusolver-cu12==11.6.1.9
|
||||||
|
nvidia-cusparse-cu12==12.3.1.170
|
||||||
|
nvidia-cusparselt-cu12==0.6.2
|
||||||
|
nvidia-nccl-cu12==2.21.5
|
||||||
|
nvidia-nvjitlink-cu12==12.4.127
|
||||||
|
nvidia-nvtx-cu12==12.4.127
|
||||||
|
packaging==24.2
|
||||||
|
peft==0.14.0
|
||||||
|
psutil==6.1.1
|
||||||
|
py-cpuinfo==9.0.0
|
||||||
|
pyyaml==6.0.2
|
||||||
|
regex==2024.11.6
|
||||||
|
requests==2.32.3
|
||||||
|
safetensors==0.5.2
|
||||||
|
setuptools==75.8.0
|
||||||
|
sympy==1.13.1
|
||||||
|
tokenizers==0.21.0
|
||||||
|
torch==2.6.0
|
||||||
|
tqdm==4.67.1
|
||||||
|
transformers==4.48.2
|
||||||
|
triton==3.2.0
|
||||||
|
typing-extensions==4.12.2
|
||||||
|
urllib3==2.3.0
|
||||||
|
werkzeug==3.1.3
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
# Test Cases
|
|
||||||
|
|
||||||
## 1. Basic test cases
|
|
||||||
|
|
||||||
[Good] `SELECT _ FROM users WHERE id = 1`
|
|
||||||
[Bad] `SELECT _ FROM users WHERE id = 1 OR 1=1`
|
|
||||||
|
|
||||||
## 2. Authentication bypass cases
|
|
||||||
|
|
||||||
[Bad] `SELECT _ FROM users WHERE username = 'admin' --`
|
|
||||||
[Bad] `SELECT _ FROM users WHERE username = 'admin' #`
|
|
||||||
[Bad] `SELECT \* FROM users WHERE username = 'admin' OR '1'='1'`
|
|
||||||
|
|
||||||
## 3. Union based injection cases
|
|
||||||
|
|
||||||
[Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT null, 'hacker'`
|
|
||||||
[Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT 1, 'hacked' FROM dual`
|
|
||||||
[Bad] `SELECT database() UNION SELECT 1`
|
|
||||||
|
|
||||||
## 4. Error based injection cases
|
|
||||||
|
|
||||||
[Bad] `SELECT _ FROM users WHERE id = 1 AND (SELECT 1 FROM users WHERE id=2)=1`
|
|
||||||
[Bad] `SELECT _ FROM users WHERE id = (SELECT COUNT(\*) FROM users)`
|
|
||||||
|
|
||||||
## 5. Blind SQL injection cases
|
|
||||||
|
|
||||||
[Bad] `SELECT _ FROM users WHERE id = 1; WAITFOR DELAY '00:00:10' --`
|
|
||||||
[Bad] `SELECT _ FROM users WHERE username = 'admin' AND 1=1`
|
|
||||||
|
|
||||||
## 6. Hex and Base64 encoded injection cases
|
|
||||||
|
|
||||||
[Bad] `SELECT _ FROM users WHERE username = 0x61646D696E`
|
|
||||||
[Bad] `SELECT _ FROM users WHERE username = 'YWRtaW4='`
|
|
||||||
|
|
||||||
## 7. False positives cases
|
|
||||||
|
|
||||||
[Good] `SELECT _ FROM users WHERE id = 5`
|
|
||||||
[Good] `SELECT users.name, orders.amount FROM users JOIN orders ON users.id = orders.user_id`
|
|
||||||
[Good] `SELECT _ FROM users WHERE username = ? AND password = ?`
|
|
||||||
|
|
||||||
## 8. Edge cases
|
|
||||||
|
|
||||||
[Good] `""`
|
|
||||||
[Bad] `'; --`
|
|
||||||
[Good] `12345`
|
|
||||||
[Good] `asdkjhasdkjh`
|
|
|
@ -1,35 +0,0 @@
|
||||||
import joblib
|
|
||||||
from sklearn.feature_extraction.text import CountVectorizer
|
|
||||||
from sklearn.linear_model import LogisticRegression
|
|
||||||
from sklearn.pipeline import make_pipeline
|
|
||||||
from sklearn.model_selection import train_test_split
|
|
||||||
|
|
||||||
# random data
|
|
||||||
data = [
|
|
||||||
("' OR '1'='1", 1),
|
|
||||||
("SELECT * FROM users WHERE id=1", 1),
|
|
||||||
("DROP TABLE users;", 1),
|
|
||||||
("username=admin'--", 1),
|
|
||||||
("hello world", 0),
|
|
||||||
("this is a normal query", 0),
|
|
||||||
("select data from table", 0),
|
|
||||||
("just another harmless input", 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
queries, labels = zip(*data)
|
|
||||||
|
|
||||||
# split data into training and testing sets
|
|
||||||
X_train, X_test, y_train, y_test = train_test_split(
|
|
||||||
queries, labels, test_size=0.2, random_state=42
|
|
||||||
)
|
|
||||||
|
|
||||||
# build a pipeline with a vectorizer and a logistic regression model
|
|
||||||
pipeline = make_pipeline(CountVectorizer(), LogisticRegression())
|
|
||||||
|
|
||||||
# train the model
|
|
||||||
pipeline.fit(X_train, y_train)
|
|
||||||
|
|
||||||
# save the model to a file
|
|
||||||
joblib.dump(pipeline, "model.pkl")
|
|
||||||
|
|
||||||
print("Model trained and saved to model.pkl")
|
|
7
server/Dockerfile
Normal file
7
server/Dockerfile
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY server /usr/local/bin/server
|
||||||
|
|
||||||
|
RUN chmod +x /usr/local/bin/server
|
2
server/build.sh
Executable file
2
server/build.sh
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server main.go
|
11
server/docker-compose.yml
Normal file
11
server/docker-compose.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
services:
|
||||||
|
cspj-server:
|
||||||
|
image: cspj-server
|
||||||
|
container_name: cspj-server
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
volumes:
|
||||||
|
- "/home/vomitblood/build/cspj-application/docker/chungus/logs/:/tmp"
|
||||||
|
command: ["server", "-l", "/tmp"]
|
24
server/internal/cli/cli.go
Normal file
24
server/internal/cli/cli.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Vomitblood/cspj-application/server/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFlags() {
|
||||||
|
logDir := flag.String("l", "", "Path to the log directory")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// check if the -l flag is provided
|
||||||
|
if flag.Lookup("l").Value.String() == "" {
|
||||||
|
fmt.Println("Error: The -l flag (log directory) is required.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the value in a globally accessible config package
|
||||||
|
config.LogDirectory = *logDir
|
||||||
|
}
|
3
server/internal/config/config.go
Normal file
3
server/internal/config/config.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
var LogDirectory string
|
|
@ -87,6 +87,7 @@ func SetupDemoDb(w http.ResponseWriter, r *http.Request) {
|
||||||
// avoid duplicate entries and specify roles
|
// avoid duplicate entries and specify roles
|
||||||
insertDataSQL := `
|
insertDataSQL := `
|
||||||
INSERT INTO users (email, password, role) VALUES
|
INSERT INTO users (email, password, role) VALUES
|
||||||
|
('asdf@gmail.com', 'asdf', 'user'),
|
||||||
('alice@example.com', 'asdfalicepassword', 'user'),
|
('alice@example.com', 'asdfalicepassword', 'user'),
|
||||||
('bob@example.com', 'asdfbobpassword', 'user'),
|
('bob@example.com', 'asdfbobpassword', 'user'),
|
||||||
('charlie@example.com', 'asdfcharliepassword', 'admin')
|
('charlie@example.com', 'asdfcharliepassword', 'admin')
|
||||||
|
@ -152,7 +153,6 @@ func FetchEmails() (map[string]bool, error) {
|
||||||
emails[email] = true
|
emails[email] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Fetched emails:", emails)
|
|
||||||
return emails, nil
|
return emails, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,17 @@ package log_backup
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Vomitblood/cspj-application/server/internal/config"
|
||||||
"github.com/Vomitblood/cspj-application/server/internal/webdav"
|
"github.com/Vomitblood/cspj-application/server/internal/webdav"
|
||||||
"github.com/studio-b12/gowebdav"
|
"github.com/studio-b12/gowebdav"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: use values from config file
|
|
||||||
var localLogPaths = []string{
|
var localLogPaths = []string{
|
||||||
"/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-accesslog.log",
|
filepath.Join(config.LogDirectory, "host-fs-accesslog.log"),
|
||||||
"/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-auditlog.log",
|
filepath.Join(config.LogDirectory, "host-fs-auditlog.log"),
|
||||||
"/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-errorlog.log",
|
filepath.Join(config.LogDirectory, "host-fs-errorlog.log"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteFiles = []string{
|
var remoteFiles = []string{
|
||||||
|
|
|
@ -5,17 +5,14 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Vomitblood/cspj-application/server/internal/config"
|
||||||
"github.com/Vomitblood/cspj-application/server/internal/telegram"
|
"github.com/Vomitblood/cspj-application/server/internal/telegram"
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// TODO: add config for user to specify log location
|
|
||||||
modsecLogFile = "/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-auditlog.log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var lastReadPosition int64 = 0
|
var lastReadPosition int64 = 0
|
||||||
|
|
||||||
type LogEntry struct {
|
type LogEntry struct {
|
||||||
|
@ -25,16 +22,18 @@ type LogEntry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func WatchFile(bot *tg.BotAPI) {
|
func WatchFile(bot *tg.BotAPI) {
|
||||||
|
modsecLogFile := filepath.Join(config.LogDirectory, "host-fs-auditlog.log")
|
||||||
|
log.Println("Watching logfile directory:", config.LogDirectory)
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to initialize watcher:", err)
|
log.Fatal("Failed to initialize watcher: ", err)
|
||||||
}
|
}
|
||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
|
|
||||||
// add log file to watcher
|
// add log file to watcher
|
||||||
err = watcher.Add(modsecLogFile)
|
err = watcher.Add(modsecLogFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to watch log file:", err)
|
log.Fatal("Failed to watch log file: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Monitoring log file for changes...")
|
log.Println("Monitoring log file for changes...")
|
||||||
|
@ -54,15 +53,16 @@ func WatchFile(bot *tg.BotAPI) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("Watcher error:", err)
|
log.Println("Watcher error: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readNewLines(bot *tg.BotAPI) {
|
func readNewLines(bot *tg.BotAPI) {
|
||||||
|
modsecLogFile := filepath.Join(config.LogDirectory, "host-fs-auditlog.log")
|
||||||
file, err := os.Open(modsecLogFile)
|
file, err := os.Open(modsecLogFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to reopen log file:", err)
|
log.Println("Failed to reopen log file: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
@ -77,7 +77,7 @@ func readNewLines(bot *tg.BotAPI) {
|
||||||
|
|
||||||
// try to parse json
|
// try to parse json
|
||||||
if err := json.Unmarshal([]byte(line), &logEntry); err != nil {
|
if err := json.Unmarshal([]byte(line), &logEntry); err != nil {
|
||||||
log.Println("⚠️ Failed to parse JSON:", err)
|
log.Println("⚠️ Failed to parse JSON: ", err)
|
||||||
// skip invalid json lines
|
// skip invalid json lines
|
||||||
// very crucial as modsecurity does not respect the json spec
|
// very crucial as modsecurity does not respect the json spec
|
||||||
continue
|
continue
|
||||||
|
@ -93,6 +93,6 @@ func readNewLines(bot *tg.BotAPI) {
|
||||||
lastReadPosition, _ = file.Seek(0, os.SEEK_CUR)
|
lastReadPosition, _ = file.Seek(0, os.SEEK_CUR)
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
log.Println("Error reading log file:", err)
|
log.Println("Error reading log file: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package sql_injection
|
package sql_injection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -134,6 +135,7 @@ func SecureRegisterSql(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) {
|
func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("/unsecure-login-sql hit")
|
||||||
// decode the json body
|
// decode the json body
|
||||||
var requestData struct {
|
var requestData struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
@ -175,12 +177,18 @@ func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// very secure login endpoint
|
// very secure login endpoint
|
||||||
func SecureLoginSql(w http.ResponseWriter, r *http.Request) {
|
func SecureLoginSql(w http.ResponseWriter, r *http.Request) {
|
||||||
// decode the json body
|
log.Println("/secure-login-sql hit")
|
||||||
var requestData struct {
|
var requestData struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AIResponse struct {
|
||||||
|
Confidence float64 `json:"confidence"`
|
||||||
|
Query string `json:"query"`
|
||||||
|
Result string `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
// declare new json decoder with custom property
|
// declare new json decoder with custom property
|
||||||
jsonDecoder := json.NewDecoder(r.Body)
|
jsonDecoder := json.NewDecoder(r.Body)
|
||||||
// rejects any unknown fields in the json, more strict
|
// rejects any unknown fields in the json, more strict
|
||||||
|
@ -214,6 +222,35 @@ func SecureLoginSql(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make insecure sql command just to send to ai model to check
|
||||||
|
insecureQuery := fmt.Sprintf("SELECT id, email, password FROM users WHERE email = '%s' AND password = '%s'", requestData.Email, requestData.Password)
|
||||||
|
|
||||||
|
// send the query to the ai model server
|
||||||
|
aiReqBody, _ := json.Marshal(map[string]string{"query": insecureQuery})
|
||||||
|
aiResp, err := http.Post("http://127.0.0.1:5000/predict", "application/json", bytes.NewBuffer(aiReqBody))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
log.Println("Failed to reach AI model:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer aiResp.Body.Close()
|
||||||
|
|
||||||
|
var aiResponse AIResponse
|
||||||
|
if err := json.NewDecoder(aiResp.Body).Decode(&aiResponse); err != nil {
|
||||||
|
http.Error(w, "Failed to decode AI response", http.StatusInternalServerError)
|
||||||
|
log.Println("Error decoding AI response:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("AI Response:", aiResponse.Result)
|
||||||
|
|
||||||
|
// reject the original request if ai result is fail
|
||||||
|
if aiResponse.Result == "fail" {
|
||||||
|
http.Error(w, "Potential SQL injection detected", http.StatusForbidden)
|
||||||
|
log.Println("AI detected SQL injection attempt")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// retrieve the email and password from the database
|
// retrieve the email and password from the database
|
||||||
var id int
|
var id int
|
||||||
var email string
|
var email string
|
||||||
|
|
|
@ -3,6 +3,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/Vomitblood/cspj-application/server/internal/cli"
|
||||||
|
"github.com/Vomitblood/cspj-application/server/internal/config"
|
||||||
"github.com/Vomitblood/cspj-application/server/internal/db"
|
"github.com/Vomitblood/cspj-application/server/internal/db"
|
||||||
"github.com/Vomitblood/cspj-application/server/internal/http_server"
|
"github.com/Vomitblood/cspj-application/server/internal/http_server"
|
||||||
"github.com/Vomitblood/cspj-application/server/internal/log_watcher"
|
"github.com/Vomitblood/cspj-application/server/internal/log_watcher"
|
||||||
|
@ -11,6 +13,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
cli.GetFlags()
|
||||||
|
log.Printf("Log directory set to: %s", config.LogDirectory)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
db.DbPool, err = db.ConnectToDb()
|
db.DbPool, err = db.ConnectToDb()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,7 +32,5 @@ func main() {
|
||||||
// start log watcher
|
// start log watcher
|
||||||
go log_watcher.WatchFile(tgBot)
|
go log_watcher.WatchFile(tgBot)
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
http_server.ServeApi()
|
http_server.ServeApi()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue