11import http from "http" ;
22import fs from "fs" ;
3+ import crypto from "crypto" ;
34import { WebStreamBuffer , getIPAddress , handleData , moduleConfigIsAvailable , redirect_post_golbat } from "./utils" ;
45import { decodePayload , decodePayloadTraffic } from "./parser/proto-parser" ;
56import SampleSaver from "./utils/sample-saver" ;
@@ -18,9 +19,114 @@ const portBind = config["default_port"];
1819// Initialize sample saver
1920const sampleSaver = config . sample_saving ? new SampleSaver ( config . sample_saving ) : null ;
2021
22+ // Authentication setup
23+ const WEB_PASSWORD = config [ "web_password" ] ;
24+ const AUTH_REQUIRED = WEB_PASSWORD !== null && WEB_PASSWORD !== undefined && WEB_PASSWORD !== "" ;
25+ const sessions = new Set < string > ( ) ;
26+
27+ // Helper functions for authentication
28+ function generateSessionToken ( ) : string {
29+ return crypto . randomBytes ( 32 ) . toString ( "hex" ) ;
30+ }
31+
32+ function parseCookies ( cookieHeader : string | undefined ) : Record < string , string > {
33+ const cookies : Record < string , string > = { } ;
34+ if ( ! cookieHeader ) return cookies ;
35+
36+ cookieHeader . split ( ';' ) . forEach ( cookie => {
37+ const parts = cookie . trim ( ) . split ( '=' ) ;
38+ if ( parts . length === 2 ) {
39+ cookies [ parts [ 0 ] ] = parts [ 1 ] ;
40+ }
41+ } ) ;
42+ return cookies ;
43+ }
44+
45+ function isAuthenticated ( req : http . IncomingMessage ) : boolean {
46+ if ( ! AUTH_REQUIRED ) return true ;
47+
48+ const cookies = parseCookies ( req . headers . cookie ) ;
49+ const sessionToken = cookies [ 'session_token' ] ;
50+ return ! ! ( sessionToken && sessions . has ( sessionToken ) ) ;
51+ }
52+
53+ function requireAuth ( req : http . IncomingMessage , res : http . ServerResponse ) : boolean {
54+ if ( ! isAuthenticated ( req ) ) {
55+ res . writeHead ( 302 , { Location : '/login' } ) ;
56+ res . end ( ) ;
57+ return false ;
58+ }
59+ return true ;
60+ }
61+
2162// server
2263const httpServer = http . createServer ( function ( req , res ) {
2364 let incomingData : Array < Buffer > = [ ] ;
65+
66+ // Authentication routes
67+ if ( req . url === "/login" && req . method === "GET" ) {
68+ if ( isAuthenticated ( req ) ) {
69+ res . writeHead ( 302 , { Location : '/' } ) ;
70+ res . end ( ) ;
71+ return ;
72+ }
73+ res . writeHead ( 200 , { "Content-Type" : "text/html" } ) ;
74+ const loginHTML = fs . readFileSync ( "./dist/views/login.html" ) ;
75+ res . end ( loginHTML ) ;
76+ return ;
77+ }
78+
79+ if ( req . url === "/auth/login" && req . method === "POST" ) {
80+ req . on ( "data" , function ( chunk ) {
81+ incomingData . push ( chunk ) ;
82+ } ) ;
83+ req . on ( "end" , function ( ) {
84+ try {
85+ const requestData = incomingData . join ( "" ) ;
86+ const parsedData = JSON . parse ( requestData ) ;
87+
88+ if ( parsedData . password === WEB_PASSWORD ) {
89+ const sessionToken = generateSessionToken ( ) ;
90+ sessions . add ( sessionToken ) ;
91+
92+ res . writeHead ( 200 , {
93+ "Content-Type" : "application/json" ,
94+ "Set-Cookie" : `session_token=${ sessionToken } ; HttpOnly; Path=/; Max-Age=86400`
95+ } ) ;
96+ res . end ( JSON . stringify ( { success : true } ) ) ;
97+ } else {
98+ res . writeHead ( 401 , { "Content-Type" : "application/json" } ) ;
99+ res . end ( JSON . stringify ( { success : false , message : "Invalid password" } ) ) ;
100+ }
101+ } catch ( error ) {
102+ res . writeHead ( 400 , { "Content-Type" : "application/json" } ) ;
103+ res . end ( JSON . stringify ( { success : false , message : "Invalid request" } ) ) ;
104+ }
105+ } ) ;
106+ return ;
107+ }
108+
109+ if ( req . url === "/auth/logout" && req . method === "POST" ) {
110+ const cookies = parseCookies ( req . headers . cookie ) ;
111+ const sessionToken = cookies [ 'session_token' ] ;
112+ if ( sessionToken ) {
113+ sessions . delete ( sessionToken ) ;
114+ }
115+
116+ res . writeHead ( 200 , {
117+ "Content-Type" : "application/json" ,
118+ "Set-Cookie" : "session_token=; HttpOnly; Path=/; Max-Age=0"
119+ } ) ;
120+ res . end ( JSON . stringify ( { success : true } ) ) ;
121+ return ;
122+ }
123+
124+ if ( req . url === "/auth/status" && req . method === "GET" ) {
125+ res . writeHead ( 200 , { "Content-Type" : "application/json" } ) ;
126+ res . end ( JSON . stringify ( { authRequired : AUTH_REQUIRED } ) ) ;
127+ return ;
128+ }
129+
24130 switch ( req . url ) {
25131 case "/golbat" :
26132 req . on ( "data" , function ( chunk ) {
@@ -158,26 +264,31 @@ const httpServer = http.createServer(function (req, res) {
158264 } ) ;
159265 break ;
160266 case "/images/favicon.png" :
267+ if ( ! requireAuth ( req , res ) ) break ;
161268 res . writeHead ( 200 , { "Content-Type" : "image/png" } ) ;
162269 const favicon = fs . readFileSync ( "./dist/views/images/favicon.png" ) ;
163270 res . end ( favicon ) ;
164271 break ;
165272 case "/css/style.css" :
273+ if ( ! requireAuth ( req , res ) ) break ;
166274 res . writeHead ( 200 , { "Content-Type" : "text/css" } ) ;
167275 const pageCssL = fs . readFileSync ( "./dist/views/css/style.css" ) ;
168276 res . end ( pageCssL ) ;
169277 break ;
170278 case "/json-viewer/jquery.json-viewer.css" :
279+ if ( ! requireAuth ( req , res ) ) break ;
171280 res . writeHead ( 200 , { "Content-Type" : "text/css" } ) ;
172281 const pageCss = fs . readFileSync ( "node_modules/jquery.json-viewer/json-viewer/jquery.json-viewer.css" ) ;
173282 res . end ( pageCss ) ;
174283 break ;
175284 case "/json-viewer/jquery.json-viewer.js" :
285+ if ( ! requireAuth ( req , res ) ) break ;
176286 res . writeHead ( 200 , { "Content-Type" : "text/javascript" } ) ;
177287 const pageJs = fs . readFileSync ( "node_modules/jquery.json-viewer/json-viewer/jquery.json-viewer.js" ) ;
178288 res . end ( pageJs ) ;
179289 break ;
180290 case "/" :
291+ if ( ! requireAuth ( req , res ) ) break ;
181292 res . writeHead ( 200 , { "Content-Type" : "text/html" } ) ;
182293 const pageHTML = fs . readFileSync ( "./dist/views/print-protos.html" ) ;
183294 res . end ( pageHTML ) ;
@@ -189,6 +300,22 @@ const httpServer = http.createServer(function (req, res) {
189300} ) ;
190301
191302var io = require ( "socket.io" ) ( httpServer ) ;
303+
304+ // Socket.IO authentication middleware
305+ if ( AUTH_REQUIRED ) {
306+ io . use ( ( socket , next ) => {
307+ const cookieHeader = socket . handshake . headers . cookie ;
308+ const cookies = parseCookies ( cookieHeader ) ;
309+ const sessionToken = cookies [ 'session_token' ] ;
310+
311+ if ( sessionToken && sessions . has ( sessionToken ) ) {
312+ next ( ) ;
313+ } else {
314+ next ( new Error ( 'Authentication required' ) ) ;
315+ }
316+ } ) ;
317+ }
318+
192319var incoming = io . of ( "/incoming" ) . on ( "connection" , function ( socket ) {
193320 const reader = {
194321 read : function ( data : object ) {
@@ -221,9 +348,12 @@ var outgoing = io.of("/outgoing").on("connection", function (socket) {
221348
222349httpServer . keepAliveTimeout = 0 ;
223350httpServer . listen ( portBind , function ( ) {
351+ const authStatus = AUTH_REQUIRED ? "ENABLED - Password required to access web UI" : "DISABLED" ;
224352 const welcome = `
225353Server start access of this in urls: http://localhost:${ portBind } or WLAN mode http://${ getIPAddress ( ) } :${ portBind } .
226-
354+
355+ - Web Authentication: ${ authStatus }
356+
227357 - Clients MITM:
228358 1) --=FurtiF™=- Tools EndPoints: http://${ getIPAddress ( ) } :${ portBind } /traffic or http://${ getIPAddress ( ) } :${ portBind } /golbat (depending on the modes chosen)
229359 2) If Other set here...
0 commit comments