Skip to content

Commit cdd7131

Browse files
committed
2.11.26 Add auth to SOAP services
1 parent a191a7a commit cdd7131

File tree

7 files changed

+554
-502
lines changed

7 files changed

+554
-502
lines changed

src/includes/constants.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/* ------------------------------------------
33
* @VERSION
44
* ------------------------------------------*/
5-
$C_VERSION = "2.11.25";
5+
$C_VERSION = "2.11.26";
66
$C_VERSION_STRING = "Version: " . $C_VERSION;
77
$C_MAX_HINT_LEVEL = 1;
88

src/webservices/includes/ws-constants.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
]);
110110

111111
define('ERROR_MESSAGE_METHOD_NOT_ALLOWED', '<?xml version="1.0" encoding="UTF-8"?><error><message>Method Not Allowed. Use POST for this endpoint.</message></error>');
112-
define('ERROR_MESSAGE_UNAUTHORIZED', '<?xml version="1.0" encoding="UTF-8"?><error><message>Unauthorized</message></error>');
112+
define('ERROR_MESSAGE_UNAUTHORIZED_PREFIX', '<?xml version="1.0" encoding="UTF-8"?><error><message>');
113+
define('ERROR_MESSAGE_UNAUTHORIZED_SUFFIX', '</message></error>');
113114

114115
?>

src/webservices/soap/ws-dns-lookup.php

Lines changed: 59 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class LookupException extends Exception {}
77

88
// Pull in the NuSOAP library
99
require_once './lib/nusoap.php';
10+
require_once '../includes/ws-constants.php';
1011

1112
$lServerName = $_SERVER['SERVER_NAME'];
1213

@@ -47,6 +48,53 @@ class LookupException extends Exception {}
4748
)
4849
);
4950

51+
/**
52+
* Function: authenticateRequest
53+
* Handles request authentication and CORS headers.
54+
*
55+
* @param int $lSecurityLevel The security level.
56+
* @throws InvalidTokenException If the authentication fails.
57+
*/
58+
function authenticateRequest($lSecurityLevel) {
59+
60+
// Set CORS headers
61+
header(CORS_ACCESS_CONTROL_ALLOW_ORIGIN);
62+
header('Access-Control-Allow-Methods: POST, OPTIONS'); // Allowed methods
63+
header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Specify allowed headers
64+
header('Access-Control-Expose-Headers: Authorization'); // Expose headers if needed
65+
header(CONTENT_TYPE_XML); // Set content type as XML
66+
67+
// Handle preflight requests (OPTIONS)
68+
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
69+
header(CORS_ACCESS_CONTROL_MAX_AGE); // Cache the preflight response for 600 seconds (10 minutes)
70+
http_response_code(RESPONSE_CODE_NO_CONTENT); // No Content
71+
exit();
72+
}
73+
74+
// Allow only POST requests
75+
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
76+
http_response_code(RESPONSE_CODE_METHOD_NOT_ALLOWED);
77+
header(CONTENT_TYPE_XML);
78+
echo ERROR_MESSAGE_METHOD_NOT_ALLOWED;
79+
exit();
80+
}
81+
82+
// Shared: Include the shared JWT token authentication function
83+
require_once '../includes/ws-authenticate-jwt-token.php';
84+
85+
// Shared: Authenticate the user if required
86+
if ($lSecurityLevel >= SECURITY_LEVEL_MEDIUM) {
87+
try {
88+
$lDecodedToken = authenticateJWTToken(); // Authenticate using the shared function
89+
} catch (InvalidTokenException $e) {
90+
http_response_code(RESPONSE_CODE_UNAUTHORIZED);
91+
header(CONTENT_TYPE_XML);
92+
echo ERROR_MESSAGE_UNAUTHORIZED_PREFIX . 'Unauthorized: ' . htmlspecialchars($e->getMessage()) . ERROR_MESSAGE_UNAUTHORIZED_SUFFIX;
93+
exit();
94+
}
95+
}
96+
}
97+
5098
/**
5199
* Method: lookupDNS
52100
* Performs a DNS lookup for a given target host.
@@ -69,68 +117,18 @@ function lookupDNS($pTargetHost) {
69117
$LogHandler = new LogHandler($lSecurityLevel);
70118
$Encoder = new EncodingHandler();
71119

72-
require_once '../includes/ws-constants.php';
73-
74-
// Set CORS headers
75-
header(CORS_ACCESS_CONTROL_ALLOW_ORIGIN);
76-
header('Access-Control-Allow-Methods: POST, OPTIONS'); // Allowed methods
77-
header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Specify allowed headers
78-
header('Access-Control-Expose-Headers: Authorization'); // Expose headers if needed
79-
header(CONTENT_TYPE_XML); // Set content type as XML
80-
81-
// Handle preflight requests (OPTIONS)
82-
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
83-
header(CORS_ACCESS_CONTROL_MAX_AGE); // Cache the preflight response for 600 seconds (10 minutes)
84-
http_response_code(RESPONSE_CODE_NO_CONTENT); // No Content
85-
exit();
86-
}
87-
88-
// Allow only POST requests
89-
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
90-
http_response_code(RESPONSE_CODE_METHOD_NOT_ALLOWED);
91-
header(CONTENT_TYPE_XML);
92-
echo ERROR_MESSAGE_METHOD_NOT_ALLOWED;
93-
exit();
94-
}
120+
// Authenticate the request using the shared function
121+
authenticateRequest($lSecurityLevel);
95122

96-
switch ($lSecurityLevel) {
97-
default:
98-
case SECURITY_LEVEL_INSECURE:
99-
$lProtectAgainstCommandInjection = false;
100-
$lProtectAgainstXSS = false;
101-
$lRequireAuthentication = false;
102-
break;
103-
case SECURITY_LEVEL_MEDIUM:
104-
$lProtectAgainstCommandInjection = false;
105-
$lProtectAgainstXSS = false;
106-
$lRequireAuthentication = true;
107-
break;
108-
case 2:
109-
case 3:
110-
case 4:
111-
case SECURITY_LEVEL_SECURE:
112-
$lProtectAgainstCommandInjection = true;
113-
$lProtectAgainstXSS = true;
114-
$lRequireAuthentication = true;
115-
break;
116-
}
117-
118-
// Shared: Include the shared JWT token authentication function
119-
require_once '../includes/ws-authenticate-jwt-token.php';
120-
121-
// Shared: Authenticate the user if required
122-
if ($lRequireAuthentication) {
123-
try {
124-
$lDecodedToken = authenticateJWTToken(); // Authenticate using the shared function
125-
} catch (InvalidTokenException $e) {
126-
http_response_code(RESPONSE_CODE_UNAUTHORIZED);
127-
header(CONTENT_TYPE_XML);
128-
echo '<?xml version="1.0" encoding="UTF-8"?><error><message>Unauthorized: ' . htmlspecialchars($e->getMessage()) . '</message></error>';
129-
exit();
130-
}
123+
// Validate the target host to protect against command injection, if security is enabled
124+
if ($lSecurityLevel >= SECURITY_LEVEL_MEDIUM) {
125+
$lProtectAgainstCommandInjection = true;
126+
$lProtectAgainstXSS = true;
127+
} else {
128+
$lProtectAgainstCommandInjection = false;
129+
$lProtectAgainstXSS = false;
131130
}
132131

133-
// Validate the target host to protect against command injection, if security is enabled
134132
if ($lProtectAgainstCommandInjection) {
135133
$lTargetHostValidated = preg_match(IPV4_REGEX_PATTERN, $pTargetHost) ||
136134
preg_match(DOMAIN_NAME_REGEX_PATTERN, $pTargetHost) ||
@@ -157,7 +155,7 @@ function lookupDNS($pTargetHost) {
157155
}
158156

159157
// Get the current timestamp
160-
$lTimestamp = date('Y-m-d H:i:s');
158+
$lTimestamp = date(DATE_TIME_FORMAT);
161159

162160
// Create a structured response as an associative array
163161
$response = array(
@@ -184,4 +182,5 @@ function lookupDNS($pTargetHost) {
184182
// Send a fault response back to the client if an error occurs
185183
$lSOAPWebService->fault('Server', "SOAP Service Error: " . htmlspecialchars($e->getMessage()));
186184
}
185+
187186
?>

src/webservices/soap/ws-echo.php

Lines changed: 82 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
<?php
22

33
class MethodExecutionException extends Exception {}
4+
class CommandExecutionException extends Exception {}
45

5-
// Include the nusoap library
6+
// Include the nusoap library and required constants
67
require_once './lib/nusoap.php';
8+
require_once '../includes/ws-constants.php';
9+
require_once '../includes/ws-authenticate-jwt-token.php';
710

811
$lServerName = $_SERVER['SERVER_NAME'];
912

@@ -37,86 +40,124 @@ class MethodExecutionException extends Exception {}
3740
'',
3841
array(
3942
'message' => array('name' => 'message', 'type' => 'xsd:string'),
43+
'command' => array('name' => 'command', 'type' => 'xsd:string'),
4044
'securityLevel' => array('name' => 'securityLevel', 'type' => 'xsd:string'),
4145
'timestamp' => array('name' => 'timestamp', 'type' => 'xsd:string'),
4246
'output' => array('name' => 'output', 'type' => 'xsd:string')
4347
)
4448
);
4549

46-
// Define the "echoMessage" method
50+
/**
51+
* Function: authenticateRequest
52+
* Handles request authentication and CORS headers.
53+
*
54+
* @param int $lSecurityLevel The security level.
55+
* @throws InvalidTokenException If the authentication fails.
56+
*/
57+
function authenticateRequest($lSecurityLevel) {
58+
59+
// Set CORS headers
60+
header(CORS_ACCESS_CONTROL_ALLOW_ORIGIN);
61+
header('Access-Control-Allow-Methods: POST, OPTIONS'); // Allowed methods
62+
header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Specify allowed headers
63+
header('Access-Control-Expose-Headers: Authorization'); // Expose headers if needed
64+
header(CONTENT_TYPE_XML); // Set content type as XML
65+
66+
// Handle preflight requests (OPTIONS)
67+
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
68+
header(CORS_ACCESS_CONTROL_MAX_AGE); // Cache the preflight response for 600 seconds (10 minutes)
69+
http_response_code(RESPONSE_CODE_NO_CONTENT); // No Content
70+
exit();
71+
}
72+
73+
// Allow only POST requests
74+
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
75+
http_response_code(RESPONSE_CODE_METHOD_NOT_ALLOWED);
76+
header(CONTENT_TYPE_XML);
77+
echo ERROR_MESSAGE_METHOD_NOT_ALLOWED;
78+
exit();
79+
}
80+
81+
// Authenticate the user if required
82+
if ($lSecurityLevel >= SECURITY_LEVEL_MEDIUM) {
83+
try {
84+
$lDecodedToken = authenticateJWTToken(); // Authenticate using the shared function
85+
} catch (InvalidTokenException $e) {
86+
http_response_code(RESPONSE_CODE_UNAUTHORIZED);
87+
header(CONTENT_TYPE_XML);
88+
echo ERROR_MESSAGE_UNAUTHORIZED_PREFIX . 'Unauthorized: ' . htmlspecialchars($e->getMessage()) . ERROR_MESSAGE_UNAUTHORIZED_SUFFIX;
89+
exit();
90+
}
91+
}
92+
}
93+
94+
/**
95+
* Define the "echoMessage" method
96+
*
97+
* @param string $pMessage The message to echo.
98+
* @return array An associative array containing the echoed message and metadata.
99+
* @throws MethodExecutionException If there is an error executing the method.
100+
*/
47101
function echoMessage($pMessage) {
48102

49-
try{
103+
try {
50104
// Include required constants and utility classes
51105
require_once '../../includes/constants.php';
52106
require_once '../../classes/EncodingHandler.php';
53107
require_once '../../classes/SQLQueryHandler.php';
54108

55109
$SQLQueryHandler = new SQLQueryHandler(0);
56-
57110
$lSecurityLevel = $SQLQueryHandler->getSecurityLevelFromDB();
58-
59111
$Encoder = new EncodingHandler();
60112

61-
switch ($lSecurityLevel){
62-
default: // Default case: This code is insecure
63-
case "0": // This code is insecure
64-
case "1": // This code is insecure
65-
$lProtectAgainstCommandInjection=false;
66-
$lProtectAgainstXSS = false;
67-
break;
68-
69-
case "2":
70-
case "3":
71-
case "4":
72-
case "5": // This code is fairly secure
73-
$lProtectAgainstCommandInjection=true;
74-
$lProtectAgainstXSS = true;
75-
break;
76-
}// end switch
77-
113+
// Authenticate the request using the shared function
114+
authenticateRequest($lSecurityLevel);
115+
116+
// Set security-related variables
117+
$lProtectAgainstCommandInjection = $lSecurityLevel >= SECURITY_LEVEL_MEDIUM;
118+
$lProtectAgainstXSS = $lSecurityLevel >= SECURITY_LEVEL_MEDIUM;
119+
78120
// Apply XSS protection if enabled
79-
if ($lProtectAgainstXSS) {
80-
$lMessage = $Encoder->encodeForHTML($pMessage);
81-
} else {
82-
$lMessage = $pMessage;
83-
}
121+
$lMessage = $lProtectAgainstXSS ? $Encoder->encodeForHTML($pMessage) : $pMessage;
122+
123+
// Construct the command
124+
$lCommand = $lProtectAgainstCommandInjection
125+
? escapeshellcmd("echo " . escapeshellarg($lMessage))
126+
: "echo $lMessage";
84127

85-
// Handle command execution based on the protection flag
86-
if ($lProtectAgainstCommandInjection) {
87-
$lResult = $lMessage;
88-
} else {
89-
// Allow command injection vulnerability (insecure)
90-
$lResult = shell_exec("echo " . $lMessage);
128+
// Execute the command and capture output
129+
$lOutput = shell_exec($lCommand);
130+
if ($lOutput === null) {
131+
throw new CommandExecutionException("Command execution failed.");
91132
}
92133

93134
// Get the current timestamp
94-
$lTimestamp = date('Y-m-d H:i:s');
135+
$lTimestamp = date(DATE_TIME_FORMAT);
95136

96137
// Create a structured response as an associative array
97138
$lResponse = array(
98139
'message' => $lMessage,
140+
'command' => $lCommand,
99141
'securityLevel' => $lSecurityLevel,
100142
'timestamp' => $lTimestamp,
101-
'output' => $lResult
143+
'output' => $lOutput
102144
);
103145

104146
return $lResponse; // Return as an array for NuSOAP to serialize
105147

106-
}catch(Exception $e){
107-
$lMessage = "Error executing method echoMessage in webservice ws-echo.php";
148+
} catch (Exception $e) {
149+
$lMessage = "Error executing method echoMessage in webservice ws-echo.php: " . $e->getMessage();
108150
throw new MethodExecutionException($lMessage);
109-
}// end try
110-
111-
} // end function echoMessage
151+
}
152+
}
112153

113154
// Handle the SOAP request with error handling
114155
try {
115156
// Process the incoming SOAP request
116157
$lSOAPWebService->service(file_get_contents("php://input"));
117158
} catch (Exception $e) {
118159
// Send a fault response back to the client
119-
$lSOAPWebService->fault('Server', "SOAP Service Error: " . $e->getMessage());
160+
$lSOAPWebService->fault('Server', "SOAP Service Error: " . htmlspecialchars($e->getMessage()));
120161
}
121162

122163
?>

0 commit comments

Comments
 (0)