5
\$\begingroup\$

My web application can take URL-encoded strings and return a JSON that contains information, like API calls.

My main concerns are with security. I need a cheap and easy (but secure) way to do certain commands. This part is handled by the adminhandle section of the code. My code is currently designed to be able to be deployed on a heroku app, but simply changing:

port = int(os.environ.get('PORT', 17995))

at line 12 to:

port = 80

should be fine, allowing you to access this server from http://localhost. Inside the code itself contains a hashed password and a salt. The password I use I generated with the random module (perhaps it is worth noting, since there are "more secure" RNGs out there).

My server is currently operational at here.

from twisted.web.server import Site
from twisted.web.resource import Resource
from twisted.internet import ssl, reactor
from twisted.python.modules import getModule
import urllib.parse
import cgi
import json
import os
import hashlib
admin_salt = ''
realpass = ''
port = int(os.environ.get('PORT', 17995))
class FormPage(Resource):
    isLeaf = True
    def render_GET(self, request):
        out = {}
        print(request.uri)
        x = (request.uri).decode('ascii')
        x = x[1:]
        x = todi(x)
        request.setHeader('Content-Type', 'text/plain; charset=UTF-8')
        valid = False
        if 'adminaccess' in x:
            vlr = adminhandle(x)
            out.update({'ADMIN':vlr[0]})
            if len(vlr) != 1:
                out.update({'CMDRES':vlr[1]})
        if 'key' in x:
            if x['key'] == 'chess':
                valid = True
                out.update({'my_name':'Bob'})
        out.update({'success':valid})
        return json.dumps(out).encode('UTF-8')


##    def render_POST(self, request):
##        x = request.content.read()
##        print(x)
##        return x

def todi(st):
    if len(st) == 0:
        return '{}'
    if st[len(st)-1] == '/':
        st = st[:-1]
    if len(st) == 0:
        return '()'
    if st[0] == '?':
        st = st[1:]
    st = urllib.parse.parse_qsl(st)
    return dict(st)

def adminhandle(di):
    rawupass = (admin_salt+di['adminaccess']).encode('ascii')
    hashupass = hashlib.sha512(rawupass).hexdigest()
    result = [False]
    if hashupass == realpass:
        #access granted!
        del result[0]
        result.append(True)
        if 'command' in di:
            if di['command'] == 'listdir':
                result.append(str(os.listdir(os.getcwd())))
    return result

#certData = getModule(__name__).filePath.sibling('server.pem').getContent()
#certificate = ssl.PrivateCertificate.loadPEM(certData)
factory = Site(FormPage())
reactor.listenTCP(port, factory)
#reactor.listenSSL(443, factory, certificate.options())
reactor.run()
\$\endgroup\$
0
2
\$\begingroup\$

The first problem is the function todi. I'm assuming a better name for it would be to_dict. If that is the case it does not make sense, that it sometimes returns the string "{}", sometimes "()" and sometimes an actual dictionary. I think this would be equivalent/better:

def to_dict(st):
    st = st.strip("/?")
    if not st:
        return dict()
    return dict(urllib.parse.parse_qsl(st))

This strips of any "/" or "?" from the beginning or end of the string and returns an empty dictionary if the string is empty.

Your function adminhandle should take the parameters it needs as parameters, not retrieve them on its own from data. It can also be slightly simplified by using the fact that lists are mutable, so you can just write result[0] = True instead of del result[0]; result.append(True):

def adminhandle(adminaccess, command):
    result = [False, ""]
    if adminaccess is not None:
        rawupass = (admin_salt+adminaccess).encode('ascii')
        hashupass = hashlib.sha512(rawupass).hexdigest()

        if hashupass == realpass:
            #access granted!
            result[0] = True
            if command == 'listdir':
                result[1] = str(os.listdir(os.getcwd()))
    return result

x is also a really bad name. At least name it data or something telling the reader what it actually is.

    data = to_dict(request.uri.decode('ascii')[1:])

Your placeholder variable valid is also not necessary. As is writing out.update({'success': valid}). out['success'] = valid is sufficient. I would also use the fact that dictionaries have a dict.get method, that returns None if the key does not exist.

    out = {'success': False}
    adminaccess, command = data.get('adminaccess'), data.get('command')
    out['ADMIN'], out['CMDRES'] = adminhandle(adminaccess, command)
    if not out['CMDRES']:
        del out['CMDRES']
    if data.get('key') == 'chess':
        out['my_name'] = 'Bob'
        out['success'] = True
    return json.dumps(out).encode('UTF-8')
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.