1

I'm currently trying to create a simple login in page to my actual local webpage that I'm running with on a virtual machine with Ubuntu.

I created the LoginPage.html at the location /var/www/html.

The HTML file then calls the login.cgi file in the /usr/lib/cgi-bin/login.cgi.

I get an Internal Server Error. The logs basically only shows this:

"POST /cgi-bin/login.cgi HTTP/1.1" 500 799 "http://localhost/LoginPage.html" "Mozialla/5.0 (X11; Ubtuntu; Linux x86_64; rv:84.0) Geck/201000101 Firefox/84.0

The HTML file seems to be working as intended, but when I press login and get redirected to the CGI file, I get the error on the CGI file. I have tried to remove everything the in the CGI file to leave only a couple of lines but still get the error.

My other project-files in the cgi-bin folder still work without an error.

<HTML>
<HEAD><TITLE>Login Page</TITLE></HEAD>
    <BODY>
        <CENTER>
        <FORM method="POST" action="/cgi-bin/login.cgi">
            <paragraph> Enter your login name: <input type="text" name="login">
            <paragraph> Enter your password: <input type=password name="password">
            <paragraph> <input type="submit" value="Connect">
        </FORM>
        </CENTER>
        <HR>

        </form>
    </BODY>
</HTML>
#!/usr/bin/python3
import sys
import cgi
import os
import cgitb

sys.path.insert(0,'/usr/lib/project_name')
def header():
    #print "Content-type: text/html\n"
    print("<HEAD>")
    print("<TITLE> title </TITLE>")
    print("</HEAD>")

def Log():
    print("<!DOCTYPE html>")
    print("<HTML>")
    print("<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">")
    print("  <meta charset=\"utf-8\" />")
    header()
    print("BODY")
    form = cgi.FieldStorage()
    login = "login"
    password = "test123"
    if not (form):
        header("Login Response")
        print("<BODY>")
    elsif (form.has_key("login") and form["login"].value == login and form.has_key("password") and form["password"].value == password):
        header("Connected ...")
        print("<BODY>")
        print("<center><hr><H3>Welcome back,\" , form[\"login\"].value, \".</H3><hr></center>")
        print("r\"\"\"<form><input type=\"hidden\" name=\"session\" value=\"%s\"></form>\"\"\" % (form[\"login\"].value)")
        print("<H3><a href=\"/cgi-bin/projects.cgi\">Click here to start browsing</a></H3>")
    else:
        header("No success!")
        print("<BODY>")
        print("<H3>Please go back and enter a valid login.</H3>")

def footer():
    print("</BODY>")
    print("</HTML>")

print("Content-type:text/html\r\n\r\n")
cgitb.enable()
Log()
footer()

Edit:

Here is the content of error.log after resolving the Internal Server Error:

[Tue Feb 02 08:40:41.199152 2021] [cgi:error] [pid 10292:tid 140490049578752] [client 127.0.0.1:38888] AH01215: (2)No such file or directory: exec of '/usr/lib/cgi-bin/login.cgi' failed: /usr/lib/cgi-bin/login.cgi, referer: http://localhost/LoginPage.html [Tue Feb 02 08:40:41.199411 2021] [cgi:error] [pid 10292:tid 140490049578752] [client 127.0.0.1:38888] End of script output before headers: login.cgi, referer: http://localhost/LoginPage.html

costaparas
  • 5,047
  • 11
  • 16
  • 26
Nidas
  • 13
  • 5
  • Here is the link to the latest code of this question(still not solved). https://drive.google.com/drive/folders/1Khp2LzXGkxO3ifsTIQ7R6cCFkaV-tWZf?usp=sharing – Nidas Feb 02 '21 at 07:39
  • The CGI script itself is now fine, but the error shown in `error.log` is related to the location. According to the error shown, its apparently its not in `/usr/lib/cgi-bin/`. Maybe you have the directory wrong, double check its in that directory and that the directory and CGI script both have 755 permissions. Also, it appears the CGI script may have windows line endings, so make sure you remove those as well, e.g. by running `dos2unix login.cgi` (see [this post](https://stackoverflow.com/a/51391922/14722562) for more details). – costaparas Feb 02 '21 at 08:42
  • @costaparas can you post it as an answer so i can mark it? :) – Nidas Feb 02 '21 at 09:30
  • Perfect, I've added that to the top of the answer below. – costaparas Feb 02 '21 at 09:45

1 Answers1

0

Correcting the setup:

No such file or directory: exec of '/usr/lib/cgi-bin/login.cgi' failed: /usr/lib/cgi-bin/login.cgi, referer: http://localhost/LoginPage.html

Make sure the CGI script and its parent directory have the right permissions:

chmod 755 /usr/lib/cgi-bin/ /usr/lib/cgi-bin/login.cgi

Also, it appears the CGI script may have windows line endings, so make sure you remove those as well, e.g. by running dos2unix login.cgi (see this post for more details).

Resolving the Internal Server Error:

First, make at least the following changes to correct your syntax (which is what's causing the Internal Server Error):

  1. elsif should be elif
  2. Your header function should take in an argument, i.e. def header(title)
  3. Where you have form.has_key, change it to use in since has_key is now deprecated, e.g. "password" in form instead of form.has_key("password")

The corrected condition would look like this:

elif "login" in form and form["login"].value == login and "password" in form and form["password"].value == password:

As an aside, stick to HTML5, which is supported by all the latest browsers, and the center tag is now deprecated (its gone the way of the marquee tag). Use CSS instead.

Also, as a complete side note, these days, its uncommon to see ALL CAPS used for HTML tags. You have used that style in some places, but I suggest dropping it in favor of lowercase tag names.

Simplfying the HTML generation:

In addition to the above, I recommend using f-strings for string formatting and also multi-line strings to simplify the logic a bit.

Below are some examples of how you can enhance your code.

Your header function may look like this using f-strings and multi-line strings as suggested. The title argument is optional.

def header(title=""):
    print(f"""
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="utf-8">
            <title> {title} </title>
        </head>
        <body>
    """)

Your footer function could look like this:

def footer():
    print("""
        </body>
        </html>
    """)

Finally, your HTML form could look like this, using text-align: center; for the centering:

print(f"""
    <hr>
        <h3 style="text-align: center;">Welcome back { form["login"].value }</h3>
    <hr>
    <form>
        <input type="hidden" name="session" value="{ form["login"].value }">
    </form>
    <h3>
        <a href="/cgi-bin/projects.cgi">Click here to start browsing</a>
    </h3>
""")

Beautifying the HTML:

To beautify the HTML further, you can import textwrap and then use textwrap.dedent() to remove the common leading spaces from the HTML (due to the indentation in Python), e.g.

print(textwrap.dedent(f"""
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <title> {title} </title>
    </head>
    <body>
"""))

This gives two advantages:

  1. It removes the unnecessary leading spaces before sending it over the network -- so you send less data, saving bandwidth
  2. The HTML source is prettier when you inspect it using a client browser -- useful for debugging

Alternatively, use HTML templating:

A further enhancement is to make use of a HTML templating framework, such as Jinja2. This will allow you to store the HTML into files and then render the HTML files by passing variables.

Then, your Python code would become a lot simpler. You would just render the template and pass the variables you want. For example, for you header, it could be like this:

template = env.get_template('header.html')
print(template.render(title='Connected ...'))

You will need to set up the Environment like this first:

from jinja2 import Environment

env = Environment(
    loader=PackageLoader('package', 'templates')
    autoescape=select_autoescape(['html'])
)

And place your header.html file in a directory called templates.

You may also want to read about the seperation of concerns principle and the MVC architecture. Using templates like this is just one step closer to achieving MVC.

Final point on security:

It looks like you are using a HTML hidden field to store the username, and treating that as a session token. This is highly insecure and won't scale. Though, it may be sufficient for your purposes. A simple way to improve on it is to store it as a cookie using the Set-cookie header, and make it HttpOnly, e.g.

Set-cookie: session=value; HttpOnly

where value could be some unique token (e.g. a uuid).

This is much better than your current approach.

But, even better yet, you could implement security using a library like PyJWT instead.

costaparas
  • 5,047
  • 11
  • 16
  • 26
  • 1
    I'm really impressed by all the extra tips(which i'm implemented in an instant :) ) But somehow my problem resides. I was able to find the proper error.log this time: "End of script output before headers", which I think is weird. I also tried to check and change every permissions to be 755 (according to some search) but the problem still lingers. – Nidas Feb 01 '21 at 13:10
  • This happens when your CGI script prints something before it prints the `Content-type` header. So, `Content-type: text/html\r\n\r\n` has to be the *very first* thing it prints. Likely, you still have (or have introduced) a bug in your code which triggers an error before the `Content-type` header is printed. Anything else shown in the `error.log` file? Also, double check your hashbang line `#!/usr/bin/python3` is valid (i.e. the path exists). – costaparas Feb 01 '21 at 13:13
  • The python directory is valid and here is the total error message:"[Mon Feb 01 14:31:02.248021 2021] [cgi:error] [pid 6967:tid 140489982437120] [client 127.0.0.1:38120] End of script output before headers: login.cgi, referer: http://localhost/LoginPage.html" I also tried to add the Content type to the very first step after the imports – Nidas Feb 01 '21 at 13:33
  • Try running the CGI script on the command-line directly, if there is any syntax error, it will show you and you can fix it and then try again via the browser. – costaparas Feb 01 '21 at 13:35
  • Tried it, still doesn't work....I'm about to bash my head in the wall T_T Same error – Nidas Feb 01 '21 at 14:50
  • @nidas can you please attach the latest version of your code to the question, along with the output of `tail -10 error.log` and `/usr/bin/python3 --version`? – costaparas Feb 01 '21 at 21:12
  • 1
    I added the latest link to my code as a new comment to the question. And thanks for putting so much effort into it :) Python version is 3.6.9 – Nidas Feb 02 '21 at 07:45