Verschlüsselung von URL-Variablen

by marcus on 08/10/2003



Eines der grundlegendsten Probleme, welches alle serverseitigen Programmiersprachen
gemeinsam haben, ist die "ab Werk" eingebaute Anfälligkeit gegenüber
URL-Hacking.

Das klassische Beispiel ist wohl das folgende:

<cfquery name="getData" datasource="#request.dsn#">
	select something 
	from theTable
	where ID = #url.ID#
</cfquery> 

Was nun geschieht, wenn man die Seite mit page.cfm?id=5;drop%20database aufruft,
kann sich jeder ausmalen (Voraussetzung ist natürlich eine schlecht aufgesetzte
Datenbank)…

Man kann (und sollte!) hier natürlich zu CFQUERYPARAM und generellen
Plausibilitätsabfragen
mit CFIF greifen, allerdings ist dies meiner Meinung nach nur die halbe
Miete. Deutlich sicherer fährt
man, wenn die zu übergebenden URL-Variablen verschlüsselt und somit
weitmöglichst
gegen Manipulation geschützt werden. 100%ige Sicherheit gibt’s natürlich
nie, aber man sollte es den bösen Buben so schwer wie möglich machen.
🙂

Das Prinzip

Grundlage des Systems, welches ich mir ausgedacht habe, ist der Custom Tag
cf_cryp von Jackson Moore. Prinzipiell wollte ich erreichen, dass man
sowohl verschlüsselte, als auch normale URL-Variablen übergeben kann,
ohne sich darum kümmern zu müssen, ob nun eine Entschlüsselung nötig
ist oder nicht. Zudem wollte ich erreichen, dass man als Programmierer
keine großen Umstellungen einplanen muss, also ganz normal mit URL-Variablen
weiterarbeiten kann.

Coding

Die Ideale Stelle für eine automatische Entschlüsselung eines verschlüsselten
Querystrings ist natürlich die Application.cfm. Ich nutze hierzu folgenden
Code:

<!--- encryptkey setzen --->
<cfset request.encryptkey="Blog_in_Black">

<!--- wenn ein querystring oder form.values vorhanden ist, string entschlüsseln --->
<cfif (len(cgi.query_string) and not find('=', cgi.query_string)) or isdefined("form.values")>
	
	<cfscript>
		//je nach situation string und scope setzen
		if(len(cgi.query_string)) {
			decodeme=cgi.query_string;
			scope="url";
		}
		else {
			decodeme=form.values;
			scope="form";
		}
	</cfscript>

	<!--- string entschlüsseln --->
	<cf_cryp type="de" string="#decodeme#" key="#request.encryptkey#">

	<!--- aus dem entschlüsselten string wieder variablen bilden --->
	<cfloop list="#cryp.value#" delimiters="&" index="idx"> 
		 <cfset name=left(idx,FindNoCase("=",idx)-1)>
		 <cfset wert=right(idx,len(idx)-FindNoCase("=",idx))>
		
		<cfset "#scope#.#name#" = wert>
	<!--- für's debugging <cfoutput>#scope#.#name# = #wert#<br></cfoutput>  --->
	</cfloop>

</cfif>

Wie man sieht, geht der Code noch einen Schritt weiter und entschlüsselt
bei Bedarf auch den Inhalt von form.values, also ist auch eine verschlüsselte
Übergabe von Formularwerten möglich.
Damit das System in dieser Form funktioniert, musste ich an cf_cryp eine
kleine Anpassung in Zeile ~89 vornehmen:

<cfif findnocase("J",string,1) is 0>
	<!--- <cfthrow type="cryp" message="nochecksum"> --->
	<!---marcus raphelt <cfml@raphelt.de>: added cfexit to suppress exceptions in cfmx--->
	<cfexit method="EXITTAG">
<cfelse>

Der Kommentar sagt’s bereits: unter CFMX gibt cf_cryp eine Exception aus,
wenn der übergebene String nicht dekodierbar ist. Dies wird zwar mit
dem not find(‘=’, cgi.query_string)weitestgehend
abgefangen, aber ich will’s nicht darauf anlegen, denn es könnte auch
z.B. ein Aufruf wie page.cfm?myVar
stattfinden.

Was geschieht nun in der Application.cfm?

Ich gehe hier nur auf die URL-Variablen ein – beim Form-Scope ist es
das gleiche, nur dass eben ein anderer Wert für scope gesetzt
wird. Es wird nachgesehen, ob ein Query-String übergeben wurde,
und ob er kein Gleichheitszeichen enthält (das ist bei mit cf_cryp
kodierten Strings nicht möglich). Ist dies der Fall, so wird
der Query-String an cf_cryp zur Entschlüsselung übergeben.
Zurückgegeben wird dann der "echte" Querystring, z.B. itemID=300&viewmode=full&bgcolor=white,
welcher dann über eine Schleife in Variablen des gewünschten
Scopes, in diesem Fall URL, umgewandelt wird. Das bedeutet gleichzeitig,
dass man im Zieltemplate wieder URL-Variablen zur
Verfügung hat und quasi normal weiterprogrammieren kann.

Übergabe

Den zu übergebenen Wert muss man vorher zusammenbauen und verschlüsselt übergeben.
Hier ein kleines Beispiel:

<!--- zu übergebenden querystring zusammenstellen --->
<cfset theQueryString = "itemID=300&viewmode=full&bgcolor=white">

<!--- querystring verschlüsseln --->
<cfmodule 
	template="cryp.cfm" 
	type="en" 
	string="#theQueryString#"
	key="#request.encryptKey#"
>

<cfoutput>
<a href="index2.cfm?#theQueryString#">Normale Übergabe...</a><br>
<a href="index2.cfm?#cryp.value#">Verschlüsselte Übergabe...</a></cfoutput>

Ergebnisseite

Auf der Ergebnisseite reicht ein Dump des URL-Scopes. Sorry an alle 4.x-User,
aber ich war zu faul, eine Loop zu nutzen. 😉

<cfdump var="#url#">

Erweiterbarkeit

Das System lässt sich natürlich in alle Richtungen erweitern. So könnte man
z.B. verschlüsselte Werte in Cookies schreiben oder Variablen, die im
Server-Scope liegen, vor unbefugten Blicken schützen (praktisch bei Virtuellen
Servern).

Nachteile

Ganz klar, durch die Ver-/Entschlüsselung hat man deutliche Performance-Einbußen.
Diese sind aber, wie ich finde, wegen der gewonnenen Sicherheit zu vernachlässigen.

Download

Natüüürlich – Abtippen wäre unnötiger Sport. Daher habe ich ein kleines ZIP-Archiv (5521 Bytes)
erstellt, in dem alle nötigen Dateien enthalten sind. Viel Spaß beim
Testen!

Feedback ist erwünscht! 🙂
agent m, 07.10.2003

 

db October 8, 2003 at 12:00 am

Koennen auch cfid & cftoken verschluesselt uebergeben werden? Soviel mir bekannt ist,
koennen diese nicht manuell gesetzt werden …

agent m October 9, 2003 at 12:00 am

Hi,

das habe ich noch nicht getestet. Dieses System habe ich mal für eine Applikation entworfen, die für cfid und cftoken Cookies genutzt hat.

bye,
marcus

Carola November 13, 2003 at 12:00 am

Hi,

die Anfälligkeit der Url Parameter besteht ja eigentlich nur bei where etwas=#url.id# (ohne Hochkomma), oder?
Als schnelle Hilfe hab ich meine Abfragen mit where etwas=#val(url.id)# modifiziert.
Was meint Ihr?

Grüßchen
Carola

agent m November 14, 2003 at 12:00 am

Hi Carola,

ja, val() ist hierbei eine schnelle Lösung. Sicherer ist cfqueryparam, noch schöner für den User z.B., wenn Du vorher auf isNumeric() prüfst und dann im Bedarfsfall eine Fehlermeldung ausgibst. Das ist allerdings gänzlich Geschmackssache 🙂

Das Verschlüsselungssystem ist eine gute Möglichkeit, es gar nicht erst zu URL-Manipulationen kommen zu lassen.

Comments on this entry are closed.

Previous post:

Next post: