Einrichten der Digest-Authentifizierung mit Python @Lambda

Einführung

Ich habe nach Digest-Authentifizierung gesucht und nur wenige Informationen gefunden. Daher habe ich sie als Fortsetzung von Grundlegende Authentifizierungseinstellungen mit Python @ Lambda geschrieben. Ich habe die Digest-Authentifizierung selbst kaum berührt und sie dient auch als Untersuchung des Mechanismus.

Code

import os
import ctypes
import json
import base64
import time
import hashlib
import copy
from Crypto.Cipher import AES

accounts = [
    {
        "user": "user1",
        "pass": "pass1"
    },
    {
        "user": "user2",
        "pass": "pass2"
    }
    ]

realm = "[email protected]"
qop = "auth"
#Im Gegensatz zur Standardauthentifizierung können Sie nach der Authentifizierung ein Zeitlimit festlegen, also habe ich es eingegeben
timeout = 30 * (10 ** 9) # 30 seconds
#Vorbereiten von Informationen zur Verwendung mit AES-Verschlüsselung
raw_key = "password1234567890"
raw_iv = "12345678"
key = hashlib.sha256(raw_key.encode()).digest()
iv = hashlib.md5(raw_iv.encode()).digest()

def lambda_handler(event, context):
    request = event.get("Records")[0].get("cf").get("request")
    
    if not check_authorization_header(request):
        return {
            'headers': {
                'www-authenticate': [
                    {
                        'key': 'WWW-Authenticate',
                        'value': create_digest_header()
                    }
                ]
            },
            'status': 401,
            'body': 'Unauthorized'
        }
            
        
    return request

def check_authorization_header(request: dict) -> bool:
    headers = request.get("headers")
    authorization_header = headers.get("authorization")
    
    if not authorization_header:
        return False
    
    data = {
        "method": request.get("method"),
        "uri": request.get("uri")
    }
    header_value = authorization_header[0].get("value")
    #Digest-Authentifizierungsdaten haben das Format "Digest ~". Löschen Sie daher zuerst unnötige Teile
    header_value = header_value[len("Digest "):]
    
    #Jeder Wert wird durch ein Komma getrennt, also teilen Sie ihn
    values = header_value.split(",")
    data = {
        "method": request.get("method"),
        "uri": request.get("uri")
    }
    #Teilen Sie jeden Wert erneut, um die Handhabung zu vereinfachen
    for v in values:
        #Da nonce Base64-codiert ist, ist es einfach`=`Wenn Sie es mit teilen, wird es seltsam sein, also beschäftigen wir uns damit
        idx = v.find("=")
        vv = [v[0:idx], v[idx+1:]]
        #Da vorher und nachher ein Leerzeichen mit halber Breite vorhanden ist, löschen Sie es
        vv[0] = vv[0].strip()
        vv[1] = vv[1].strip()
        #Einige Werte sind in doppelte Anführungszeichen eingeschlossen. Löschen Sie sie daher.
        if vv[1].startswith("\""):
            vv[1] = vv[1][1:]
        if vv[1].endswith("\""):
            vv[1] = vv[1][:len(vv[1])-1]
            
        data[vv[0]] = vv[1]

    for account in accounts:
        if account.get("user") != data.get("username"):
            continue
        
        d = copy.deepcopy(data)
        d["user"] = account.get("user")
        d["pass"] = account.get("pass")
        
        encoded_value = create_validation_data(d)

        if d.get("response") == encoded_value:
            if check_timeout(data.get("nonce")):
                return True

    return False

def check_timeout(nonce: str) -> bool:
    aes = AES.new(key, AES.MODE_CBC, iv)
    value = aes.decrypt(base64.b64decode(nonce.encode())).decode()
    #Mit Polsterung bei der Verschlüsselung mit AES`_`Wird hinzugefügt, löschen Sie diesen Betrag
    while value.endswith("_"):
        value = value[:len(value)-1]

    return int(value) + timeout > time.time_ns()
    
def create_validation_data(data: dict) -> str:
    v1 = "{}:{}:{}".format(data.get("user"), realm, data.get("pass"))
    vv1 = hashlib.md5(v1.encode()).hexdigest()
    v2 = "{}:{}".format(data.get("method"), data.get("uri"))
    vv2 = hashlib.md5(v2.encode()).hexdigest()
    
    v3 = "{}:{}:{}:{}:{}:{}".format(vv1, data.get("nonce"), data.get("nc"), data.get("cnonce"), qop, vv2)

    return hashlib.md5(v3.encode()).hexdigest()

def create_digest_header() -> str:
    aes = AES.new(key, AES.MODE_CBC, iv)
    timestamp = "{}".format(time.time_ns()).encode()
    #Da die Länge beim Verschlüsseln ein Vielfaches von 16 sein muss, ist sie mit Polsterung gefüllt
    while len(timestamp) % 16 != 0:
        timestamp += "_".encode()
        
    header = "Digest "
    values = {
        "realm": '"' + realm + '"',
        "qop": '"auth,auth-int"',
        "algorithm": 'MD5',
        "nonce": '"' + base64.b64encode(aes.encrypt(timestamp)).decode() + '"'
    }
    
    idx = 0
    for k, v in values.items():
        if idx != 0:
            header += ","
        header += '{}={}'.format(k, v)
        idx += 1
        
    return header

Bereit zum Bewegen

Die Einstellungen für Lambda und CloudFront sind dieselben wie für die Standardauthentifizierung, daher gibt es nichts zu beschreiben. Die AES-Verschlüsselungsbibliothek muss jedoch mit pip installiert werden, sodass ein wenig Unterstützung erforderlich ist.

Machen Sie die Bibliothek zu einer Zip-Datei

Da "pip" auf Lambda nicht ausgeführt werden kann, muss die "pip install" als Zip-Datei auf einen lokalen PC hochgeladen werden.

Zu diesem Zeitpunkt ist zu beachten, dass das Betriebssystem von AWS Lambda Amazon Linux ist. Beachten Sie, dass selbst wenn Sie eine Zip-Datei auf Ihrem lokalen Mac erstellen, diese nicht funktioniert und sagt: "Oh, ich sollte sie einfach komprimieren."

Sie können EC2 für Amazon Linux erstellen und eine Zip-Datei erstellen, Docker reicht jedoch aus, da Sie höchstens eine Zip-Datei erstellen. Also habe ich es mit Docker erstellt.

#Ziehen Sie das Amazon Linux 2-Image und starten Sie es
$ docker run -it amazonlinux:2 bash
#Installieren Sie die erforderlichen Pakete auf dem Docker-Image
$ yum install -y gcc python3 pip3 python3-devel.x86_64
#Installieren Sie Pakete zur Verwendung auf Lambda
$ pip install pycrypto -t ./
#Erstellen Sie eine Zip-Datei
$ zip -r pycrypto.zip Crypto/

Lambda_handler erstellen

Wenn Sie die Zip-Datei hochladen, ist lambda_function.py nicht mehr vorhanden. Erstellen Sie also erneut lambda_function.py und schreiben Sie lambda_handler.

Andere Websites, auf die verwiesen wird

Recommended Posts

Einrichten der Digest-Authentifizierung mit Python @Lambda
Einrichten der Basisauthentifizierung mit Python @Lambda
Zusammenfassung bei Verwendung von AWS Lambda (Python)
Authentifizierung mit Tweepy-User-Authentifizierung und Anwendungsauthentifizierung (Python)
[AWS] Verwenden von INI-Dateien mit Lambda [Python]
[Python] Beschleunigung der Verarbeitung mit Cache-Tools
Installieren Sie die Python-Bibliothek auf Lambda mit [/ tmp]
Hinweise zum Einrichten eines Docker-Containers für die Verwendung von JUMAN ++, KNP, Python
Achten Sie beim Konfigurieren der Python-Protokollierung auf disable_existing_loggers
Erreichen Sie eine grundlegende Authentifizierung mit CloudFront Lambda @ Edge mit Python 3.8
Starten Sie Python
Python-HTTP-Authentifizierung
Scraping mit Python
So richten Sie eine Python-Umgebung mit pyenv ein
Über einen Authentifizierungs-Proxy für die Kommunikation mit Python urllib3
Bearbeiten Sie Redmine mit Python Redmine
Fibonacci-Sequenz mit Python
Pythons Lambda-Ausdruck ...
Datenbereinigung mit Python
Verwenden von Python # externen Paketen
WiringPi-SPI-Kommunikation mit Python
Altersberechnung mit Python
Suchen Sie Twitter mit Python
Beispiel für die Verwendung von Lambda
Namensidentifikation mit Python
Hinweise zur Verwendung von Python-Unterprozessen
Python Umweltfreundliche japanische Umgebung
Versuchen Sie es mit Tweepy [Python2.7]
Erstellen Sie mit AWS SAM schnell eine API mit Python, Lambda und API Gateway