Repository navigation
2 ‐ The SecureRequests Class
stblr edited this page Jul 21, 2024
·
1 revision
Link to secureRequests.py source
This class provides methods to make HTTP requests with enhanced security features, including SSL/TLS configuration, certificate management, and custom headers and cookies powered with enums.
-
requests: Module reference torequests -
os: Module reference toos -
datetime: Module reference todatetime.datetime -
useEVar: Boolean flag to determine if the configuration should be loaded from environment variables. Default isFalse. -
envVars: Dictionary mapping configuration attributes to their corresponding environment variable names. -
useTLS: Boolean indicating whether to use TLS. Default isTrue. -
unsafe: Boolean indicating if unsafe operations are allowed. Default isFalse. -
certificateNeedFetch: Boolean indicating if a certificate needs to be fetched. Default isTrue. -
certificateURL: String URL from which the certificate should be fetched. Default isFalse. -
certificatePath: String path to the certificate file. Default iscacert.pem. -
logToFile: Boolean indicating if logs should be written to a file. Default isTrue. -
logLevel: Logging level. Default islogging.INFO. -
logPath: Specifies the log path. -
logExtensive: If to include extra data such as cookies and headers in the log. -
silent: Boolean indicating if logging should be silent. Default isFalse. -
suppressWarnings: Boolean indicating if warnings should be suppressed. Default isFalse.
-
session: Therequests.Sessionobject used for making HTTP requests. -
headers: A dictionary containting the header data to include in the requests. -
cookies: A dictionary containting the cookies to include in the requests.
def __init__(
self,
requests: requests = requests,
os: os = os,
datetime: datetime = datetime,
useEnv: bool = False,
customEnvVars: Optional[Dict[str, str]] = None,
headers: Optional[Dict[str, str]] = None,
useTLS: Optional[bool] = None,
unsafe: Optional[bool] = None,
certificateNeedFetch: Optional[bool] = None,
certificateURL: Optional[str] = None,
certificatePath: Optional[str] = None,
logToFile: Optional[bool] = None,
logLevel: int = logging.INFO,
logPath: str = None,
logExtensive: bool = None,
silent: Optional[bool] = None,
suppressWarnings: Optional[bool] = None,
session: requests.Session = None) -> None:secureRequest = SecureRequests(useTLS=True, unsafe=False,
fetchCert=True, logToFile=True, silent=False) config.setUseTLS(False)
config.setUnsafe(True)
config.setFetchCert(False)
config.setLogToFile(True)
# Print current configuration for verification
# print(f"useTLS: {config.getUseTLS()}")
# print(f"unsafe: {config.getUnsafe()}")
# print(f"fetchCert: {config.getFetchCert()}")
# print(f"logToFile: {config.getLogToFile()}")
# Initialize
secureRequest = SecureRequests(config=config)@handleResponse
def makeRequest(
self,
url: str,
method: str = "GET",
payload: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None,
) -> requests.Response:
"""
Makes an HTTP request with the specified parameters.
Parameters
----------
url : str
The URL for the request.
method : str, optional
The HTTP method to use (e.g., 'GET', 'POST'). Defaults to 'GET'.
payload : dict, optional
The payload to send with the request. Defaults to None.
headers : dict, optional
Custom headers to include with the request. Defaults to None.
Returns
-------
requests.Response
The HTTP response.
Raises
------
ValueError
If an unsupported HTTP method is provided.
Any StatusCode related exception from secureRequests.decorator
"""
srMethods = {
"GET": self.session.get,
"POST": self.session.post,
"PUT": self.session.put,
"DELETE": self.session.delete,
"PATCH": self.session.patch,
}
if method not in srMethods:
raise ValueError(f"Unsupported HTTP method: {method}")
headers = headers or self.headers
response = srMethods[method](
url, json=payload, headers=headers, verify=self.verify
)
self._logRequest(method, url, response=response, json=payload, headers=headers)
return responsedef _logRequest(self, method: str, url: str, response: requests.Response, **kwargs: Any) -> None:
"""
Logs the details of the HTTP request if logging is enabled.
Parameters
----------
method : str
The HTTP method used for the request (e.g., 'GET', 'POST').
url : str
The URL of the request.
response : requests.Response
The HTTP response object.
kwargs : Any
Additional keyword arguments passed to the request, such as headers or parameters.
Returns
-------
None
This method does not return any value.
Example
-------
>>> self._logRequest('GET', 'https://example.com/api/data', response, headers={'Authorization': 'Bearer token'}, params={'key': 'value'})
INFO:root:GET request to https://example.com/api/data with headers {'Authorization': 'Bearer token'} and params {'headers': {'Authorization': 'Bearer token'}, 'params': {'key': 'value'}} with Status Code 200
"""
if hasattr(self, "logger"):
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
safetyStatus = "[UNSAFE]" if self.unsafe else "[SAFE]"
tlsStatus = "[TLS]" if self.useTLS else "[NO TLS]"
logExtra = f' with headers {kwargs.get("headers")} and params {kwargs}' if self.logExtensive else ''
logDefaults = f' with Status Code {response.status_code} - {response.reason}'
logBase = f'[{timestamp}]{safetyStatus}{tlsStatus} {method} request to {url}{logExtra}{logDefaults}'
self.logger.info(logBase)The secureRequests.header* functions provide functionality for managing HTTP headers used in web requests. This includes generating default headers, setting specific header values, and updating or removing headers. This module is particularly useful for ensuring that HTTP requests adhere to certain standards and practices, such as setting appropriate user agent strings and content types.
@staticmethod
def headerGenerate(customHeaders: Optional[Dict[str, Any]] = None) -> Dict[str, str]:
"""
Generates headers for the session with optional custom values.
Parameters
----------
customHeaders : Optional[Dict[str, Any]], optional
A dictionary of headers to include or override the default headers. By default, None.
Returns
-------
Dict[str, str]
A dictionary of the generated headers, including both default headers and any custom headers provided.
Default Header Generation
-------------------------
>>> chromeMajors = list(range(110, 126))
>>> SecCHUAPlatform = ["Windows", "Macintosh", "X11"]
>>> platforms = [
... "Windows NT 10.0; Win64; x64",
... "Windows NT 6.1; Win64; x64",
... "Macintosh; Intel Mac OS X 10_15_7",
... "Macintosh; Intel Mac OS X 11_2_3",
... "X11; Linux x86_64",
... "X11; Ubuntu; Linux x86_64",
... ]
>>> randPlatform = random.choice(platforms)
>>> randSecCHUAPlatform = random.choice(SecCHUAPlatform)
>>> randChromeMajors = random.choice(chromeMajors)
>>> if not customHeaders:
>>> customHeaders = {}
Default Header
--------------
>>> defaultHeader = {
... HeaderKeys.ACCEPT.value: "application/x-www-form-urlencoded",
... HeaderKeys.CONTENT_TYPE.value: "application/x-www-form-urlencoded",
... HeaderKeys.SEC_CH_UA.value: f'"Google Chrome";v="{randChromeMajors}", "Chromium";v="{randChromeMajors}", "Not.A/Brand";v="24"',
... HeaderKeys.SEC_CH_UA_MOBILE.value: "?0",
... HeaderKeys.SEC_CH_UA_PLATFORM.value: f'"{randSecCHUAPlatform}"',
... HeaderKeys.SEC_FETCH_DEST.value: "empty",
... HeaderKeys.SEC_FETCH_MODE.value: "cors",
... HeaderKeys.SEC_FETCH_SITE.value: "same-site",
... HeaderKeys.USER_AGENT.value: f"Mozilla/5.0 ({randPlatform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{randChromeMajors}.0.0.0 Safari/537.36"
... }
Example
-------
>>> from secureRequests import HeaderKeys
>>> from secureRequests import SecureRequests
>>> customHeaders = {HeaderKeys.CONTENT_TYPE.value: "application/json", HeaderKeys.AUTHORIZATION.value: "Bearer Token"}
>>> # Directly genereate them on creation
>>> sr = SecureRequests(headers=customHeaders)
>>> # Regenerate a needed one works aswell
>>> headers = secureRequests.headerGenerate(customHeaders=customHeaders)
>>> print(sr.headers)
>>> {
... 'Accept': 'application/x-www-form-urlencoded',
... 'Authorization': 'Bearer Token',
... 'Content-Type': 'application/json',
... 'Sec-CH-UA': '"Google Chrome";v="120", "Chromium";v="120", "Not.A/Brand";v="24"',
... 'Sec-CH-UA-Mobile': '?0',
... 'Sec-CH-UA-Platform': '"Windows"',
... 'Sec-Fetch-Dest': 'empty',
... 'Sec-Fetch-Mode': 'cors',
... 'Sec-Fetch-Site': 'same-site',
... 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'}
"""
chromeMajors = list(range(110, 126))
SecCHUAPlatform = ["Windows", "Macintosh", "X11"]
platforms = [
"Windows NT 10.0; Win64; x64",
"Windows NT 6.1; Win64; x64",
"Macintosh; Intel Mac OS X 10_15_7",
"Macintosh; Intel Mac OS X 11_2_3",
"X11; Linux x86_64",
"X11; Ubuntu; Linux x86_64",
]
randPlatform = random.choice(platforms)
randSecCHUAPlatform = random.choice(SecCHUAPlatform)
randChromeMajors = random.choice(chromeMajors)
if not customHeaders:
customHeaders = {}
defaultHeader = {
HeaderKeys.ACCEPT.value: "application/x-www-form-urlencoded",
HeaderKeys.CONTENT_TYPE.value: "application/x-www-form-urlencoded",
HeaderKeys.SEC_CH_UA.value: f'"Google Chrome";v="{randChromeMajors}", "Chromium";v="{randChromeMajors}", "Not.A/Brand";v="24"',
HeaderKeys.SEC_CH_UA_MOBILE.value: "?0",
HeaderKeys.SEC_CH_UA_PLATFORM.value: f'"{randSecCHUAPlatform}"',
HeaderKeys.SEC_FETCH_DEST.value: "empty",
HeaderKeys.SEC_FETCH_MODE.value: "cors",
HeaderKeys.SEC_FETCH_SITE.value: "same-site",
HeaderKeys.USER_AGENT.value: f"Mozilla/5.0 ({randPlatform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{randChromeMajors}.0.0.0 Safari/537.36"
}
headers = {key: customHeaders.get(key, defaultValue) for key, defaultValue in defaultHeader.items()}
# Add any custom headers that are not in the default set
for key, value in customHeaders.items():
if key not in headers:
headers[key] = value
return headers def headerSetKey(self, key: HeaderKeys, value: str) -> None:
"""
Sets a specific header key to a given value.
Parameters
----------
key : HeaderKeys
The header key to set.
value : str
The value to set for the header key.
Returns
-------
None
Example
-------
>>> headers = {"Content-Type": "application/json"}
>>> MyClass.headerSetKey(HeaderKeys.AUTHORIZATION, "Bearer token123")
>>> print(headers)
{'Content-Type': 'application/json', 'Authorization': 'Bearer token123'}
"""
self.headers[key.value] = value def headerRemoveKey(self, key: HeaderKeys) -> None:
"""
Removes a specific header key.
Parameters
----------
key : HeaderKeys
The header key to remove.
Returns
-------
None
Example
-------
>>> headers = {"Content-Type": "application/json", "Authorization": "Bearer token123"}
>>> MyClass.headerRemoveKey(HeaderKeys.AUTHORIZATION)
>>> print(headers)
{'Content-Type': 'application/json'}
"""
if key.value in self.headers:
del self.headers[key.value] def headerUpdateMultiple(self, newHeader: Dict[HeaderKeys, str]) -> None:
"""
Updates specific headers based on the provided dictionary.
Parameters
----------
newHeader : Dict[HeaderKeys, str]
The new headers to update, where keys are of type HeaderKeys.
Returns
-------
None
Example
-------
>>> headers = {"Content-Type": "application/json"}
>>> MyClass.headerUpdateMultiple({HeaderKeys.AUTHORIZATION: "Bearer token123", HeaderKeys.ACCEPT: "application/xml"})
>>> print(headers)
{'Content-Type': 'application/json', 'Authorization': 'Bearer token123', 'Accept': 'application/xml'}
"""
for key, value in newHeader.items():
self.headers[key.value] = value def headerRemoveMultiple(self, keys: List[HeaderKeys]) -> None:
"""
Removes multiple header keys at once.
Parameters
----------
keys : List[HeaderKeys]
The list of header keys to remove.
Returns
-------
None
Example
-------
>>> headers = {"Content-Type": "application/json", "Authorization": "Bearer token123", "Accept": "application/xml"}
>>> MyClass.headerRemoveMultiple([HeaderKeys.AUTHORIZATION, HeaderKeys.ACCEPT])
>>> print(headers)
{'Content-Type': 'application/json'}
"""
for key in keys:
if key.value in self.headers:
del self.headers[key.value]The secureRequests.cookie* functions provide functionality for managing HTTP cookies used in web requests.
def _serializeCookieInfo(self, cookieInfo: Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]) -> str:
"""
Serializes the cookie attributes into a string.
Parameters:
------------
cookieInfo: Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]
A dictionary containing the attributes of the cookie.
Returns:
------------
str
The serialized string of cookie attributes.
"""
return '|'.join(f'{key.value}={value}' for key, value in cookieInfo.items()) def _deserializeCookieInfo(self, cookieInfoStr: str) -> Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]:
"""
Deserializes the string back into a dictionary of cookie attributes.
Parameters:
------------
cookieInfoStr: str
The serialized string of cookie attributes.
Returns:
------------
Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]
The deserialized dictionary of cookie attributes.
"""
items = cookieInfoStr.split('|')
cookieInfo = {}
for item in items:
key, value = item.split('=')
cookieInfo[CookieAttributeKeys(key)] = value
return cookieInfo def cookieUpdate(self, key: CookieKeys, cookieInfo: Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]) -> None:
"""
Sets or updates a single cookie with specified attributes.
Parameters:
------------
key: CookieKeys
The key identifying the cookie to be set or updated.
cookieInfo: Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]
A dictionary containing the attributes of the cookie.
Example:
------------
>>> cookie_attributes = {
... CookieAttributeKeys.DOMAIN: 'example.com',
... CookieAttributeKeys.PATH: '/',
... CookieAttributeKeys.EXPIRES: datetime.now() + timedelta(days=7),
... CookieAttributeKeys.SECURE: True,
... CookieAttributeKeys.HTTP_ONLY: True,
... CookieAttributeKeys.SAME_SITE: 'Strict',
... CookieAttributeKeys.MAX_AGE: 604800 # 7 days in seconds
... }
# Set the cookie
secureRequests.cookieUpdate(CookieKeys.SESSION_ID, cookieAttributes)
"""
cookieValue = self._serializeCookieInfo(cookieInfo)
self.session.cookies.set(key.value, cookieValue) def cookieGet(self, key: CookieKeys) -> Optional[Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]]:
"""
Retrieves the attributes of a single cookie.
Parameters:
------------
key: CookieKeys
The key identifying the cookie to be retrieved.
Returns:
------------
Optional[Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]]
A dictionary containing the attributes of the cookie if it exists, otherwise None.
"""
cookieValue = self.session.cookies.get(key.value)
if cookieValue:
return self._deserializeCookieInfo(cookieValue)
return None def cookieRemove(self, key: CookieKeys) -> None:
"""
Removes a single cookie.
Parameters:
------------
key: CookieKeys
The key identifying the cookie to be removed.
"""
self.session.cookies.pop(key.value, None) def cookieUpdateMultiple(self, cookies: Dict[CookieKeys, Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]]) -> None:
"""
Adds or updates multiple cookies at once.
Parameters:
------------
cookies: Dict[CookieKeys, Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]]
A dictionary of cookies with their attributes to be set or updated.
Example:
------------
>>> cookies = {
... CookieKeys.AUTH_TOKEN: {
... CookieAttributeKeys.DOMAIN: 'example.com',
... CookieAttributeKeys.PATH: '/',
... CookieAttributeKeys.EXPIRES: datetime.now() + timedelta(days=30),
... CookieAttributeKeys.SECURE: True,
... CookieAttributeKeys.HTTP_ONLY: True,
... CookieAttributeKeys.SAME_SITE: 'Lax'
... },
... CookieKeys.USER_PREFERENCES: {
... CookieAttributeKeys.DOMAIN: 'example.com',
... CookieAttributeKeys.PATH: '/',
... CookieAttributeKeys.EXPIRES: datetime.now() + timedelta(days=7),
... CookieAttributeKeys.SAME_SITE: 'None',
... CookieAttributeKeys.SECURE: True
... }
... }
# Set or update multiple cookies
secureRequests.cookieUpdateMultiple(cookies)
"""
for key, cookieInfo in cookies.items():
self.cookieUpdate(key, cookieInfo) def cookieGetAll(self) -> Dict[CookieKeys, Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]]:
"""
Retrieves all cookies with their attributes.
Returns:
------------
Dict[CookieKeys, Dict[CookieAttributeKeys, Union[str, bool, int, datetime]]]
A dictionary of all cookies with their attributes.
"""
allCookies = {}
for cookie in self.session.cookies:
key = CookieKeys(cookie.name)
cookieInfo = self._deserializeCookieInfo(cookie.value)
allCookies[key] = cookieInfo
return allCookiesfrom datetime import datetime, timedelta
from secureRequests import SecureRequests
from secureRequests import HeaderKeys, CookieKeys, CookieAttributeKeys
# Creating secureRequests empty to demonstrate a third way of configuring it
sr = SecureRequests(headers={HeaderKeys.CONTENT_TYPE.value: "application/json"})
sr.headerSetKey(HeaderKeys.AUTHORIZATION, "Bearer token123")
sr.cookieUpdate(CookieKeys.SESSION_ID, {
CookieAttributeKeys.PATH: '/',
CookieAttributeKeys.EXPIRES: datetime.now() + timedelta(days=7),
CookieAttributeKeys.SECURE: True
})
response = sr.makeRequest("https://httpbin.org/get")
print(response.status_code)
print(sr.headers)
print(sr.cookieGetAll())