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

 

← PreviousNext →Cluster mal anders...

4 comments

db 08/10/2003

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

agent m 09/10/2003

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 13/11/2003

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 14/11/2003

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.