不,您不需要数据库。
使用以下代码设置基于套接字的安全 http 服务器。它对特定类型的 DOS 攻击具有一些基本保护,并允许您禁止特定地址访问您的服务。
注意 cmets:它们解释了重要的代码部分。确保安全地配置您的服务器。不要依赖来自您的应用程序的数据。例如,攻击者可以创建恶意请求,看起来就像来自您的应用程序的正常请求。不要在 Response 结构中插入无效数据,否则您将收到 500 Internal Server Error 回复。
这段代码没有依赖;它可以在任何操作系统上运行,因为它不使用特定于操作系统的 API。 (在 Python 3.9.0、Windows 10 上测试。)
import socket
import threading
import ssl
import time
import tempfile
import os
hostname="localhost" #hostName in your code
port=8080 #serverPort in your code - default HTTP Port is 80
banned_IPs=() #Every IP in this list is denied access to your service
#SSL-Specific settings
SSLEnabled=False #set to True if you got a valid SSL/TLS configuration for your server, and want to provide a secure version of the service
SSLContext=None #must be set if SSLEnabled - set to ssl.SSLContext with proper configuration - see documentation: https://docs.python.org/3/library/ssl.html
SSLPort=None #Port for the secure version of your service - must be set if SSLEnabled is True and may not hav the same value as port
#These are some security limits - change them if necessary
MaxIpConnections=1024 #Maximum Connections per IP address per minute - DDOS-Protection
MaxHeaderSize=8192 #8 KB maximum Header Size - Protection against malicious clients trying to fill up device-memory
MaxBodySize=1073741824 #1 GB maximum Payload Size - malicious clients may try
to overload memory with extremely large payloads
#Internal variables:
_clientRegister={} #DDOS-Protection
_max_blank_packets=128 #Detect wheter a socket has been closed
#Configuration - I use IPv4 here - replace AF_INET by AF_INET6 for IPv6
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
if SSLEnabled:
SSLSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
def IO(request,response):
"""do something with the data here.
As i don't know, what you want to do, I will do nothing here."""
pass
class Request(object):
"""A HTTP request"""
method=None #GET,POST,PUT,DELETE,...
route=None #path, /example,/foo/example,/,...
body=None #Filename of a File containing the request's Body, None if no body was present.
proto="HTTP/1.1"#Protocol, either "HTTP/1.1" or "HTTP/1.0"
headers={} #Headers, having a structure like {key:[value[,value[,...]]],[key:[...][,...]]}
ip=None #I don't need to explain that
localport=None #The Locally used port, one of port, SSLPort
secure=False #wheter the request wa made over TLS/SSL
remoteport=None #Client-Side port. Your'e probaly not gonna need it
socket=None #underlying socket. Your'e probaly not gonna need it
def __init__(self,method,route,proto,headers,bodyfile,addr,lport,sock):
self.method=method.decode("ascii")
self.route=route.decode("ascii")
self.proto=proto.decode("ascii")
for i in headers:
k=i
self.headers[k.decode("ascii")]=[]
for v in headers[i]:
self.headers[k.decode("ascii")].append(v.decode("ascii"))
self.body=bodyfile
self.ip=addr[0]
self.port=addr[1]
self.localport=lport
self.secure=(self.localport==SSLPort)
self.socket=sock
class Response(object):
"""A HTTP Response"""
status=200 #Statuscode
statustext="OK" #Statustext
proto="HTTP/1.1"#Protocol, either "HTTP/1.1" or "HTTP/1.0"
headers={"Connection":["close"]}#Same structure as in Request
body=None #Filename for Responsebody, use tempfile.mktemp() for temporary file.
socket=None #underlying socket. Your'e probaly not gonna need it
"Now some Internal functions - just ignore them"
#Some binding + listening stuff
sock.bind((hostname,port))
sock.listen(32)
if SSLEnabled:
SSLSock.bind((hostname,SSLPort))
SSLSock.listen(32)
def checkIp(ip):
if _clientRegister[ip][1]<=time.time():
del _clientRegister[ip]
def garbage_collector_thread():
while True:
try:
for i in _clientRegister:
checkIp(i)
except:
pass
time.sleep(30)#Garbage-Collection every half minute
def client_thread(cli,addr):
ip=addr[0]
if ip in banned_IPs:
cli.close()
return
if ip in _clientRegister:
_clientRegister[ip][0]+=1
if _clientRegister[ip][0]>MaxIpConnections:
cli.close()
return
else:
_clientRegister[ip]=[0,time.time()+60]
if cli.getsockname()[1]==SSLPort:
cli=SSLContext.wrap(cli,True)
header=b""
blank=0
try:
while not b"\r\n\r\n" in header:
block=cli.recv(1024)
if block==b"":
blank+=1
else:
blank=0
if blank>=_max_blank_packets:
cli.close()
return
if len(header)>MaxHeaderSize:
cli.sendall(b"HTTP/1.1 431 Header Fields Too Large\r\nConnection: closed\r\n\r\n")
cli.close()
return
header+=block
header,body=header.split(b"\r\n\r\n",1)
head={}
status,*header=header.split(b"\r\n")
for i in header:
k,v=i.split(b": ",1)
if k in header:
head[k].append(v)
else:
head[k]=[v]
method,route,proto=status.split(b" ",2)
if b"Content-Length" in header:
v=int(header[b"Content-Length"][0])
if v>MaxBodySize:
cli.sendall(b"HTTP/1.1 413 Payload too large\r\nConnection: close\r\n\r\n")
cli.close()
return
size=len(body)
returnfile=tempfile.mktemp()
f=open(returnfile,"wb")
f.write(returnfile)
while size<v:
block=cli.recv(1024)
if block==b"":
blank+=1
else:
blank=0
if blank>=_max_blank_packets:
cli.close()
f.close()
os.remove(returnfile)
return
size+=len(block)
f.write(block)
f.close()
elif body!=b"":
cli.sendall(b"HTTP/1.1 411 Length Required\r\nConnection: close\r\n\r\n")
cli.close()
return
else:
returnfile=None
request=Request(method,route,proto,head,returnfile,addr,cli.getsockname()[1],cli)
response=Response()
response.socket=cli
except ConnectionError:
cli.close()
return
except:
try:
cli.sendall(b"HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n")
except:
pass
cli.close()
return
try:
IO(request,response)
except:
try:
cli.sendall(b"HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n")
cli.close()
return
except:
cli.close()
return
try:
if response.body!=None:
response.headers["Content-Length"]=[str(os.path.getsize(response.body))]
r=b" ".join([response.proto.encode("ascii"),str(response.status).encode("ascii"),response.statustext.encode("ascii")])
r+=b"\r\n"
for i in response.headers:
k=i
for v in response.headers[k]:
r+=k.encode("ascii")+b": "+v.encode("ascii")+b"\r\n"
r+=b"\r\n"
cli.sendall(r)
if response.body!=None:
f=open(response.body,"rb")
while True:
block=f.read(1024)
if block==b"":
break
cli.sendall(block)
f.close()
cli.close()
return
except:
try:
cli.sendall(b"HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n")
cli.close()
return
except:
cli.close()
return
def SSLAccept(sock):
while True:
cli,addr=sock.accept()
threading.Thread(target=client_thread,args=(cli,addr)).start()
if SSLEnabled:
threading.Thread(target=SSLAccept,args=(SSLSock,)).start()
threading.Thread(target=garbage_collector_thread).start()
while True:
cli,addr=sock.accept()
threading.Thread(target=client_thread,args=(cli,addr)).start()