Wie man den Passster-Passwortschutz mit 1,3 Millionen Codes verwendet

Es gibt eine Frage zum Passwortschutz, die ich mehrmals im Jahr im Support von Passster erhalte. Ist es möglich, für eine große Anzahl von Passwörtern einen Passwortschutz zu verwenden? Wie viele Passwörter sind zu viele?

Nachdem ich eine imaginäre Obergrenze auf 10.000 Codes gesetzt habe, fragen die Leute immer noch nach immer mehr Codes, die als Passwortschutz verarbeitet werden sollen.

Letzten Monat erhielt ich ein neues Kundenprojekt mit einer verrückten Anforderung.

Wir wollen den Passster-Passwortschutz für unseren Relaunch verwenden, aber wir müssen sicherstellen, dass wir über eine Million Codes unter einer Sekunde verifizieren können.

Der Auftraggeber

Oh, nein. Ich dachte zunächst, das könnte nicht klappen. Wie auch immer, ich mag herausfordernde Aufgaben und Projekte, also nahm ich mich der Aufgabe an und begann mit dem Brainstorming für den erweiterten Passwortschutz.

Sollte das innerhalb von WordPress gehandhabt werden? Nein, ganz klares Nein. Dies wird ein Passwortschutz mit einer großen Menge an Datenverkehr sein und jede zusätzliche Server-Spitze zum Hashing, Verifizieren und Speichern von Codes könnte sich negativ darauf auswirken.

Microservices / API

Einige von euch, die einen Entwickler-Hintergrund haben, kennen vielleicht den Begriff „microservice architecture“. Ich bin ein wirklich großer Fan des Konzepts. Ein einzelner Dienst erledigt eine Aufgabe ziemlich gut und hat eine API, um sich mit anderen zu verbinden. Saubere Architektur, leichteres Debugging, massive Leistungsverbesserungen … Sie haben es verstanden.

Microservices sind eine Software-Entwicklungstechnik, eine Variante des strukturellen Stils der serviceorientierten Architektur (SOA), die eine Anwendung als eine Sammlung lose gekoppelter Dienste anordnet.[1] In einer Architektur für Microservices sind die Dienste feinkörnig und die Protokolle leichtgewichtig.

Wikipedia

Lumen

Abgesehen davon, dass ich (offensichtlich) ein Fan von WordPress und WooCommerce bin, bin ich wirklich begeistert von Laravel und ihrem ganzen Ökosystem. Laravel macht PHP schick, modern und skalierbar. Es hat großartige Konzepte wie die Konsolenbefehle von Symfony, ihre Cache-Lösung und (vielleicht das Beste von allen) ihre Datenbankabfrage-Lösung Eloquent (besonders im Vergleich zu $wpdb …).

Nun handelt es sich aber um einen Microservice – ist Laravel nicht ein bisschen zu groß für diese Aufgabe? Ja, das stimmt. Deshalb habe ich mich stattdessen für Lumen entschieden. Lumen ist ein auf Laravel basierendes Microframework mit gerade genug Funktionen, um die Aufgabe zu erfüllen.

Passwortschutz mit Lumen

Blitzschnelle Micro-Services und APIs, die mit der Eleganz geliefert werden, die ihr erwartet.

Laravel Lumen

Wie ihr Lumen einrichtet

Ich gehe davon aus, dass ihr wisst, wie ihr euer Terminal benutzen könnt und wie ihr den Composer als Dependency Manager einsetzen könnt. Wenn nicht, fangt hier an.

Öffnet das Terminal, navigiert zu eurem Projektordner und erstellt die Lumen-App

php composer create-project — prefer-dist laravel/lumen passster-storage

Dadurch wird Lumen mit allen erforderlichen Abhängigkeiten in einem Ordner namens passster-storage installiert.

.env

Nun wollen wir die env-Datei modifizieren. Benennt zunächst die Datei von .env.example in .env um. Öffnet dann die Datei und gebt die erforderlichen Anmeldedaten wie Datenbank-Host, Benutzer und Kennwort, Anwendungs-URL und Caching (falls verwendet) ein.

Datenbank und Migrationen

In einem ersten Schritt erstellen wir eine Migration, um unsere Datenbanktabellen einzurichten. Zum Glück ist das mit Artisan einfach zu bewerkstelligen, verwendet einfach den folgenden Befehl im Terminal.

php artisan make:migration create_codes_table

Dadurch wird eine neue Migrationsdatei für euch erstellt. Ihr findet sie in der Datenbank/den Migrationen. Ändert sie so, dass sie unseren Anforderungen entspricht:

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCodesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('codes', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('code');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('codes');
    }
}

Danach können wir einfach das Artisan-Kommando in unserem Terminal ausführen, um es zu migrieren.

php artisan migrate

Code-Modell

Wir haben unsere Migration gemacht, jetzt ist es an der Zeit, unser Modell zu erschaffen. Erstellt in eurem Ordner /sites/patrickposner.dev/files eine neue Datei namens Code.php.

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Code extends Model {
	/**
	 * The attributes that are mass assignable.
	 *
	 * @var array
	 */
	protected $fillable = [ 'code' ];
	/**
	 * The attributes excluded from the model's JSON form.
	 *
	 * @var array
	 */
	protected $hidden = [];
}

bootstrap.php

Da wir Lumen anstelle von Laravel verwenden, müssen wir für unser Projekt einige Funktionen wie z.B. Eloquent aktivieren. Öffnet dazu die Datei in app/bootstrap.php und unkommentiert die folgenden zwei Zeilen:

//$app->withFacades();
//$app->withEloquent();

Controller

Der nächste Teil unseres Puzzles ist der Controller. Navigiert zu app/http/controller und erstellt eine Datei namens CodeController.php mit folgendem Inhalt:

<?php
namespace App\Http\Controllers;
use App\Code;
use Illuminate\Http\Request;
class CodeController extends Controller {
	/**
	 * Create a new controller instance.
	 *
	 * @return void
	 */
	public function index() {
		$codes = Code::all();
		return response()->json( $codes );
	}
	public function create( Request $request ) {
		$code       = new Code;
		$code->code = $request->code;
		$code->save();
		return response()->json( $code );
	}
	public function show( $code ) {
		$code = Code::where( 'code', $code )->get();
		return response()->json( $code );
	}
	public function update( Request $request, $code ) {
		$code             = Code::where( 'code', $code )->first();
		$code->updated_at = new \DateTime();
		$code->save();
		return response()->json( $code );
	}
	public function destroy( $code ) {
		$code = Code::where( 'code', $code )->get()->each->delete();
		return response()->json( 'code removed successfully' );
	}
}

Da es meist einfache CRUD-Methoden gibt, möchte ich das nicht weiter erläutern. Was euch auffallen wird, ist die sehr angenehme Syntax, die von Laravels Eloquent herrührt.

Routes

Der letzte fehlende Teil unserer Lumen-App sind die Routes. Öffnet die Datei web.php in /routes und fügt den folgenden Inhalt dort ein:


<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/
$router->group( ['prefix'=>'api/v1'], function() use( $router ) {
	$router->get( '/codes', '[email protected]' );
	$router->post( '/code', '[email protected]' );
	$router->get( '/code/{code}', '[email protected]' );
	$router->put( '/code/{code}', '[email protected]' );
	$router->delete( '/code/{code}', '[email protected]' );
});

Das ist vorerst alles für unsere Lumen-App. Bitte beachtet, dass dieses Tutorial tendenziell so minimal wie möglich gehalten wird. Es wird dringend empfohlen, jede Art von Authentifizierungs- und Sicherheitslayers zu verwenden, um die Routes vor unbefugtem Zugriff zu schützen. Lasst es uns vorerst einfach halten und mit dem WordPress-Plugin-Teil fortsetzen.

Connector-Plugin für den Passwortschutz mit Passster

Ich habe ein Connector-Plugin erstellt, das eine Verbindung zur Lumen-API herstellt und die Haken innerhalb des Passster-Passwortschutz-Plugins verwendet, um den zusätzlichen Parameter zum Verifizierungsprozess hinzuzufügen.

Das Plugin hat im Wesentlichen zwei Funktionen, eine zur Kommunikation mit der API und die zweite zum Freischalten des Inhalts innerhalb des Passster-Passwortschutz-Plugins.

/**
* Check if code exists in code storage.
*
* @param string $code code to check.
* @return bool
*/
function passster_code_exists( $code ) {
    global $wp_version;
    // link to your lumen service API
    $url = 'https://example.com/api/v1/' . $code;

    $args = array(
	'timeout'     => 30,
	'redirection' => 5,
	'httpversion' => '1.0',
	'user-agent'  => 'WordPress/' . $wp_version . '; ' . home_url(),
	'blocking'    => true,
	'headers'     => array(
			  'cache-control: no-cache',
			),
	);
		
     $response = wp_remote_get( $url, $args );
     if ( ! is_wp_error( $response ) ) {
	$body = json_decode( $response['body'] );
	if ( ! empty( $body ) ) {
	    return true;
	}
    } else {
	$error_string = $result->get_error_message();
	echo '<div id="message" class="error"><p>' . esc_html( $error_string ) . '</p></div>';
   }
	return false;
}

Diese Methode prüft, ob der angegebene Code in eurem Datenspeicher existiert. Ihr müsst die Lumen-Service-URL hinzufügen, die Verbindung wird über die HTTP-API von WordPress hergestellt, und wenn der Code existiert, wird true zurückgegeben, andernfalls false.

Der letzte Schritt ist das Einbinden in die korrekte Aktion innerhalb des Passster-Passwortschutz-Plugins:

add_filter( 'passster_api_validation', 'passster_check_code' );

/**
* Check code via REST API
*
* @param bool $status is code in storage.
* @return bool
*/
function passster_check_code( $code ) {
   $status     = false;
   $code_valid = passster_code_exists( $code );

   if ( true === $code_valid ) {
     $status = true;
   }
   return $status;
}

Das ist im Grunde alles, was man braucht, um einen Microservice aufzubauen, der sich mit dem Passster-Passwortschutz-Plugin verbinden und eine große Anzahl von Codes in einer Sekunde oder weniger verifizieren kann. Diese Umsetzung dieses erweiterten Passwortschutzes war eine echte Herausforderung. Vielleicht ist es für den einen oder anderen aber nützlich.