Overview: I am trying to develop a Python Flask web aplication named (SSL Scanner) which uses the “testssl.sh script” by Mr. Dirk Wetter (as available on Github) as backend. The application is supposed to be hosted on IIS and on the web-page there is an input field where the hostname (with or without port number) or an ip-address can be entered by the user and a button “Begin Scan”. Upon clicking the button “Begin Scan”, the hostname is extracted from the html form in the file named “app.py” and is passed on as a parameter to the command that I wish to execute with respect to the testssl.sh script for scanning the server configuration of the entered hostname.
I have extracted the ubuntu executable (ubuntu.exe) and have added it to the path of the Environment and System Variables, so that I am able to run shell commands (in this case testssl.sh script) just by appending the word "bash" in front of the actual command.
Here firstly while executing the flask application, the directory is changed to where the testssl.sh script is stored and then the scan can be performed by executing the “testssl.sh” script as can be seen from the code written in the file named “app.py”. The “testssl.sh” script generates a html file which gets stored in a particular directory and is finally served as a download link on the web interface along with a proper message once the scan is completed. There is one other thing that I wish to mention that, while the scan is running, i.e. after entering the hostname and then after clicking on the “Begin Scan” button, a message is supposed to be shown on the web interface “Scanning the server configuration of entered hostname”, and once the scan is completed, this message is replaced by the message "Scan completed for entered hostname along with the download link for the report is shown on the web interface".
Problem: Now, when I am running this flask application in localhost:5000 (flask development server), it is running successfully as per expectations, but after deploying it on IIS, when I access the website (the one deployed on the IIS) the “Internal_scanner.html” template is being rendered properly as expected, but after entering the hostname and clicking on the “Begin Scan” button, the message “Scanning the server configuration of entered hostname” is being shown for around 1 second and then the message "Scan completed for entered hostname along with the download link for the report is shown on the web interface" is shown but the scan does not take place.
Note: In the app.log file I was getting the error as mentionined in this StackOverflow post text but with flask and IIS, not Django and IIS and there is only one solution given here, which I have applied. And still the issue persists, but now atleast this error is not getting logged in the app.log file.
I am attaching the "Internal_Scanner.html", "app.py", and the "web.config" file for your generous reference. I will be highly obliged if anyone could kindly assist me in this regard, so that I am able to host it on IIS and make it accessible on other devices by sharing the URL.
(Modified)Flask Application app.py
from flask import Flask, render_template, request, jsonify, url_for, send_file
import os
import datetime
import subprocess
app = Flask(__name__) # app: Test SSL Interface
dateandtime = datetime.datetime.now().strftime("%H%M")
def create_app():
@app.route("/")
def index():
return render_template("Internal_Scanner.html")
@app.route("/scan", methods=["POST"])
def scan():
# Get the hostname from the form data
hostname = request.form["hostname"]
if len(hostname) > 50:
return jsonify({"return_output": "The hostname is too long."})
elif hostname.find(" ") != -1:
return jsonify(
{
"return_output": "The entered hostname is not valid as it has a whitespace."
}
)
else:
if hostname.find(":") == -1:
htmlfilename = f"{hostname}_{dateandtime}.html"
else:
htmlfilename = f"{(hostname.split(':'))[0]}_{dateandtime}.html"
# Call the backend code to scan the given hostname
os.chdir(r"C:\inetpub\wwwroot\scanner\testssl.sh-3.1dev")
# Execute the testssl.sh script with the URL and redirect the output to a folder
subprocess.run(f"bash ./testssl.sh --htmlfile ../reports/scans/{htmlfilename} -p -s -f -P -S -h -H -I -T -BB -R -C -B -O -W -F -D -4 --openssl-timeout 5 {hostname}")
download_link = f"/api/download/{htmlfilename}"
return jsonify({"return_output": f"Scan completed for hostname: {hostname}.", "download_link": download_link})
@app.route("/api/download/<string:filename>", methods=["GET"])
def download_report(filename):
# Serve the file from the reports directory
# r"C:\WSL\reports\scans"
file_path = os.path.join(r"C:\inetpub\wwwroot\scanner\reports\scans", filename)
if os.path.isfile(file_path):
return send_file(file_path, as_attachment=True)
else:
return jsonify({"error": "File not found"})
return app
Template Internal_Scanner.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<title>SSL Scanner</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
<link rel="shortcut icon" type="image/x-icon" href="{{ url_for('static', filename='images/icons.ico') }}" />
</head>
<body>
<div id="header">
<div id="logo">
<img src="{{ url_for('static', filename='images/image.jpeg') }}" width="150" height="50" alt="Logo"
title="Scanner Logo" />
</div>
<div>
<a href="" id="documentation">Documentation</a>
<a href="mailto:prateek406@gmail.com" id="contactUs">Contact Us</a>
</div>
</div>
<div id="submitBox">
<form method="POST" action="/scan" style="color: rgb(255, 119, 1)">
<label for="hostname"><b>Hostname</b></label>
<input type="text" name="hostname" id="hostname" required />
<button id="submit-btn" type="submit" style="
color: rgb(227, 216, 203);
font-weight: bold;
background-color: rgb(255, 119, 1);
border-radius: 5px;
padding: 8px 12px;
border: none;
cursor: pointer;
">
Begin Scan
</button>
<label style="margin-left: 5px">Do not refresh or go back during an ongoing scan.</label>
</form>
</div>
<div id="loading" style="
display: none;
padding: 30px;
text-align: center;
color: rgb(255, 119, 1);
">
Scanning the server configuration of <span id="hostname-text"></span>...
</div>
<div id="return_output" style="padding: 10px; text-align: center; color: rgb(255, 119, 1)">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$("form").submit(function (event) {
event.preventDefault();
var hostname = $("#hostname").val();
$("#hostname-text").text(hostname);
$("#submit-btn").attr("disabled", true);
$("#loading").show();
$("#return_output").empty(); // Remove previous output message and download link when a hostname is submitted
$.ajax({
type: "POST",
url: "/scan",
data: $("form").serialize(),
success: function (response) {
// Display the output message
$("#return_output").text(response.return_output);
// Check if download link is available in response
if (response.download_link) {
// Create a download link element
var downloadLink = $("<a>")
.attr("href", response.download_link)
.attr("download", "report.html")
.text(" Download Scan Report");
// Append download link element to the return_output div
$("#return_output").append(downloadLink);
}
},
error: function (error) {
console.log(error);
},
complete: function () {
$("#submit-btn").attr("disabled", false);
$("#loading").hide();
},
});
});
</script>
</div>
</body>
</html>
(Modified) web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="TesterHttpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
</handlers>
<httpPlatform stdoutLogEnabled="true" stdoutLogFile="C:\inetpub\wwwroot\scanner\logs" processPath="C:\inetpub\wwwroot\scanner\env\Scripts\python.exe" arguments="-m flask run --port %HTTP_PLATFORM_PORT%">
<environmentVariables>
<environmentVariable name="Ubuntu_Executable" value="C:\inetpub\wwwroot\Ubuntu\Ubuntu_2004.2021.825.0_x64" />
</environmentVariables>
</httpPlatform>
</system.webServer>
</configuration>