From 77baf5570e941387a4c8279a80c1de7018e0d876 Mon Sep 17 00:00:00 2001 From: webpwnized Date: Sat, 9 Nov 2024 23:07:15 -0500 Subject: [PATCH 1/5] 2.11.24 Update accounts table --- src/view-account-profile.php | 101 +++++++++++++++++------------------ 1 file changed, 48 insertions(+), 53 deletions(-) diff --git a/src/view-account-profile.php b/src/view-account-profile.php index 58e33fe..78df226 100644 --- a/src/view-account-profile.php +++ b/src/view-account-profile.php @@ -122,62 +122,57 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
User Profile
ID
First Name
Last Name
Username
Password
Signature
Account Type
Client ID
Client Secret
+ - From d59c8d79a4af8f3e0b8ff56de99cfc04af83e8e1 Mon Sep 17 00:00:00 2001 From: webpwnized Date: Sat, 9 Nov 2024 23:10:35 -0500 Subject: [PATCH 2/5] 2.11.24 Update accounts table --- src/view-account-profile.php | 101 ++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/src/view-account-profile.php b/src/view-account-profile.php index 78df226..58e33fe 100644 --- a/src/view-account-profile.php +++ b/src/view-account-profile.php @@ -122,57 +122,62 @@ - - - + + From 5b204ecd482f10d6282fa736f12898973c65a66d Mon Sep 17 00:00:00 2001 From: webpwnized Date: Sat, 9 Nov 2024 23:31:55 -0500 Subject: [PATCH 3/5] 2.11.24 Update accounts table --- src/classes/SQLQueryHandler.php | 735 ++++++------------------------ src/webservices/rest/ws-login.php | 2 +- 2 files changed, 143 insertions(+), 594 deletions(-) diff --git a/src/classes/SQLQueryHandler.php b/src/classes/SQLQueryHandler.php index 16de67a..488243f 100755 --- a/src/classes/SQLQueryHandler.php +++ b/src/classes/SQLQueryHandler.php @@ -1,595 +1,144 @@ mSecurityLevel = $pSecurityLevel; - - switch ($this->mSecurityLevel){ - default: // Default case: This code is insecure, we are not encoding output - case "0": // This code is insecure, we are not encoding output - case "1": // This code is insecure, we are not encoding output - $this->encodeOutput = false; - $this->stopSQLInjection = false; - $this->mLimitOutput = false; - break; - - case "2": - case "3": - case "4": - case "5": // This code is fairly secure - // If we are secure, then we encode all output. - $this->encodeOutput = true; - $this->stopSQLInjection = true; - $this->mLimitOutput = true; - break; - }// end switch - }// end function - - private function generateClientID($length = 16 /* 16 bytes = 128 bits */){ - // Generates a secure 16-byte token for use as a Client ID - // The token is generated using a cryptographically secure pseudorandom number generator - // The token is then converted to hexadecimal format - // The token will be 32 characters long - return bin2hex(random_bytes($length)); - } - - private function generateClientSecret($length = 32 /* 32 bytes = 256 bits */){ - // Generates a secure 32-byte token for use in API calls - // The token is generated using a cryptographically secure pseudorandom number generator - // The token is then converted to hexadecimal format - // The token will be 64 characters long - return bin2hex(random_bytes($length)); - } - - public function __construct($pSecurityLevel = 0) { - // Ensure the provided level is valid; fall back to 0 if it's not. - if (!is_int($pSecurityLevel) || $pSecurityLevel < 0 || $pSecurityLevel > 5) { - $pSecurityLevel = 0; - } - - $this->doSetSecurityLevel($pSecurityLevel); - - //initialize encoder - require_once __SITE_ROOT__.'/classes/EncodingHandler.php'; - $this->mEncoder = new EncodingHandler(); - - /* Initialize MySQL Connection handler */ - require_once 'MySQLHandler.php'; - $this->mMySQLHandler = new MySQLHandler($pSecurityLevel); - $this->mMySQLHandler->connectToDefaultDatabase(); - - }// end function - - public function setSecurityLevel($pSecurityLevel){ - $this->doSetSecurityLevel($pSecurityLevel); - $this->mMySQLHandler->setSecurityLevel($pSecurityLevel); - }// end function - - public function getSecurityLevel(){ - return $this->mSecurityLevel; - }// end function - - public function affected_rows(){ - return $this->mMySQLHandler->affected_rows(); - }//end function - - /* ************************************************************** - * BEGIN: Queries * - ****************************************************************/ - public function escapeDangerousCharacters($pData){ - return $this->mMySQLHandler->escapeDangerousCharacters($pData); - } - - public function getSecurityLevelFromDB() { - // Query to retrieve the security level from the row with id = 1 - $lQueryString = "SELECT level FROM security_level WHERE id = 1"; - - // Execute the query - $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); - - // Check if the query returned a valid result - if ($lQueryResult && $lQueryResult->num_rows > 0) { - $lRow = $lQueryResult->fetch_assoc(); - return (int) $lRow['level']; // Return the level as an integer - } else { - return null; // Return null if the row does not exist - } - } // end function getSecurityLevelFromDB - - public function setSecurityLevelInDB($pLevel) { - if ($pLevel < 0 || $pLevel > 5) { - throw new InvalidArgumentException("Security level must be between 0 and 5."); - } - - $safeLevel = (int) $pLevel; - $lQueryString = "UPDATE security_level SET level = $safeLevel WHERE id = 1"; - $this->mMySQLHandler->executeQuery($lQueryString); - - // Ensure the row was actually updated - return $this->mMySQLHandler->affected_rows() > 0; - } // end function setSecurityLevelInDB - - public function getPageHelpTexts($pPageName){ - - if ($this->stopSQLInjection){ - $pPageName = $this->mMySQLHandler->escapeDangerousCharacters($pPageName); - }// end if - - $lQueryString = " - SELECT CONCAT( - '
- ', - help_text, - '
' - ) AS help_text - FROM page_help - INNER JOIN help_texts - ON page_help.help_text_key = help_texts.help_text_key - WHERE page_help.page_name = '" . $pPageName . "' " . - "ORDER BY page_help.order_preference"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getPageHelpTexts - - public function getPageLevelOneHelpIncludeFiles($pPageName){ - - if ($this->stopSQLInjection){ - $pPageName = $this->mMySQLHandler->escapeDangerousCharacters($pPageName); - }// end if - - $lQueryString = " - SELECT level_1_help_include_files.level_1_help_include_file_key, - level_1_help_include_files.level_1_help_include_file_description - FROM page_help - INNER JOIN level_1_help_include_files - ON page_help.help_text_key = - level_1_help_include_files.level_1_help_include_file_key - WHERE page_help.page_name = '" . $pPageName . "' " . - "ORDER BY page_help.order_preference"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getPageLevelOneHelpIncludeFiles - - public function getLevelOneHelpIncludeFile($pIncludeFileKey){ - - if ($this->stopSQLInjection){ - $pIncludeFileKey = $this->mMySQLHandler->escapeDangerousCharacters($pIncludeFileKey); - }// end if - - $lQueryString = " - SELECT level_1_help_include_files.level_1_help_include_file, - level_1_help_include_files.level_1_help_include_file_description - FROM level_1_help_include_files - WHERE level_1_help_include_files.level_1_help_include_file_key = " . $pIncludeFileKey; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getPageLevelOneHelpIncludeFiles - - public function deleteCapturedData(){ - $lQueryString = "TRUNCATE TABLE captured_data"; - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function deleteCapturedData - - public function getCapturedData(){ - $lQueryString = " - SELECT ip_address, hostname, port, user_agent_string, referrer, data, capture_date - FROM captured_data - ORDER BY capture_date DESC"; - - if ($this->mLimitOutput){ - $lQueryString .= " LIMIT 20"; - }// end if - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getCapturedData() - - public function insertVoteIntoUserPoll(/*Text*/ $pToolName, /*Text*/ $pUserName){ - - if ($this->stopSQLInjection){ - $pToolName = $this->mMySQLHandler->escapeDangerousCharacters($pToolName); - $pUserName = $this->mMySQLHandler->escapeDangerousCharacters($pUserName); - }// end if - - $lQueryString = " - INSERT INTO user_poll_results(tool_name, username, date) VALUES ('". - $pToolName . "', '". - $pUserName . "', " . - " now() );"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function insertVoteIntoUserPoll - - public function getUserPollVotes(){ - - $lQueryString = " - SELECT tool_name, COUNT(tool_name) as tool_count - FROM user_poll_results - GROUP BY tool_name"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function insertVoteIntoUserPoll - - public function insertBlogRecord($pBloggerName, $pBlogEntry){ - - if ($this->stopSQLInjection){ - $pBloggerName = $this->mMySQLHandler->escapeDangerousCharacters($pBloggerName); - $pBlogEntry = $this->mMySQLHandler->escapeDangerousCharacters($pBlogEntry); - }// end if - - $lQueryString = " - INSERT INTO blogs_table(blogger_name, comment, date) VALUES ('". - $pBloggerName . "', '". - $pBlogEntry . "', " . - " now() )"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function insertBlogRecord - - public function getBlogRecord($pBloggerName){ - - if ($this->stopSQLInjection){ - $pBloggerName = $this->mMySQLHandler->escapeDangerousCharacters($pBloggerName); - }// end if - - $lQueryString = " - SELECT * FROM blogs_table - WHERE blogger_name like '{$pBloggerName}%' - ORDER BY date DESC - LIMIT 0 , 100"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getBlogRecord - - public function getPenTestTool($pPostedToolID){ - /* - * Note: While escaping works ok in some case, it is not the best defense. - * Using stored procedures is a much stronger defense. - */ - if ($this->stopSQLInjection){ - $pPostedToolID = $this->mMySQLHandler->escapeDangerousCharacters($pPostedToolID); - }// end if - - if ($pPostedToolID != "c84326e4-7487-41d3-91fd-88280828c756"){ - $lWhereClause = " WHERE tool_id = '".$pPostedToolID."';"; - }else{ - $lWhereClause = ";"; - }// end if - - $lQueryString = " - SELECT tool_id, tool_name, phase_to_use, tool_type, comment - FROM pen_test_tools" . - $lWhereClause; - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getPenTestTool - - public function getPenTestTools(){ - /* Note: No possibility of SQL injection because the query - * is static. - */ - $lQueryString = "SELECT tool_id, tool_name FROM pen_test_tools;"; - return $this->mMySQLHandler->executeQuery($lQueryString); - }// end function getPenTestTools - - public function getHitLogEntries(){ - /* Note: No possibility of SQL injection because the query - * is static. - */ - $lLimitString = ""; - if ($this->mLimitOutput){ - $lLimitString .= " LIMIT 20"; - }// end if - - $lQueryString = "SELECT * FROM `hitlog` ORDER BY date DESC".$lLimitString.";"; - return $this->mMySQLHandler->executeQuery($lQueryString); - }// end function getHitLogEntries - - public function getYouTubeVideo($pRecordIdentifier){ - /* - * Note: While escaping works ok in some case, it is not the best defense. - * Using stored procedures is a much stronger defense. - */ - if ($this->stopSQLInjection){ - $pRecordIdentifier = $this->mMySQLHandler->escapeDangerousCharacters($pRecordIdentifier); - }// end if - - $lQueryString = "SELECT identificationToken, title FROM youTubeVideos WHERE recordIndetifier = " . $pRecordIdentifier . ";"; - $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); - return $lQueryResult->fetch_object(); - }//end public function getYouTubeVideo - - public function getUsernames(){ - - $lQueryString = "SELECT username FROM accounts;"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getUsernames - - public function accountExists($pUsername){ - - if ($this->stopSQLInjection){ - $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); - }// end if - - $lQueryString = - "SELECT username FROM accounts WHERE username='".$pUsername."';"; - - $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); - - if (isset($lQueryResult->num_rows)){ - return $lQueryResult->num_rows > 0; - }else{ - return false; - }// end if - - }//end public function getUsernames - - public function authenticateAccount($pUsername, $pPassword){ - - if ($this->stopSQLInjection){ - $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); - $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); - }// end if - - $lQueryString = - "SELECT username ". - "FROM accounts ". - "WHERE username='".$pUsername."' ". - "AND password='".$pPassword."';"; - - $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); - - if (isset($lQueryResult->num_rows)){ - return $lQueryResult->num_rows > 0; - }else{ - return false; - }// end if - - }//end public function getUsernames - - public function getNonSensitiveAccountInformation($pUsername){ - /* - * Note: While escaping works ok in some case, it is not the best defense. - * Using stored procedures is a much stronger defense. - */ - if ($this->stopSQLInjection){ - $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); - }// end if - - $lQueryString = - "SELECT username, firstname, lastname, mysignature - FROM accounts - WHERE username='".$pUsername."'"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getNonSensitiveAccountInformation - - public function getUserAccountByID($pUserID){ - - if ($this->stopSQLInjection){ - $pUserID = $this->mMySQLHandler->escapeDangerousCharacters($pUserID); - }// end if - - $lQueryString = "SELECT * FROM accounts WHERE cid='" . $pUserID . "'"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getUserAccountByID - - public function getUserAccount($pUsername, $pPassword){ - /* - * Note: While escaping works ok in some case, it is not the best defense. - * Using stored procedures is a much stronger defense. - */ - - if ($this->stopSQLInjection){ - $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); - $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); - }// end if - - $lQueryString = - "SELECT * FROM accounts - WHERE username='".$pUsername. - "' AND password='".$pPassword."'"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function getUserAccount - - public function getAccountByClientId($pClientId){ - /* - * Vulnerability: Using direct user input in SQL without escaping or parameterization, - * making it vulnerable to SQL injection. - */ - if ($this->stopSQLInjection) { - $pClientId = $this->mMySQLHandler->escapeDangerousCharacters($pClientId); - } - - $lQueryString = "SELECT * FROM accounts WHERE client_id='" . $pClientId . "'"; - return $this->mMySQLHandler->executeQuery($lQueryString); - } - - public function authenticateByClientCredentials($pClientId, $pClientSecret){ - /* - * Vulnerability: Directly using user-supplied client_id and client_secret without proper escaping, - * making this function vulnerable to SQL injection. - */ - if ($this->stopSQLInjection) { - $pClientId = $this->mMySQLHandler->escapeDangerousCharacters($pClientId); - $pClientSecret = $this->mMySQLHandler->escapeDangerousCharacters($pClientSecret); - } - - $lQueryString = - "SELECT COUNT(*) AS count FROM accounts " . - "WHERE client_id='" . $pClientId . "' " . - "AND client_secret='" . $pClientSecret . "'"; - - $result = $this->mMySQLHandler->executeQuery($lQueryString); - $row = $result->fetch_assoc(); - - return $row['count'] > 0; - } - - /* ----------------------------------------- - * Insert Queries - * ----------------------------------------- */ - public function insertNewUserAccount($pUsername, $pPassword, $pFirstName, $pLastName, $pSignature){ - /* - * Note: While escaping works ok in some cases, it is not the best defense. - * Using stored procedures is a much stronger defense. - */ - if ($this->stopSQLInjection){ - $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); - $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); - $pFirstName = $this->mMySQLHandler->escapeDangerousCharacters($pFirstName); - $pLastName = $this->mMySQLHandler->escapeDangerousCharacters($pLastName); - $pSignature = $this->mMySQLHandler->escapeDangerousCharacters($pSignature); - }// end if - - $lClientID = $this->generateClientID(); - $lClientSecret = $this->generateClientSecret(); - - $lQueryString = "INSERT INTO accounts (username, password, firstname, lastname, mysignature, client_id, client_secret) VALUES ('" . - $pUsername ."', '" . - $pPassword . "', '" . - $pFirstName . "', '" . - $pLastName . "', '" . - $pSignature . "', '" . - $lClientID . "', '" . - $lClientSecret . - "')"; - - if ($this->mMySQLHandler->executeQuery($lQueryString)){ - return $this->mMySQLHandler->affected_rows(); - }else{ - return 0; - } - }//end function insertNewUserAccount - - public function insertCapturedData( - $pClientIP, - $pClientHostname, - $pClientPort, - $pClientUserAgentString, - $pClientReferrer, - $pCapturedData - ){ - if ($this->stopSQLInjection){ - $pClientIP = $this->mMySQLHandler->escapeDangerousCharacters($pClientIP); - $pClientHostname = $this->mMySQLHandler->escapeDangerousCharacters($pClientHostname); - $pClientPort = $this->mMySQLHandler->escapeDangerousCharacters($pClientPort); - $pClientUserAgentString = $this->mMySQLHandler->escapeDangerousCharacters($pClientUserAgentString); - $pClientReferrer = $this->mMySQLHandler->escapeDangerousCharacters($pClientReferrer); - }// end if - - /* Always encode to prevent captured data from breaking query */ - $pCapturedData = $this->mMySQLHandler->escapeDangerousCharacters($pCapturedData); - - $lQueryString = - "INSERT INTO captured_data(" . - "ip_address, hostname, port, user_agent_string, referrer, data, capture_date" . - ") VALUES ('". - $pClientIP . "', '". - $pClientHostname . "', '". - $pClientPort . "', '". - $pClientUserAgentString . "', '". - $pClientReferrer . "', '". - $pCapturedData . "', ". - " now()" . - ")"; - - return $this->mMySQLHandler->executeQuery($lQueryString); - }//end public function insertBlogRecord - - /* ----------------------------------------- - * Update Queries - * ----------------------------------------- */ - public function updateUserAccount($pUsername, $pPassword, $pFirstName, $pLastName, $pSignature, $pUpdateClientID, $pUpdateClientSecret){ - /* - * Note: While escaping works ok in some cases, it is not the best defense. - * Using stored procedures is a much stronger defense. - */ - if ($this->stopSQLInjection){ - $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); - $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); - $pFirstName = $this->mMySQLHandler->escapeDangerousCharacters($pFirstName); - $pLastName = $this->mMySQLHandler->escapeDangerousCharacters($pLastName); - $pSignature = $this->mMySQLHandler->escapeDangerousCharacters($pSignature); - } - - if ($pUpdateClientID){ - $lClientID = $this->generateClientID(); - } else { - $lClientID = ""; - } - - if ($pUpdateClientSecret){ - $lClientSecret = $this->generateClientSecret(); - } else { - $lClientSecret = ""; - } - - $lQueryString = - "UPDATE accounts - SET - username = '".$pUsername."', - password = '".$pPassword."', - firstname = '".$pFirstName."', - lastname = '".$pLastName."', - mysignature = '".$pSignature."'"; - - if ($pUpdateClientID){ - $lQueryString .= "," . - "client_id = '".$lClientID."'"; - } - - if ($pUpdateClientSecret){ - $lQueryString .= "," . - "client_secret = '".$lClientSecret."'"; - } - - $lQueryString .= " WHERE username = '".$pUsername."';"; - - if ($this->mMySQLHandler->executeQuery($lQueryString)){ - return $this->mMySQLHandler->affected_rows(); - } else { - return 0; - } - }//end function updateUserAccount - - /* ----------------------------------------- - * Delete Queries - * ----------------------------------------- */ - public function deleteUser($pUsername){ - if ($this->stopSQLInjection){ - $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); - }// end if - - $lQueryString = "DELETE FROM accounts WHERE username = '".$pUsername."';"; - if ($this->mMySQLHandler->executeQuery($lQueryString)){ - return $this->mMySQLHandler->affected_rows(); - }else{ - return 0; - } - }// end function deleteUser - - /* ----------------------------------------- - * Truncate Queries - * ----------------------------------------- */ - public function truncateHitLog(){ - /* Note: No possibility of SQL injection because the query is static.*/ - $lQueryString = "TRUNCATE TABLE hitlog;"; - return $this->mMySQLHandler->executeQuery($lQueryString); - }// end function truncateHitLog - -}// end class \ No newline at end of file +require_once '../../includes/constants.php'; +require_once '../../classes/JWT.php'; +require_once '../../classes/SQLQueryHandler.php'; + +// Configuration Constants +define('JWT_SECRET_KEY', getenv('JWT_SECRET_KEY') ?: 'snowman'); +define('JWT_EXPIRATION_TIME', 3600); // Token expiration time in seconds +define('MAX_FAILED_ATTEMPTS', 10); // Maximum number of failed login attempts +define('CORS_MAX_AGE', 600); // CORS preflight cache duration in seconds + +define('TRUSTED_ORIGINS', [ + 'http://mutillidae.localhost' +]); + +// Initialize SQLQueryHandler +$lSQLQueryHandler = new SQLQueryHandler(0); + +// Get the origin of the request +$lOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : 'http://mutillidae.localhost'; + +// CORS Validation - Only allow trusted origins +if (!in_array($lOrigin, TRUSTED_ORIGINS)) { + http_response_code(403); + echo json_encode(["error" => "Origin not allowed."]); + exit(); +} + +// Set CORS headers +header("Access-Control-Allow-Origin: $lOrigin"); +header('Access-Control-Allow-Methods: POST, OPTIONS'); +header('Access-Control-Allow-Headers: Content-Type, Authorization'); +header('Content-Type: application/json'); + +// Handle preflight requests +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + header('Access-Control-Allow-Origin: ' . $lOrigin); + header('Access-Control-Max-Age: ' . CORS_MAX_AGE); + http_response_code(204); + exit(); +} + +// Only allow POST requests +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + http_response_code(405); + echo json_encode(["error" => "Method not allowed. Use POST."]); + exit(); +} + +// Parse JSON input +$lData = json_decode(file_get_contents('php://input'), true); +$lClientId = $lData['client_id'] ?? null; +$lClientSecret = $lData['client_secret'] ?? null; +$lAudience = $lData['audience'] ?? null; + +// Validate Inputs +if (!isset($lClientId) || !preg_match('/^[a-f0-9]{32}$/', $lClientId)) { + http_response_code(400); + echo json_encode(["error" => "Invalid Client ID format."]); + exit(); +} + +if (!isset($lClientSecret) || !preg_match('/^[a-f0-9]{64}$/', $lClientSecret)) { + http_response_code(400); + echo json_encode(["error" => "Invalid Client Secret format."]); + exit(); +} + +if (!isset($lAudience) || !filter_var($lAudience, FILTER_VALIDATE_URL)) { + http_response_code(400); + echo json_encode(["error" => "Invalid Audience format."]); + exit(); +} + +// Define a list of valid audiences based on known endpoints +$lValidAudiences = [ + "$lBaseUrl/rest/ws-cors-echo.php", + "$lBaseUrl/rest/ws-dns-lookup.php", + "$lBaseUrl/rest/ws-echo.php", + "$lBaseUrl/rest/ws-test-connectivity.php", + "$lBaseUrl/rest/ws-user-account.php", + "$lBaseUrl/soap/ws-dns-lookup.php", + "$lBaseUrl/soap/ws-echo.php", + "$lBaseUrl/soap/ws-test-connectivity.php", + "$lBaseUrl/soap/ws-user-account.php" +]; + +// Check if the requested audience is valid +if (!in_array($lAudience, $lValidAudiences)) { + http_response_code(400); + echo json_encode(["error" => "Invalid audience specified."]); + exit(); +} + +// Rate limiting mechanism +session_start(); +$lFailedAttemptsKey = "failed_attempts_" . $_SERVER['REMOTE_ADDR']; +if (!isset($_SESSION[$lFailedAttemptsKey])) { + $_SESSION[$lFailedAttemptsKey] = 0; +} + +// Lockout mechanism after MAX_FAILED_ATTEMPTS failed attempts +if ($_SESSION[$lFailedAttemptsKey] >= MAX_FAILED_ATTEMPTS) { + http_response_code(429); // Too Many Requests + echo json_encode(["error" => "Too many failed attempts. Please try again later."]); + exit(); +} + +// Validate credentials +$lIsValid = $lSQLQueryHandler->authenticateByClientCredentials($lClientId, $lClientSecret); +if (!$lIsValid) { + $_SESSION[$lFailedAttemptsKey]++; + http_response_code(401); + echo json_encode(["error" => "Authentication failed."]); + exit(); +} else { + // Reset failed attempts on successful login + $_SESSION[$lFailedAttemptsKey] = 0; +} + +// Define JWT claims with audience +$lPayload = [ + 'iss' => $lBaseUrl, // Issuer is your domain + 'aud' => $lAudience, // Audience for the token + 'iat' => time(), // Issued at + 'nbf' => time(), // Not before + 'exp' => time() + JWT_EXPIRATION_TIME, // Expiration time + 'sub' => $lClientId, // Subject is the client ID + 'jti' => bin2hex(random_bytes(16)) // JWT ID +]; + +// Encode the JWT token with a specified algorithm +$lJwt = JWT::encode($lPayload, JWT_SECRET_KEY, 'HS256'); // Use a secure algorithm + +// Respond with JWT token +http_response_code(200); +echo json_encode([ + 'access_token' => $lJwt, + 'token_type' => 'bearer', + 'expires_in' => JWT_EXPIRATION_TIME +]); + +?> diff --git a/src/webservices/rest/ws-login.php b/src/webservices/rest/ws-login.php index 69b8345..b1ebd44 100644 --- a/src/webservices/rest/ws-login.php +++ b/src/webservices/rest/ws-login.php @@ -73,7 +73,7 @@ "$lBaseUrl/rest/ws-cors-echo.php", "$lBaseUrl/rest/ws-dns-lookup.php", "$lBaseUrl/rest/ws-echo.php", - "$lBaseUrl/rest/ws-prototype-login.php", + "$lBaseUrl/rest/ws-login.php", "$lBaseUrl/rest/ws-test-connectivity.php", "$lBaseUrl/rest/ws-user-account.php", "$lBaseUrl/soap/ws-dns-lookup.php", From ff8fd8a9e366b29b2c09d019336fafa971412e51 Mon Sep 17 00:00:00 2001 From: webpwnized Date: Sat, 9 Nov 2024 23:34:06 -0500 Subject: [PATCH 4/5] 2.11.24 Update accounts table --- src/classes/SQLQueryHandler.php | 735 ++++++++++++++++++++++++------ src/webservices/rest/ws-login.php | 93 ++-- 2 files changed, 654 insertions(+), 174 deletions(-) diff --git a/src/classes/SQLQueryHandler.php b/src/classes/SQLQueryHandler.php index 488243f..16de67a 100755 --- a/src/classes/SQLQueryHandler.php +++ b/src/classes/SQLQueryHandler.php @@ -1,144 +1,595 @@ "Origin not allowed."]); - exit(); -} - -// Set CORS headers -header("Access-Control-Allow-Origin: $lOrigin"); -header('Access-Control-Allow-Methods: POST, OPTIONS'); -header('Access-Control-Allow-Headers: Content-Type, Authorization'); -header('Content-Type: application/json'); - -// Handle preflight requests -if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { - header('Access-Control-Allow-Origin: ' . $lOrigin); - header('Access-Control-Max-Age: ' . CORS_MAX_AGE); - http_response_code(204); - exit(); -} - -// Only allow POST requests -if ($_SERVER['REQUEST_METHOD'] !== 'POST') { - http_response_code(405); - echo json_encode(["error" => "Method not allowed. Use POST."]); - exit(); -} - -// Parse JSON input -$lData = json_decode(file_get_contents('php://input'), true); -$lClientId = $lData['client_id'] ?? null; -$lClientSecret = $lData['client_secret'] ?? null; -$lAudience = $lData['audience'] ?? null; - -// Validate Inputs -if (!isset($lClientId) || !preg_match('/^[a-f0-9]{32}$/', $lClientId)) { - http_response_code(400); - echo json_encode(["error" => "Invalid Client ID format."]); - exit(); -} - -if (!isset($lClientSecret) || !preg_match('/^[a-f0-9]{64}$/', $lClientSecret)) { - http_response_code(400); - echo json_encode(["error" => "Invalid Client Secret format."]); - exit(); -} - -if (!isset($lAudience) || !filter_var($lAudience, FILTER_VALIDATE_URL)) { - http_response_code(400); - echo json_encode(["error" => "Invalid Audience format."]); - exit(); -} - -// Define a list of valid audiences based on known endpoints -$lValidAudiences = [ - "$lBaseUrl/rest/ws-cors-echo.php", - "$lBaseUrl/rest/ws-dns-lookup.php", - "$lBaseUrl/rest/ws-echo.php", - "$lBaseUrl/rest/ws-test-connectivity.php", - "$lBaseUrl/rest/ws-user-account.php", - "$lBaseUrl/soap/ws-dns-lookup.php", - "$lBaseUrl/soap/ws-echo.php", - "$lBaseUrl/soap/ws-test-connectivity.php", - "$lBaseUrl/soap/ws-user-account.php" -]; - -// Check if the requested audience is valid -if (!in_array($lAudience, $lValidAudiences)) { - http_response_code(400); - echo json_encode(["error" => "Invalid audience specified."]); - exit(); -} - -// Rate limiting mechanism -session_start(); -$lFailedAttemptsKey = "failed_attempts_" . $_SERVER['REMOTE_ADDR']; -if (!isset($_SESSION[$lFailedAttemptsKey])) { - $_SESSION[$lFailedAttemptsKey] = 0; -} - -// Lockout mechanism after MAX_FAILED_ATTEMPTS failed attempts -if ($_SESSION[$lFailedAttemptsKey] >= MAX_FAILED_ATTEMPTS) { - http_response_code(429); // Too Many Requests - echo json_encode(["error" => "Too many failed attempts. Please try again later."]); - exit(); -} - -// Validate credentials -$lIsValid = $lSQLQueryHandler->authenticateByClientCredentials($lClientId, $lClientSecret); -if (!$lIsValid) { - $_SESSION[$lFailedAttemptsKey]++; - http_response_code(401); - echo json_encode(["error" => "Authentication failed."]); - exit(); -} else { - // Reset failed attempts on successful login - $_SESSION[$lFailedAttemptsKey] = 0; -} - -// Define JWT claims with audience -$lPayload = [ - 'iss' => $lBaseUrl, // Issuer is your domain - 'aud' => $lAudience, // Audience for the token - 'iat' => time(), // Issued at - 'nbf' => time(), // Not before - 'exp' => time() + JWT_EXPIRATION_TIME, // Expiration time - 'sub' => $lClientId, // Subject is the client ID - 'jti' => bin2hex(random_bytes(16)) // JWT ID -]; - -// Encode the JWT token with a specified algorithm -$lJwt = JWT::encode($lPayload, JWT_SECRET_KEY, 'HS256'); // Use a secure algorithm - -// Respond with JWT token -http_response_code(200); -echo json_encode([ - 'access_token' => $lJwt, - 'token_type' => 'bearer', - 'expires_in' => JWT_EXPIRATION_TIME -]); - -?> +/* Determine the root of the entire project. + * Recall this file is in the "includes" folder so its "2 levels deep". */ +if (!defined('__SITE_ROOT__')){if (!defined('__SITE_ROOT__')){define('__SITE_ROOT__', dirname(dirname(__FILE__)));}} + +class SQLQueryHandler { + protected $encodeOutput = false; + protected $stopSQLInjection = false; + protected $mLimitOutput = false; + protected $mSecurityLevel = 0; + + // private objects + protected $mMySQLHandler = null; + protected $mEncoder = null; + + private function doSetSecurityLevel($pSecurityLevel){ + $this->mSecurityLevel = $pSecurityLevel; + + switch ($this->mSecurityLevel){ + default: // Default case: This code is insecure, we are not encoding output + case "0": // This code is insecure, we are not encoding output + case "1": // This code is insecure, we are not encoding output + $this->encodeOutput = false; + $this->stopSQLInjection = false; + $this->mLimitOutput = false; + break; + + case "2": + case "3": + case "4": + case "5": // This code is fairly secure + // If we are secure, then we encode all output. + $this->encodeOutput = true; + $this->stopSQLInjection = true; + $this->mLimitOutput = true; + break; + }// end switch + }// end function + + private function generateClientID($length = 16 /* 16 bytes = 128 bits */){ + // Generates a secure 16-byte token for use as a Client ID + // The token is generated using a cryptographically secure pseudorandom number generator + // The token is then converted to hexadecimal format + // The token will be 32 characters long + return bin2hex(random_bytes($length)); + } + + private function generateClientSecret($length = 32 /* 32 bytes = 256 bits */){ + // Generates a secure 32-byte token for use in API calls + // The token is generated using a cryptographically secure pseudorandom number generator + // The token is then converted to hexadecimal format + // The token will be 64 characters long + return bin2hex(random_bytes($length)); + } + + public function __construct($pSecurityLevel = 0) { + // Ensure the provided level is valid; fall back to 0 if it's not. + if (!is_int($pSecurityLevel) || $pSecurityLevel < 0 || $pSecurityLevel > 5) { + $pSecurityLevel = 0; + } + + $this->doSetSecurityLevel($pSecurityLevel); + + //initialize encoder + require_once __SITE_ROOT__.'/classes/EncodingHandler.php'; + $this->mEncoder = new EncodingHandler(); + + /* Initialize MySQL Connection handler */ + require_once 'MySQLHandler.php'; + $this->mMySQLHandler = new MySQLHandler($pSecurityLevel); + $this->mMySQLHandler->connectToDefaultDatabase(); + + }// end function + + public function setSecurityLevel($pSecurityLevel){ + $this->doSetSecurityLevel($pSecurityLevel); + $this->mMySQLHandler->setSecurityLevel($pSecurityLevel); + }// end function + + public function getSecurityLevel(){ + return $this->mSecurityLevel; + }// end function + + public function affected_rows(){ + return $this->mMySQLHandler->affected_rows(); + }//end function + + /* ************************************************************** + * BEGIN: Queries * + ****************************************************************/ + public function escapeDangerousCharacters($pData){ + return $this->mMySQLHandler->escapeDangerousCharacters($pData); + } + + public function getSecurityLevelFromDB() { + // Query to retrieve the security level from the row with id = 1 + $lQueryString = "SELECT level FROM security_level WHERE id = 1"; + + // Execute the query + $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); + + // Check if the query returned a valid result + if ($lQueryResult && $lQueryResult->num_rows > 0) { + $lRow = $lQueryResult->fetch_assoc(); + return (int) $lRow['level']; // Return the level as an integer + } else { + return null; // Return null if the row does not exist + } + } // end function getSecurityLevelFromDB + + public function setSecurityLevelInDB($pLevel) { + if ($pLevel < 0 || $pLevel > 5) { + throw new InvalidArgumentException("Security level must be between 0 and 5."); + } + + $safeLevel = (int) $pLevel; + $lQueryString = "UPDATE security_level SET level = $safeLevel WHERE id = 1"; + $this->mMySQLHandler->executeQuery($lQueryString); + + // Ensure the row was actually updated + return $this->mMySQLHandler->affected_rows() > 0; + } // end function setSecurityLevelInDB + + public function getPageHelpTexts($pPageName){ + + if ($this->stopSQLInjection){ + $pPageName = $this->mMySQLHandler->escapeDangerousCharacters($pPageName); + }// end if + + $lQueryString = " + SELECT CONCAT( + '
+ ', + help_text, + '
' + ) AS help_text + FROM page_help + INNER JOIN help_texts + ON page_help.help_text_key = help_texts.help_text_key + WHERE page_help.page_name = '" . $pPageName . "' " . + "ORDER BY page_help.order_preference"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getPageHelpTexts + + public function getPageLevelOneHelpIncludeFiles($pPageName){ + + if ($this->stopSQLInjection){ + $pPageName = $this->mMySQLHandler->escapeDangerousCharacters($pPageName); + }// end if + + $lQueryString = " + SELECT level_1_help_include_files.level_1_help_include_file_key, + level_1_help_include_files.level_1_help_include_file_description + FROM page_help + INNER JOIN level_1_help_include_files + ON page_help.help_text_key = + level_1_help_include_files.level_1_help_include_file_key + WHERE page_help.page_name = '" . $pPageName . "' " . + "ORDER BY page_help.order_preference"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getPageLevelOneHelpIncludeFiles + + public function getLevelOneHelpIncludeFile($pIncludeFileKey){ + + if ($this->stopSQLInjection){ + $pIncludeFileKey = $this->mMySQLHandler->escapeDangerousCharacters($pIncludeFileKey); + }// end if + + $lQueryString = " + SELECT level_1_help_include_files.level_1_help_include_file, + level_1_help_include_files.level_1_help_include_file_description + FROM level_1_help_include_files + WHERE level_1_help_include_files.level_1_help_include_file_key = " . $pIncludeFileKey; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getPageLevelOneHelpIncludeFiles + + public function deleteCapturedData(){ + $lQueryString = "TRUNCATE TABLE captured_data"; + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function deleteCapturedData + + public function getCapturedData(){ + $lQueryString = " + SELECT ip_address, hostname, port, user_agent_string, referrer, data, capture_date + FROM captured_data + ORDER BY capture_date DESC"; + + if ($this->mLimitOutput){ + $lQueryString .= " LIMIT 20"; + }// end if + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getCapturedData() + + public function insertVoteIntoUserPoll(/*Text*/ $pToolName, /*Text*/ $pUserName){ + + if ($this->stopSQLInjection){ + $pToolName = $this->mMySQLHandler->escapeDangerousCharacters($pToolName); + $pUserName = $this->mMySQLHandler->escapeDangerousCharacters($pUserName); + }// end if + + $lQueryString = " + INSERT INTO user_poll_results(tool_name, username, date) VALUES ('". + $pToolName . "', '". + $pUserName . "', " . + " now() );"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function insertVoteIntoUserPoll + + public function getUserPollVotes(){ + + $lQueryString = " + SELECT tool_name, COUNT(tool_name) as tool_count + FROM user_poll_results + GROUP BY tool_name"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function insertVoteIntoUserPoll + + public function insertBlogRecord($pBloggerName, $pBlogEntry){ + + if ($this->stopSQLInjection){ + $pBloggerName = $this->mMySQLHandler->escapeDangerousCharacters($pBloggerName); + $pBlogEntry = $this->mMySQLHandler->escapeDangerousCharacters($pBlogEntry); + }// end if + + $lQueryString = " + INSERT INTO blogs_table(blogger_name, comment, date) VALUES ('". + $pBloggerName . "', '". + $pBlogEntry . "', " . + " now() )"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function insertBlogRecord + + public function getBlogRecord($pBloggerName){ + + if ($this->stopSQLInjection){ + $pBloggerName = $this->mMySQLHandler->escapeDangerousCharacters($pBloggerName); + }// end if + + $lQueryString = " + SELECT * FROM blogs_table + WHERE blogger_name like '{$pBloggerName}%' + ORDER BY date DESC + LIMIT 0 , 100"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getBlogRecord + + public function getPenTestTool($pPostedToolID){ + /* + * Note: While escaping works ok in some case, it is not the best defense. + * Using stored procedures is a much stronger defense. + */ + if ($this->stopSQLInjection){ + $pPostedToolID = $this->mMySQLHandler->escapeDangerousCharacters($pPostedToolID); + }// end if + + if ($pPostedToolID != "c84326e4-7487-41d3-91fd-88280828c756"){ + $lWhereClause = " WHERE tool_id = '".$pPostedToolID."';"; + }else{ + $lWhereClause = ";"; + }// end if + + $lQueryString = " + SELECT tool_id, tool_name, phase_to_use, tool_type, comment + FROM pen_test_tools" . + $lWhereClause; + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getPenTestTool + + public function getPenTestTools(){ + /* Note: No possibility of SQL injection because the query + * is static. + */ + $lQueryString = "SELECT tool_id, tool_name FROM pen_test_tools;"; + return $this->mMySQLHandler->executeQuery($lQueryString); + }// end function getPenTestTools + + public function getHitLogEntries(){ + /* Note: No possibility of SQL injection because the query + * is static. + */ + $lLimitString = ""; + if ($this->mLimitOutput){ + $lLimitString .= " LIMIT 20"; + }// end if + + $lQueryString = "SELECT * FROM `hitlog` ORDER BY date DESC".$lLimitString.";"; + return $this->mMySQLHandler->executeQuery($lQueryString); + }// end function getHitLogEntries + + public function getYouTubeVideo($pRecordIdentifier){ + /* + * Note: While escaping works ok in some case, it is not the best defense. + * Using stored procedures is a much stronger defense. + */ + if ($this->stopSQLInjection){ + $pRecordIdentifier = $this->mMySQLHandler->escapeDangerousCharacters($pRecordIdentifier); + }// end if + + $lQueryString = "SELECT identificationToken, title FROM youTubeVideos WHERE recordIndetifier = " . $pRecordIdentifier . ";"; + $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); + return $lQueryResult->fetch_object(); + }//end public function getYouTubeVideo + + public function getUsernames(){ + + $lQueryString = "SELECT username FROM accounts;"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getUsernames + + public function accountExists($pUsername){ + + if ($this->stopSQLInjection){ + $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); + }// end if + + $lQueryString = + "SELECT username FROM accounts WHERE username='".$pUsername."';"; + + $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); + + if (isset($lQueryResult->num_rows)){ + return $lQueryResult->num_rows > 0; + }else{ + return false; + }// end if + + }//end public function getUsernames + + public function authenticateAccount($pUsername, $pPassword){ + + if ($this->stopSQLInjection){ + $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); + $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); + }// end if + + $lQueryString = + "SELECT username ". + "FROM accounts ". + "WHERE username='".$pUsername."' ". + "AND password='".$pPassword."';"; + + $lQueryResult = $this->mMySQLHandler->executeQuery($lQueryString); + + if (isset($lQueryResult->num_rows)){ + return $lQueryResult->num_rows > 0; + }else{ + return false; + }// end if + + }//end public function getUsernames + + public function getNonSensitiveAccountInformation($pUsername){ + /* + * Note: While escaping works ok in some case, it is not the best defense. + * Using stored procedures is a much stronger defense. + */ + if ($this->stopSQLInjection){ + $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); + }// end if + + $lQueryString = + "SELECT username, firstname, lastname, mysignature + FROM accounts + WHERE username='".$pUsername."'"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getNonSensitiveAccountInformation + + public function getUserAccountByID($pUserID){ + + if ($this->stopSQLInjection){ + $pUserID = $this->mMySQLHandler->escapeDangerousCharacters($pUserID); + }// end if + + $lQueryString = "SELECT * FROM accounts WHERE cid='" . $pUserID . "'"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getUserAccountByID + + public function getUserAccount($pUsername, $pPassword){ + /* + * Note: While escaping works ok in some case, it is not the best defense. + * Using stored procedures is a much stronger defense. + */ + + if ($this->stopSQLInjection){ + $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); + $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); + }// end if + + $lQueryString = + "SELECT * FROM accounts + WHERE username='".$pUsername. + "' AND password='".$pPassword."'"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function getUserAccount + + public function getAccountByClientId($pClientId){ + /* + * Vulnerability: Using direct user input in SQL without escaping or parameterization, + * making it vulnerable to SQL injection. + */ + if ($this->stopSQLInjection) { + $pClientId = $this->mMySQLHandler->escapeDangerousCharacters($pClientId); + } + + $lQueryString = "SELECT * FROM accounts WHERE client_id='" . $pClientId . "'"; + return $this->mMySQLHandler->executeQuery($lQueryString); + } + + public function authenticateByClientCredentials($pClientId, $pClientSecret){ + /* + * Vulnerability: Directly using user-supplied client_id and client_secret without proper escaping, + * making this function vulnerable to SQL injection. + */ + if ($this->stopSQLInjection) { + $pClientId = $this->mMySQLHandler->escapeDangerousCharacters($pClientId); + $pClientSecret = $this->mMySQLHandler->escapeDangerousCharacters($pClientSecret); + } + + $lQueryString = + "SELECT COUNT(*) AS count FROM accounts " . + "WHERE client_id='" . $pClientId . "' " . + "AND client_secret='" . $pClientSecret . "'"; + + $result = $this->mMySQLHandler->executeQuery($lQueryString); + $row = $result->fetch_assoc(); + + return $row['count'] > 0; + } + + /* ----------------------------------------- + * Insert Queries + * ----------------------------------------- */ + public function insertNewUserAccount($pUsername, $pPassword, $pFirstName, $pLastName, $pSignature){ + /* + * Note: While escaping works ok in some cases, it is not the best defense. + * Using stored procedures is a much stronger defense. + */ + if ($this->stopSQLInjection){ + $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); + $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); + $pFirstName = $this->mMySQLHandler->escapeDangerousCharacters($pFirstName); + $pLastName = $this->mMySQLHandler->escapeDangerousCharacters($pLastName); + $pSignature = $this->mMySQLHandler->escapeDangerousCharacters($pSignature); + }// end if + + $lClientID = $this->generateClientID(); + $lClientSecret = $this->generateClientSecret(); + + $lQueryString = "INSERT INTO accounts (username, password, firstname, lastname, mysignature, client_id, client_secret) VALUES ('" . + $pUsername ."', '" . + $pPassword . "', '" . + $pFirstName . "', '" . + $pLastName . "', '" . + $pSignature . "', '" . + $lClientID . "', '" . + $lClientSecret . + "')"; + + if ($this->mMySQLHandler->executeQuery($lQueryString)){ + return $this->mMySQLHandler->affected_rows(); + }else{ + return 0; + } + }//end function insertNewUserAccount + + public function insertCapturedData( + $pClientIP, + $pClientHostname, + $pClientPort, + $pClientUserAgentString, + $pClientReferrer, + $pCapturedData + ){ + if ($this->stopSQLInjection){ + $pClientIP = $this->mMySQLHandler->escapeDangerousCharacters($pClientIP); + $pClientHostname = $this->mMySQLHandler->escapeDangerousCharacters($pClientHostname); + $pClientPort = $this->mMySQLHandler->escapeDangerousCharacters($pClientPort); + $pClientUserAgentString = $this->mMySQLHandler->escapeDangerousCharacters($pClientUserAgentString); + $pClientReferrer = $this->mMySQLHandler->escapeDangerousCharacters($pClientReferrer); + }// end if + + /* Always encode to prevent captured data from breaking query */ + $pCapturedData = $this->mMySQLHandler->escapeDangerousCharacters($pCapturedData); + + $lQueryString = + "INSERT INTO captured_data(" . + "ip_address, hostname, port, user_agent_string, referrer, data, capture_date" . + ") VALUES ('". + $pClientIP . "', '". + $pClientHostname . "', '". + $pClientPort . "', '". + $pClientUserAgentString . "', '". + $pClientReferrer . "', '". + $pCapturedData . "', ". + " now()" . + ")"; + + return $this->mMySQLHandler->executeQuery($lQueryString); + }//end public function insertBlogRecord + + /* ----------------------------------------- + * Update Queries + * ----------------------------------------- */ + public function updateUserAccount($pUsername, $pPassword, $pFirstName, $pLastName, $pSignature, $pUpdateClientID, $pUpdateClientSecret){ + /* + * Note: While escaping works ok in some cases, it is not the best defense. + * Using stored procedures is a much stronger defense. + */ + if ($this->stopSQLInjection){ + $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); + $pPassword = $this->mMySQLHandler->escapeDangerousCharacters($pPassword); + $pFirstName = $this->mMySQLHandler->escapeDangerousCharacters($pFirstName); + $pLastName = $this->mMySQLHandler->escapeDangerousCharacters($pLastName); + $pSignature = $this->mMySQLHandler->escapeDangerousCharacters($pSignature); + } + + if ($pUpdateClientID){ + $lClientID = $this->generateClientID(); + } else { + $lClientID = ""; + } + + if ($pUpdateClientSecret){ + $lClientSecret = $this->generateClientSecret(); + } else { + $lClientSecret = ""; + } + + $lQueryString = + "UPDATE accounts + SET + username = '".$pUsername."', + password = '".$pPassword."', + firstname = '".$pFirstName."', + lastname = '".$pLastName."', + mysignature = '".$pSignature."'"; + + if ($pUpdateClientID){ + $lQueryString .= "," . + "client_id = '".$lClientID."'"; + } + + if ($pUpdateClientSecret){ + $lQueryString .= "," . + "client_secret = '".$lClientSecret."'"; + } + + $lQueryString .= " WHERE username = '".$pUsername."';"; + + if ($this->mMySQLHandler->executeQuery($lQueryString)){ + return $this->mMySQLHandler->affected_rows(); + } else { + return 0; + } + }//end function updateUserAccount + + /* ----------------------------------------- + * Delete Queries + * ----------------------------------------- */ + public function deleteUser($pUsername){ + if ($this->stopSQLInjection){ + $pUsername = $this->mMySQLHandler->escapeDangerousCharacters($pUsername); + }// end if + + $lQueryString = "DELETE FROM accounts WHERE username = '".$pUsername."';"; + if ($this->mMySQLHandler->executeQuery($lQueryString)){ + return $this->mMySQLHandler->affected_rows(); + }else{ + return 0; + } + }// end function deleteUser + + /* ----------------------------------------- + * Truncate Queries + * ----------------------------------------- */ + public function truncateHitLog(){ + /* Note: No possibility of SQL injection because the query is static.*/ + $lQueryString = "TRUNCATE TABLE hitlog;"; + return $this->mMySQLHandler->executeQuery($lQueryString); + }// end function truncateHitLog + +}// end class \ No newline at end of file diff --git a/src/webservices/rest/ws-login.php b/src/webservices/rest/ws-login.php index b1ebd44..f1caa21 100644 --- a/src/webservices/rest/ws-login.php +++ b/src/webservices/rest/ws-login.php @@ -4,16 +4,28 @@ require_once '../../classes/JWT.php'; require_once '../../classes/SQLQueryHandler.php'; +// Configuration Constants +define('JWT_SECRET_KEY', getenv('JWT_SECRET_KEY') ?: 'snowman'); +define('JWT_EXPIRATION_TIME', 3600); // Token expiration time in seconds +define('MAX_FAILED_ATTEMPTS', 10); // Maximum number of failed login attempts +define('CORS_MAX_AGE', 600); // CORS preflight cache duration in seconds + +define('TRUSTED_ORIGINS', [ + 'http://mutillidae.localhost' +]); + // Initialize SQLQueryHandler $lSQLQueryHandler = new SQLQueryHandler(0); // Get the origin of the request -$lOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : 'https://mutillidae.localhost'; +$lOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : 'http://mutillidae.localhost'; -// Get the current domain dynamically -$lCurrentDomain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'mutillidae.localhost'; -$lScheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; -$lBaseUrl = "{$lScheme}://{$lCurrentDomain}"; +// CORS Validation - Only allow trusted origins +if (!in_array($lOrigin, TRUSTED_ORIGINS)) { + http_response_code(403); + echo json_encode(["error" => "Origin not allowed."]); + exit(); +} // Set CORS headers header("Access-Control-Allow-Origin: $lOrigin"); @@ -24,7 +36,7 @@ // Handle preflight requests if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { header('Access-Control-Allow-Origin: ' . $lOrigin); - header('Access-Control-Max-Age: 600'); + header('Access-Control-Max-Age: ' . CORS_MAX_AGE); http_response_code(204); exit(); } @@ -42,29 +54,22 @@ $lClientSecret = $lData['client_secret'] ?? null; $lAudience = $lData['audience'] ?? null; -if (!$lClientId) { +// Validate Inputs +if (!isset($lClientId) || !preg_match('/^[a-f0-9]{32}$/', $lClientId)) { http_response_code(400); - echo json_encode(["error" => "Client ID is missing."]); + echo json_encode(["error" => "Invalid Client ID format."]); exit(); } -if (!$lClientSecret) { +if (!isset($lClientSecret) || !preg_match('/^[a-f0-9]{64}$/', $lClientSecret)) { http_response_code(400); - echo json_encode(["error" => "Client secret is missing."]); + echo json_encode(["error" => "Invalid Client Secret format."]); exit(); } -if (!$lAudience) { +if (!isset($lAudience) || !filter_var($lAudience, FILTER_VALIDATE_URL)) { http_response_code(400); - echo json_encode(["error" => "Audience is missing."]); - exit(); -} - -// Validate credentials -$lIsValid = $lSQLQueryHandler->authenticateByClientCredentials($lClientId, $lClientSecret); -if (!$lIsValid) { - http_response_code(401); - echo json_encode(["error" => "Invalid client credentials."]); + echo json_encode(["error" => "Invalid Audience format."]); exit(); } @@ -73,7 +78,6 @@ "$lBaseUrl/rest/ws-cors-echo.php", "$lBaseUrl/rest/ws-dns-lookup.php", "$lBaseUrl/rest/ws-echo.php", - "$lBaseUrl/rest/ws-login.php", "$lBaseUrl/rest/ws-test-connectivity.php", "$lBaseUrl/rest/ws-user-account.php", "$lBaseUrl/soap/ws-dns-lookup.php", @@ -89,27 +93,52 @@ exit(); } +// Rate limiting mechanism +session_start(); +$lFailedAttemptsKey = "failed_attempts_" . $_SERVER['REMOTE_ADDR']; +if (!isset($_SESSION[$lFailedAttemptsKey])) { + $_SESSION[$lFailedAttemptsKey] = 0; +} + +// Lockout mechanism after MAX_FAILED_ATTEMPTS failed attempts +if ($_SESSION[$lFailedAttemptsKey] >= MAX_FAILED_ATTEMPTS) { + http_response_code(429); // Too Many Requests + echo json_encode(["error" => "Too many failed attempts. Please try again later."]); + exit(); +} + +// Validate credentials +$lIsValid = $lSQLQueryHandler->authenticateByClientCredentials($lClientId, $lClientSecret); +if (!$lIsValid) { + $_SESSION[$lFailedAttemptsKey]++; + http_response_code(401); + echo json_encode(["error" => "Authentication failed."]); + exit(); +} else { + // Reset failed attempts on successful login + $_SESSION[$lFailedAttemptsKey] = 0; +} + // Define JWT claims with audience $lPayload = [ - 'iss' => $lBaseUrl, // Issuer is your domain - 'aud' => $lAudience, // Include audience in token - 'iat' => time(), - 'nbf' => time(), - 'exp' => time() + 3600, - 'sub' => $lClientId, - 'jti' => bin2hex(random_bytes(16)) + 'iss' => $lBaseUrl, // Issuer is your domain + 'aud' => $lAudience, // Audience for the token + 'iat' => time(), // Issued at + 'nbf' => time(), // Not before + 'exp' => time() + JWT_EXPIRATION_TIME, // Expiration time + 'sub' => $lClientId, // Subject is the client ID + 'jti' => bin2hex(random_bytes(16)) // JWT ID ]; -// Encode the JWT token -$lSecretKey = getenv('JWT_SECRET_KEY') ?: 'snowman'; -$lJwt = JWT::encode($lPayload, $lSecretKey); +// Encode the JWT token with a specified algorithm +$lJwt = JWT::encode($lPayload, JWT_SECRET_KEY, 'HS256'); // Use a secure algorithm // Respond with JWT token http_response_code(200); echo json_encode([ 'access_token' => $lJwt, 'token_type' => 'bearer', - 'expires_in' => 3600 + 'expires_in' => JWT_EXPIRATION_TIME ]); ?> From 46490ef958a90f38eb26ae86df8e341dde62d0bc Mon Sep 17 00:00:00 2001 From: webpwnized Date: Sat, 9 Nov 2024 23:38:07 -0500 Subject: [PATCH 5/5] 2.11.24 Update accounts table --- src/webservices/rest/ws-login.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/webservices/rest/ws-login.php b/src/webservices/rest/ws-login.php index f1caa21..e94584c 100644 --- a/src/webservices/rest/ws-login.php +++ b/src/webservices/rest/ws-login.php @@ -7,18 +7,21 @@ // Configuration Constants define('JWT_SECRET_KEY', getenv('JWT_SECRET_KEY') ?: 'snowman'); define('JWT_EXPIRATION_TIME', 3600); // Token expiration time in seconds -define('MAX_FAILED_ATTEMPTS', 10); // Maximum number of failed login attempts +define('MAX_FAILED_ATTEMPTS', 5); // Maximum number of failed login attempts define('CORS_MAX_AGE', 600); // CORS preflight cache duration in seconds define('TRUSTED_ORIGINS', [ 'http://mutillidae.localhost' ]); +// Define the Base URL dynamically based on the current request +define('BASE_URL', ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']); + // Initialize SQLQueryHandler $lSQLQueryHandler = new SQLQueryHandler(0); // Get the origin of the request -$lOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : 'http://mutillidae.localhost'; +$lOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : BASE_URL; // CORS Validation - Only allow trusted origins if (!in_array($lOrigin, TRUSTED_ORIGINS)) { @@ -75,15 +78,15 @@ // Define a list of valid audiences based on known endpoints $lValidAudiences = [ - "$lBaseUrl/rest/ws-cors-echo.php", - "$lBaseUrl/rest/ws-dns-lookup.php", - "$lBaseUrl/rest/ws-echo.php", - "$lBaseUrl/rest/ws-test-connectivity.php", - "$lBaseUrl/rest/ws-user-account.php", - "$lBaseUrl/soap/ws-dns-lookup.php", - "$lBaseUrl/soap/ws-echo.php", - "$lBaseUrl/soap/ws-test-connectivity.php", - "$lBaseUrl/soap/ws-user-account.php" + BASE_URL . "/rest/ws-cors-echo.php", + BASE_URL . "/rest/ws-dns-lookup.php", + BASE_URL . "/rest/ws-echo.php", + BASE_URL . "/rest/ws-test-connectivity.php", + BASE_URL . "/rest/ws-user-account.php", + BASE_URL . "/soap/ws-dns-lookup.php", + BASE_URL . "/soap/ws-echo.php", + BASE_URL . "/soap/ws-test-connectivity.php", + BASE_URL . "/soap/ws-user-account.php" ]; // Check if the requested audience is valid @@ -121,7 +124,7 @@ // Define JWT claims with audience $lPayload = [ - 'iss' => $lBaseUrl, // Issuer is your domain + 'iss' => BASE_URL, // Issuer is your domain 'aud' => $lAudience, // Audience for the token 'iat' => time(), // Issued at 'nbf' => time(), // Not before