String - Wörter finden, welches Punkt und entsprechender Pre / Suffix hat?

beta20

Top Contributor
Hallo,

wie kann ich aus einem String alle "Wörter" bekommen, die ein "." haben + ein entsprechendes Zeichen am Anfang oder Ende...

Ich möchte herausfinden, ob es Wörter in einem String gibt, die davor kein bestimmtes Zeichen haben
- Möchliche Zeichen davor: [ oder ?
- Mögliche Zeichen danach ] oder ?
-> Die Liste könnte ggf. noch erweitert werden....

Beispiel:
Java:
SELECT [MyTableLanguage.name]
FROM [MyTable]  AS MyTable
    INNER JOIN [MyTableLanguage AS] MyTableLanguage
    ON [MyTable.id = [MyTableLanguage.mytable.id]
WHERE [MyTableLanguage.name] = 'Test' and MyTable.id = 3

Im unteren Beispiel benötige ich das, also eine Liste an Strings, die dem entsprechen (Trenner durch Punkt und KEIN entsprechendes Zeichen davor oder danach)
MyTable.id = 3

Vielen Dank
 
Also generell ist es ungeschickt, nach Zeichen in einem String (oder am Ende) zu suchen, da dann die Optimierungen des Servers nicht greifen können und er dann tatsächlich alle Elemente des Feldes anpacken muss.

Aber mit Hilfe des like Operators kannst Du natürlich Dinge abprüfen:
WHERE MyTableLanguage.name not like '%]' wäre z.B. der Ausschluss vom ] am Ende.
WHERE MyTableLanguage.name like '_%._%' wäre z.B. ein . im Text (mit mindestens einem Zeichen davor / danach).

Das kann man auch noch zusammen fassen - das Zeichen am Ende darf kein ] sein: [^\]]

Also evtl. sowas wie
[^\[]%.%[^\]]

Aber da kannst Du ja noch frei herum probieren...

Edit: Ein Link (gibt aber sehr viel zu dem Thema): https://www.sqlshack.com/overview-of-the-sql-like-operator/
 
Ich weiß nicht, ob meine Frage verstanden wurde. Es geht nicht um LIKE Operatoren o.ä.

Ich suche aus dem String oben (also das SQL) die Wörter, die:
- Einen Punkt haben (also: MyTable.id)
- Und kein entsprechender Prefix oder Suffix

-------------------------------------------------------------------

Generell was ich mache: Ich habe ein "Modell" gebaut, das drei verschiedene Schichten hat:
- Datenbank Schicht (Referenz zur Datenbank)
- Modell Schicht (Hier geschieht das Joinen)
- Business Schicht (was dem User angezeigt wird)

Zur Laufzeit werden dann die Namen aus der Business View ersetzt mit den Werten aus der Datenbank Schicht.

Business View (Was der User sich gebaut / erzeugt)
Java:
SELECT [BusinessView.MyTableLanguage.name]
FROM [BusinessView.MyTable]  AS MyTable
    INNER JOIN [BusinessView.MyTableLanguage AS] MyTableLanguage
    ON [BusinessView.MyTable.id = [BusinessView.MyTableLanguage.mytable.id]
WHERE [BusinessView.MyTableLanguage.name] = 'Test' and MyTable.id = 3

Die Objekte in dem Modell werden durch verschiedene Referenzen im Modell gesucht und entsprechend ersetzt, sodass der Tabellenname gefunden wird. Das funktioniert auch alles ganz gut.

Datenbank View (SQL, was auf der DB abgesetzt wird)
Java:
SELECT MyTableLanguage.name
FROM MyTable  AS MyTable
    INNER JOIN MyTableLanguage AS MyTableLanguage
    ON MyTable.id = MyTableLanguage.mytable.id
WHERE MyTableLanguage.name = 'Test' and MyTable.id = 3

Der User kann ebenfalls Filter erstellen (siehe WHERE [BusinessView.MyTableLanguage.name] = 'Test' and MyTable.id = 3).
Das Problem ist nun aber, wenn der User meine Datenbankstruktur kennt, dann würde er hiermit entsprechend auf die Tabelle gehen, was ich vermeiden will.
MyTable.id = 3

Dies möchte ich eben verhindern, sodass ich das vor der SQL Generierung (Zugriff auf Modell bzw. der Datenbank View) abfange, und eben nur Felder zulässig sind, die mit [ oder ? anfangen bzw. ] oder ? enden...
 
Der Nutzer gibt seine Abfragen in dieser SQL-artigen Syntax an und du erstellst dann mit String-Ersetzungen das SQL-Statement daraus?
 
Der Nutzer gibt seine Abfragen in dieser SQL-artigen Syntax an und du erstellst dann mit String-Ersetzungen das SQL-Statement daraus?
Ja.... Bzw. der User erstellt die "SQL artige Syntax" nicht unbedingt, sondern wird generiert durch das Hinzufügen von sog. Komponenten (Listen, Charts usw.)... Die "DataQuery" kann er zB anpassen, in dem er Filter etc. hinzufügt...

Siehe bspw. oder auch Cognos Report Studio

Problem ist aber ein String Problem, nicht das SQL 🙂
 
das ist mir durchaus bewusst...
Daher die eigentliche Frage mit der Stringanpassung.

- Felder können nicht abgefragt werden, da mindestens eine Tabelle benötigt werden. Also etwas wie "table.myfield"...
-> Daher meine Eingangsfrage...

- Selbst wenn der User etwas probiert wie: [BusinessView.MyTableLanguage.otherField] würde das nur gehen, wenn dies im sog. Package enthalten ist...
 
das ist mir durchaus bewusst...
Daher die eigentliche Frage mit der Stringanpassung.

- Felder können nicht abgefragt werden, da mindestens eine Tabelle benötigt werden. Also etwas wie "table.myfield"...
-> Daher meine Eingangsfrage...

- Selbst wenn der User etwas probiert wie: [BusinessView.MyTableLanguage.otherField] würde das nur gehen, wenn dies im sog. Package enthalten ist...
falls man das möchte müsste man wie du gesagt hast es mit irgendwelchen Schiebe Kästchen oder Knöpfen oder sonst was gelöst werden dass nur Kombinationen möglich sind die auch unschädlich sind ( könnte ab gewissen grad unübersichtlich werden )
 
Die Eingangsfrage ist nicht so lösbar, das es keinen Weg drum rum gibt. Das Problem ist, man kann nie alle Kombinationen abfangen, die der Parser hinten durchlässt. Mit Sonderzeichen, Unicode und Co kann man so viel Scheiße bauen, dass du nie alle Dinge erschlägst. Sprich, du kannst - sobald dem Benutzer erlaubst selber Text so einzugeben - nicht verhindern, dass er einen Weg an deiner Prüfung vorbeifindet.
 
Um @LimDul zu ergänzen: du kannst das generell schon sicher bekommen (je nachdem, auf was für ein SQL-Subset du dich beschränken kannst), nur eben nicht wenn du rein mit Strings arbeitest (und das ist wirklich absolutes Grundwissen, wenn man mit SQL arbeitet...).

SQL-Injections ist auch nicht das einzige Problem, wenn auch das mit Abstand schlimmste. Z.B. reichst du dann auch den spezifischen SQL-Dialekt an den Nutzer weiter, was man nahezu nie will.



Du hast ja oben noch was editiert...

Bzw. der User erstellt die "SQL artige Syntax" nicht unbedingt, sondern wird generiert durch das Hinzufügen von sog. Komponenten (Listen, Charts usw.)... Die "DataQuery" kann er zB anpassen, in dem er Filter etc. hinzufügt...
Also der Weg ist nicht "SQL vom Nutzer -> richtiges SQL" sondern eher "Nutzereingaben -> irgendwelche Objekte -> komisches SQL -> richtiges SQL"?

Dann verzichte auf den unsinnigen Zwischenschritt, oder nimm als zwischen-repräsentation nicht Strings, sondern sinnvolle Datentypen. Dein ganzes Problem ist doch selbst geschaffen, weil du aus irgendeinem Grund so einen merkwürdigen String generierst. Sinnvoller als deine jetzigen "Model to Text to Text"-Transformationen wären dort "Model to Model to Text"-Transformationen, die sowohl deutlich einfacher als auch deutlich mächtiger sind.
 
Um @LimDul zu ergänzen: du kannst das generell schon sicher bekommen (je nachdem, auf was für ein SQL-Subset du dich beschränken kannst), nur eben nicht wenn du rein mit Strings arbeitest (und das ist wirklich absolutes Grundwissen, wenn man mit SQL arbeitet...).

SQL-Injections ist auch nicht das einzige Problem, wenn auch das mit Abstand schlimmste. Z.B. reichst du dann auch den spezifischen SQL-Dialekt an den Nutzer weiter, was man nahezu nie will.



Du hast ja oben noch was editiert...


Also der Weg ist nicht "SQL vom Nutzer -> richtiges SQL" sondern eher "Nutzereingaben -> irgendwelche Objekte -> komisches SQL -> richtiges SQL"?

Dann verzichte auf den unsinnigen Zwischenschritt, oder nimm als zwischen-repräsentation nicht Strings, sondern sinnvolle Datentypen. Dein ganzes Problem ist doch selbst geschaffen, weil du aus irgendeinem Grund so einen merkwürdigen String generierst. Sinnvoller als deine jetzigen "Model to Text to Text"-Transformationen wären dort "Model to Model to Text"-Transformationen, die sowohl deutlich einfacher als auch deutlich mächtiger sind.

  • Also der User kann nur den Filter anpassen (was Auswirkung auf die Where Condition hat)
  • Im Backend wird ohnehin immer ein "AND user.id = 11234" hinzugefügt. Somit bekommt der User auch wirklich nur seine Daten.
    • Selbst wenn er etwas probiert wie: SELECT myField From MyTable WHERE [BusinessView.Table.Name] = 'TEST' OR 1=1 AND user.id = 11234
      • Das rote Statement kann der User erstellen
      • Der Rest des Statements wird vom Backend generiert

Aber auf meine eigentliche Frage konnte mir noch keiner helfen 🙄
 
Aber auf meine eigentliche Frage konnte mir noch keiner helfen 🙄
Nunja - ich hatte dich leider missverstanden (sorry), aber unter dem Strich reduziert es sich doch jetzt darauf, dass der Benutzer eine Eingabe macht, die geprüft werden soll. Und die kannst du parsen und auswerten. Die Auswertung wird auch nicht einmal sonderlich schwer, so mann das Erlaubte massiv eingrenzt.

Es geht nur um einen Text wie:

[BusinessView.Table.Name] = 'TEST USER' OR 1=1

Und da sieht es dann so aus, dass Du den einfach parsen kannst. Wie man erkennen kann, kann man nicht einfach ein split auf Whitespace machen, daher einfach einmal in Ruhe heran gehen, und schauen, was alles erlaubt ist. Das kann man einmal definieren.

<Einfacher Ausdruck> := <Feld> <Operator> <Wert>

<Wert> := <Feld> | <Konstanter Wert>

<Konstanter Wert> := <String> | <Zahl>

Dein Ausdruck wäre dann:
<Ausdruck> = <Einfacher Ausdruck> { <Verbindungsoperation> <Einfacher Ausdruck> }

Jetzt kannst Du das weiter ausbauen und dann kannst Du einiges erlauben. Warum soll der User immer [BusinessView.TableName.Name] schreiben? Du kannst auch erlauben, dass nur TableName.Name geschrieben wird. Nach dem Parsen kannst Du dann ja daraus das [BusinessView.TableName.Name] bauen.

Du kannst sogar das Parsen so weit treiben, dass ein feld eben immer Tabellenname und Feld hat - da kannst Du dann prüfen, ob es das überhaupt gibt. Dann bekommt ein Anwender eine genaue Fehlemeldung: Die Tabelle x hat kein Feld y oder die Tabelle z existiert nicht.

Aber das ist dann normaler Parserbau würde ich sagen ... oder eben wie auch schon angeklungen: Der User gibt Dir etwas, das Du dann erst einmal in einer eigenen Datenstruktur erfasst um daraus dann ein SQL Statement zu bauen. (Da kann man dann sogar mit PreparedStatements arbeiten, d.h. das was Du als KonstanterWert erfasst hast, ist wirklich ein konstanter Wert und wird als Wert übergeben.
 
Und wenn er einfach mit -- abschließt, degradiert er dein Sicherheits-AND zu einem Kommentar.
Nö, dann kommt ein JPA / Hibernate Fehler:
Java:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: - near line 1, column 381

Danke @kneitzel

Das wäre natürlich die perfekteste Lösung, wenn der String quasi überprüft wird mit dem Ausdruck in dem vom User erstellten Filter.
Aber wie kann ich das machen?
Es könnte ja auch sowas geben wie:
IF, ELSE, WHEN, CASE, UPPER.....

Jetzt kannst Du das weiter ausbauen und dann kannst Du einiges erlauben. Warum soll der User immer [BusinessView.TableName.Name] schreiben? Du kannst auch erlauben, dass nur TableName.Name geschrieben wird. Nach dem Parsen kannst Du dann ja daraus das [BusinessView.TableName.Name] bauen.
Der User schreibt das nicht so... Im Prinzip erstellt er ein DataItem, welches dann im Bauch die Referenz auf [BusinessView.TableName.Name] ist. Dies wird dann eben später beim Rendering auf die Datenbank gedrückt.

In seinem Filter verwendet er dann:
[MY_ITEM] = 1
könnte aber eben auch:
[BusinessView.TableName.Name] =1
verwenden

Nunja - ich hatte dich leider missverstanden (sorry), aber unter dem Strich reduziert es sich doch jetzt darauf, dass der Benutzer eine Eingabe macht, die geprüft werden soll. Und die kannst du parsen und auswerten. Die Auswertung wird auch nicht einmal sonderlich schwer, so mann das Erlaubte massiv eingrenzt.
genau - im Prinzip bräuchte ich eine Validierung des Filters.

In diesem Video kann man eigentlich ganz gut sehen, was ich machen will:

Demnach wäre die Frage noch allgemeiner, wie ich sowas erstelle?
 
Nö, dann kommt ein JPA / Hibernate Fehler:
Irgendjemand findet IMMER einen Weg, das zu umgehen.

Java:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: - near line 1, column 381
Der Fehler dürfte nur kommen, wenn du das ganze nicht als native Query, sondern als JPQL/HQL absetzte? Das eröffnet uU noch weitere Probleme.


Aber wie kann ich das machen?
Wie von @kneitzel recht deutlich und von mir mindesten implizit gesagt: Parse den String (oder verzichte gleich auf diese merkwürdige String-Repräsentation).

Wenn du einen sinnvollen AST daraus baust, kannst du relativ einfach beliebige Validierenden umsetzen und auch sicheres SQL daraus bauen.
 
Der Fehler dürfte nur kommen, wenn du das ganze nicht als native Query, sondern als JPQL/HQL absetzte? Das eröffnet uU noch weitere Probleme.
Ja, ist JPQL...

Wie von @kneitzel recht deutlich und von mir mindesten implizit gesagt: Parse den String (oder verzichte gleich auf diese merkwürdige String-Repräsentation).

Wenn du einen sinnvollen AST daraus baust, kannst du relativ einfach beliebige Validierenden umsetzen und auch sicheres SQL daraus bauen.
Wie meinst du das genau mit "String parsen" ? Welche Lib?
Zum Beispiel sowas?
https://github.com/antlr/grammars-v4/tree/master/sql

Wenn du einen sinnvollen AST daraus baust, kannst du relativ einfach beliebige Validierenden umsetzen und auch sicheres SQL daraus bauen.
Wie gehe ich hier denn vor?
 
Mit „Parsen“, „AST“, „Model to Model/Text-Transformation“ hast du doch schon ein paar Stichpunkte, zu denen du dich informieren kannst 😉

Dir jetzt hier Lösungsansätze zu zeigen, ohne das du das minimale Grundwissen dazu hast, bringt nicht viel und ist potentiell eher kontraproduktiv
 
Du kannst das Parsen problemlos selbst machen. Das ist nichts wirklich kompliziertes.

Aber wenn Du willst, kannst Du auch entsprechende Libraries nutzen, die Parser bauen. YACC / Bison sind die beiden ganz bekannten und stark genutzten (nicht Java) Libraries. Es gibt aber auch Java Gegenstücke wir JavaCC.

Die Einarbeitung in so eine Library kann aber deutlich zeitaufwändiger sein als einen eigenen speziellen Parser zu bauen. Zumindest wenn man sich mit Compilerbau noch nicht beschäftigt hat und sich nicht in diesem Bereich massiv einarbeiten will.

Ich würde das in 2 Schritten machen:
a) Tokenizer -> Bau einfach aus dem String Token. Token sind dann die entsprechenden Stücke, also aus
[BusinessView.Table.Name] = 'TEST USER' OR 1=1
machen:
[BusinessView.Table.Name]
=
'TEST USER'
OR
1
=
1
(Jeweils ein Token pro Zeile).

Oder ggf. schon incl. Bedeutung finden:
Tabelle: Table.Name
Operator.GLEICH
Konstante - String: 'TEST USER'
Verbindungsoperator.OR
Konstante - Int: 1
Operator.GLEICH
Konstante - Int: 1

Ob man das als reine Sequenz speichert oder ob man da eine Baumstruktur braucht, das muss man dann sehen. Das hängt auch davon ab, was alles erlaubt sein soll und wie Komplex das werden darf.

b) Prüfen, ob die Token so in der Reihenfolge etwas sind, das erlaubt ist.

c) Generierung von was auch immer - Da bist Du ja komplett offen und das ist unabhängig von den ersten Schritten.

Wichtig ist, dass Du Dir im Detail darüber klar wirst, was alles wie erlaubt ist. Das kannst Du so aufschreiben, wie ich es getan habe. Das kann dann auch eine klare Dokumentation für den Benutzer sein. Der muss es ja auch irgendwie wissen, was Du wie erlauben willst.

Generell ist es eh schon beschränkt - vieles kann der Anwender ja nicht machen. Also Dinge wie Subqueries oder so. Dann ist die Frage, ob der Wert nur konstant sein darf oder auch Felder haben darf. Also etwas wie: tabelle1.name1 = tabelle2.name2

Aber dann auch Operationen ... Where tabelle1.name1 > tabelle2.name2 + tabelle3.name3

Je komplexer Du es machst, desto schwerer und komplexer wird die Umsetzung. Und um so schwerer wird es auch abzusichern sein.
(Und je komplexer es wird, desto mehr machen entsprechende Tools Sinn.)

Ich sage nur Kaseya VSA Angriff. Sowas willst Du vermeiden!

Das einfach noch einmal von meiner Seite. Eine einfache 08/15 Lösung kannst Du auch noch machen - einfach ein paar minimale Tests machen und dann 1:1 weiter geben ... Aber da würde ich mich von der Infrastruktur schon einmal verabschieden ... denn da bist du garantiert offen.
 
Aber wenn Du willst, kannst Du auch entsprechende Libraries nutzen, die Parser bauen. YACC / Bison sind die beiden ganz bekannten und stark genutzten (nicht Java) Libraries. Es gibt aber auch Java Gegenstücke wir JavaCC.
ANTLR dürfte mit das bekannteste sein, da gibt’s auch fertige Grammatiken für SQL, woraus man sich relativ einfach ein passendes Subset ableiten könnte.
 
ANTLR dürfte mit das bekannteste sein, da gibt’s auch fertige Grammatiken für SQL, woraus man sich relativ einfach ein passendes Subset ableiten könnte.
Super, dann gibt es sogar einen guten Tool-Vorschlag - so dies der Weg ist, den Du gehen möchtest. (Ich selbst habe mich nur in meinem Studium damit beschäftigt - und unabhängig von Java. Buchvorschläge unterlasse ich daher auch - schlicht aus Angst, dass die letzten Exemplare des vorgeschlagenen Buches nur noch in Museen zu finden sind 🙂 )
 
Ich bin allerdings auch gar nicht sicher, ob ein Parser der richtige Weg ist.

Das Statement gibt der Nutzer ja nicht als String ein - es liegt also schon in einer (mehr oder weniger) sinnvollen Form vor. Daraus dann einen String zu machen, um den wieder zu parsen, wäre Unsinn.

Wenn aber Strings das Ausgangsformat sind (auch wenn nur in Teilen) führt am Parsen mMn kein Weg vorbei.
 
Das scheint mir in die richtige Richtung für das Vorhaben zu gehen:

Ich werde das mal entsprechend austesten. Vielen Dank für die Ratschläge und Hilfen.
 
Mit einem Parser, der SQL verarbeitet, musst du etwas aufpasse – deine Zwischen-Repräsentation ist ja nicht zwingend valides SQL, ein Angreifer würde aber valides SQL liefern.
 
Mit einem Parser, der SQL verarbeitet, musst du etwas aufpasse – deine Zwischen-Repräsentation ist ja nicht zwingend valides SQL, ein Angreifer würde aber valides SQL liefern.
was wäre wenn man zb einem Button einen Final String hinterlegt "FROM database1"
und dann halt 10 Buttons hat mit dem der User dann den SQL Befehl zusammen bauen kann in dem die BUttons gedrückt werden.. wäre das möglich sinnvoll umzusetzen
 
was wäre wenn man zb einem Button einen Final String hinterlegt "FROM database1"
und dann halt 10 Buttons hat mit dem der User dann den SQL Befehl zusammen bauen kann in dem die BUttons gedrückt werden.. wäre das möglich sinnvoll umzusetzen
Warum wollen denn immer alle Strings benutzen? 😀

Wenn man das eh über Buttons „eingeben“ lässt, kann man auch direkt sinnvolle Datentypen nutzen.
 
Warum wollen denn immer alle Strings benutzen? 😀

Wenn man das eh über Buttons „eingeben“ lässt, kann man auch direkt sinnvolle Datentypen nutzen.
wie meinst du das ?
zb auf klick des Buttons sollte ein FROM xyz hinzugefügt werden

wie würde man das Theoretisch gebacken kriegen.. will ja was lernen
 
wie meinst du das ?
Du baust dir im wesentlichen ein AST auf:

Code:
      SELECT
      /    \
     /      \
COLUMNS     FROM
   |          |
  FOO        xyz


Wenn man jetzt noch ein "JOIN bar ON a=b" einfügt:
Code:
      SELECT
      /    \
     /      \
COLUMNS     FROM
   |          |
  foo        JOIN
            /  | \
           /   |  \
          xyz a=b  bar



Ist grad nur auf die Schnelle und ohne viel Nachdenken hingekritzelt, irgendwo findet man bestimmt vernünftige Modelle für SQL.
Mit einem AST kannst du im Gegensatz zu einem String dann sinnvoll weiterarbeiten.

Benutzte Tabellen erkennen? Einfach die Passenden "Table"-Knoten finden.
Variablen finden und extrahieren, damit man Prepared Statements nutzen kann und abgesichert gegen Injection ist? Bei der Transformation zu Text einfach jeden "Variable"-Node durch ein "?" darstellen und alle zugehörigen Werte an das Prepared Statement übergeben.
Jeden JOIN um irgendwelche merkwürdigen Bedingungen (z.B. um unerlaubte Zugriffe zu verhindern) ergänzen? Den passenden Node suchen und erweitern/ersetzen.
 

Zurück
Oben