• Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!

[GELOEST] Javascript und HTML "Chat"

PlatinSecurity

Neues Mitglied
Moinsen,

ich habe vor, mit nodeJS die Twitch API ab zu rufen und den Chat an ein DIV zu übergeben.
Problem hierbei ist, dass man im Twitch Chat einfach ein typischen HTML kram rein schreiben kann und dies mein Tool komplett hops nimmt.

Wie unterbinde ich am besten den Input von anderen HTML-Tags. Ich habe es schon mit escape() versucht, ist aber irgendwie nicht die schönste Lösung, da ich auch mit HTML-Tags arbeite. Ich will die Emotes ja bspw. richtig anzeigen lassen, weswegen ich ein IMG-Tag verwende. Desweiteren haben ich auch das klassische "Alles versuchen raus zu filtern mit replaceAll()" versucht. Auch keine schöne Lösung.

Wäre lieb wenn ihr mir da helfen könntet.

LG
 
Werbung:
Du solltest einen sogenannten Sanitizer dafür verwenden, um Dinge wie Code Injection, bzw. XSS zu verhindern.

Die gängigen Front-End-Frameworks machen das automatisch für dich. Falls du keins benutzt, gibt es diverse Module auf npm, die das vermutlich 1000x besser machen als Du oder Ich. Das ist nämlich ein ziemlich komplexes Thema.
 
Kannst du mir für meine nodeJS Twitch API Chat Ansicht ein "Sanitizer" empfehlen?
Ich habe ehrlich gesagt auch noch nie was von sowas gehört.

EDIT: Und wird das meine Arbeit nicht beeinträchtigen? Also wenn ich einem Emoji noch ein IMG Tag geben will?
 
Werbung:
Ich persönlich habe noch keine Erfahrung mit den Sanitizern, die es so auf npm gibt gemacht, da ich ohnehin nur mit Front-End-Frameworks arbeite.

Basierend auf der Popularität scheint mir sanitize-html allerdings wie eine gute Wahl.

Und wird das meine Arbeit nicht beeinträchtigen? Also wenn ich einem Emoji noch ein IMG Tag geben will?
Nicht, wenn du es richtig machst. Da es um den Twitch-Chat geht, werden meines Wissens bestimmte Wörter wie z. B. "KEKW" durch entsprechende Bilder ersetzt. Diese Wörter sollten also nicht durch den Sanitizer verändert werden.

Sprich, du sanitized deine Chat-Nachrichten erst, dann ersetzt Du die entsprechenden Wörter durch <img />.

Javascript:
let message = "Hello KEKW world! <a>My HTML Code</a>";

sanitizedMessage = sanitizeHtml(message);

// sanitizedMessage should now look like:
// "Hello KEKW world! &#60;div&#62;My HTML Code&#60;/div&#62;"

emojiMessage = sanitizedMessage.replace("KEKW", "<img src='https://thumbs.dreamstime.com/b/emoji-smiley-face-yellow-emoji-face-smile-isolated-white-158698752.jpg' height='20' />");

messageP = document.createElement("p");

messageP.innerHTML = emojiMessage;

&#60;div&#62;My HTML Code&#60;/div&#62; wird dabei im Browser als <div>...</div> angezeigt, aber im String wird es entsprechend encoded.
 
Zuletzt bearbeitet:
Moin, also irgendwie klappt das bei mir nicht. Ich lade die JS von npm immer mit
Code:
<script type="text/javascript" src="js/index.js"></script>
bspw. Bei diesem npm Projekt kommen dann nur Fehler, wie "das wurde schon definiert" oder "das wurde noch nicht definiert" usw. Die dazugehörigen Dependencies habe ich natürlich auch geladen.

Habe daher nach einer alternativen gesucht und das hier gefunden:

Funktioniert lustigerweise auch nicht so, wie ich es brauche, da dieses github projekt fast gar nichts Sanitizert außer
Code:
<iframe>
.... zumindest war das bei meinen Test aktuell das einzige was verändert wurde. Der rest, wurde noch als
Code:
<a href="">
oder
Code:
<h2>
oder eben als
Code:
<br>
angezeigt.

Und wenn ich
oder
nutze, dann kommt entweder dieser Fehler
Code:
Uncaught TypeError: sanitizer.sanitizeFor is not a function
oder dieser
Code:
Uncaught TypeError: sanitizer.sanitize is not a function
.

Wie kann ich denn ein npm Projekt nutzen ohne es mit npm install zu installieren?
Ist ein einfacher Webserver mit FTP. Mehr kann ich da nämlich nicht machen.
 
Werbung:
Ich muss noch mal kurz etwas hier einwerfen...

Ich habe das Problem soweit gelöst, dass die TAGs ignoriert werden mit diesem einfachen Code:
Javascript:
const unsafe = data.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
return unsafe;
Nur wie du schon sagtest @Aaron3219 ... Man sollte den replace in der richtigen Reihenfolge machen.
An sich mach ich das auch. Nur übergibt die Twitch API ja auch emote infos. Und darin steht die ID des Emotes und die Position von - bis.

Wenn natürlich der Text jetzt zuerst replacet wird, ist die Position der eigentlichen Emotes nicht mehr an der Stelle, wo die API mir das sagt.
 
bspw. Bei diesem npm Projekt kommen dann nur Fehler, wie "das wurde schon definiert" oder "das wurde noch nicht definiert" usw. Die dazugehörigen Dependencies habe ich natürlich auch geladen.
Diesen Ansatz solltest du vergessen. Du meintest in deinem Beitrag, dass du mit NodeJS arbeitest. Ich bin deshalb davon ausgegangen, dass du ein Backend-Server und ein Frontend hast. Gehe bitte ein wenig genauer darauf ein, was genau der Aufbau deiner Applikation ist.

Was die Fehlermeldungen "Uncaught TypeError: sanitizer.sanitizeFor is not a function" angeht, funktioniert es vermutlich nicht, weil es sich um experimental feature handelt und auch in gängigen Browsern oft noch nicht implementiert ist.

Wie kann ich denn ein npm Projekt nutzen ohne es mit npm install zu installieren?
Ist ein einfacher Webserver mit FTP. Mehr kann ich da nämlich nicht machen.
Das ist zwar theoretisch möglich, rate ich allerdings dringend von ab. Ich empfehle dir, npm zu nutzen. Dass du nur einen einfachen FTP-Server hast, ist kein Problem, denn normalerweise würdest du deine Website "bauen" lassen. Das Produkt daraus kannst du ganz normal auf deinem FTP-Server hochladen.

Noch ein paar Kommentare zu deinem workaround:
Javascript:
const unsafe = data.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
return unsafe;
Simpel und einfach, aber nicht unbedingt sicher. Es kommt immer darauf an, was du genau machst und wie du es genau machst. Auch deshalb solltest du genauer auf dein Vorhaben und den Aufbau deiner Applikation eingehen.

Wird zum Beispiel alles an einen Backend-Server geschickt, musst du erstmal darauf achten, auch alles im Backend zu sanitizen. Ansonsten ist deine Anwendung über Dinge wie SQL-Injection o.ä. angreifbar. In diesem Fall reicht dein Workaround nicht mehr.

Falls das alles noch ein wenig fortgeschritten ist, ist es durchaus in Ordnung, zum Üben deinen Workaround zu nutzen. Die Seite allerdings öffentlich zugänglich zu machen, halte ich für problematisch.
Wenn du einfach nur Chatnachrichten in einem Browserfenster anzeigst, ohne Backend-Server o.ä., sollte es wohl reichen.
Nur wie du schon sagtest @Aaron3219 ... Man sollte den replace in der richtigen Reihenfolge machen.
An sich mach ich das auch. Nur übergibt die Twitch API ja auch emote infos. Und darin steht die ID des Emotes und die Position von - bis.

Wenn natürlich der Text jetzt zuerst replacet wird, ist die Position der eigentlichen Emotes nicht mehr an der Stelle, wo die API mir das sagt.
Ich bräuchte mal eine Beispielantwort der Twitch-API, um mir das besser vorstellen zu können.
 
Hey, das ist super lieb von dir, dass du dir so viel mühe machst mir so ausführlich zu antworten. Dafür schon einmal ganz lieben dank.

Gehe bitte ein wenig genauer darauf ein, was genau der Aufbau deiner Applikation ist.
Du hast recht, ich hätte sagen sollen, dass ich das via Javascript im Browser mache. Ich hatte einfach nur im Kopf das dass, was ich da gemacht hab, was mit NodeJS zu tun hatte, weil ich eben schon mal ein einfachen Chat Bot gemacht hab über ein Root Server.

Wird zum Beispiel alles an einen Backend-Server geschickt, ...
Ich hätte einen kompletten Root Server, der NPM und alles kann.
Wenn ich darauf jetzt den NodeJS kram mache, wie sende ich von meinem Browser via Javascript den die info ... "hey ich brauch von dir alles was du empfängst an Daten" ...?

Und wie sendet der Bot auf meinem Root dann die Infos zurück an meine Browser Anwendung?

Ich bräuchte mal eine Beispielantwort der Twitch-API, um mir das besser vorstellen zu können.
So eine Antwort von der Twitch API kann unterschiedlich aussehen. Gehen wir aber mal von einer einfachen Chat-Nachricht aus, dann sieht es so hier aus:

Event:
Javascript:
client.on("message", (channel, tags, message, self) => {

 //Hier der Code, was halt passieren soll...

});

Output von console.log(channel, tags, message, self);

channel:
Code:
#biergarten100

tags:
JSON:
{
    "badge-info": null,
    "badges": {
        "broadcaster": "1",
        "glitchcon2020": "1"
    },
    "client-nonce": "a24f7ec900e6edd0768f07a19a018ae9",
    "color": "#FF4500",
    "display-name": "BierGarten100",
    "emotes": {
        "555555560": [
            "17-18",
            "73-74"
        ]
    },
    "first-msg": false,
    "flags": null,
    "id": "dfec0e89-3d23-4ed3-acb3-5dfe3cdc3190",
    "mod": false,
    "returning-chatter": false,
    "room-id": "40590923",
    "subscriber": false,
    "tmi-sent-ts": "1671643878296",
    "turbo": false,
    "user-id": "40590923",
    "user-type": null,
    "emotes-raw": "555555560:17-18,73-74",
    "badge-info-raw": null,
    "badges-raw": "broadcaster/1,glitchcon2020/1",
    "username": "biergarten100",
    "message-type": "chat"
}

message:
Code:
Das ist ein Test :D & <img src="../old/src/vip.png"></img> das ist cool. :D
Hier ist es so, dass wenn jetzt natürlich vor dem ganzen replace der Emotes die Message Sanitizert wird, dann sieht es so aus:
Code:
Das ist ein Test :D & &lt;img src="../old/src/vip.png"&gt;&lt;/img&gt; das ist cool. :D
Hier sieht man ja deutlich, dass mehr Zeichen dazu gekommen sind. Dadurch ist der Teil bei tags.emotes nicht mehr so einfach brauchbar, weil sich die Emotes ja verschoben haben.
Der Output im Browser sieht so aus:
Code:
Das ist ein Test (hier ist so ein kaputtes Bild)tvnw.net/emoticons/v2/555555560/static/dark/1.0' /> & <img src="../old/src/vip.png"></img> das ist cool. :D
Hier bin ich schon die ganze Zeit am rum probieren, weil die Nachrichten ja immer unterschiedlich sind.
Wenn ich Bspw. Die Nachricht sende:
Code:
Das ist ein Test & das ist cool. :D & voll cool & hahaha :P
oder
Code:
Das ist ein Test <img src="../old/src/vip.png"></img> & das ist cool. :D :D
Dann ist die Ausgabe korrekt mit dem Replace von eben :D oder :p.

self:
Code:
false
 
Zuletzt bearbeitet:
Werbung:
Ich hätte einen kompletten Root Server, der NPM und alles kann.
Wenn ich darauf jetzt den NodeJS kram mache, wie sende ich von meinem Browser via Javascript den die info ... "hey ich brauch von dir alles was du empfängst an Daten" ...?

Und wie sendet der Bot auf meinem Root dann die Infos zurück an meine Browser Anwendung?
Dafür würde es verschiedene Wege geben, z. B. einen HTTP-Request, Websockets, etc., darauf genauer einzugehen, würde den Rahmen sprengen. Allerdings scheint es mir so, als wenn du gar kein Backend benötigst, wenn du einfach nur Chatnachrichten anzeigen möchtest.

Dementsprechend solltest du eher Recherche betreiben, wie man mit npm und webpack ein Frontend baut, um es auf einem FTP-Server zu deployen. Natürlich nur, wenn das noch nicht zu fortgeschritten ist.

Falls du das nur zu Lernzwecken machst, beschäftigen wir uns mal mit dem Problem der Emotepositionen:
Tatsächlich ist das gar nicht so einfach. Ein Ansatz wäre, den String an den entsprechenden Emotepositionen aufzuspalten.
Dazu müsstest du:
  1. z. B. mit RegEx die Positionen und Emojiarten aus tags.emotes-raw heraustrennen.
  2. An den herausgetrennten Emotepositionen den String aufspalten. Das Resultat wäre ein Array.
  3. Über das gesamte Array loopen und sanitizen.
  4. Über die Arrayelemente, die die Emotes enthalten, loopen und entsprechend durch Bilder ersetzen.
Schon mal gleich vorweg. Das ist kein leichtes Vorhaben.

Was ich mich aber frage ist, wenn du sowieso als Emoteart nur so codes wie 555555560 bekommst, dann musst du ja irgendwo eine Liste der Codes mit entsprechendem Bild haben.

Warum sanitized du also nicht zuerst und suchst dann nach z.B. :D oder KEKW oder :P und ersetzt es durch Bilder?
Anstatt also zu sagen "bei 555555560 ersetze durch Bild XY", sag doch lieber, "bei Nachricht :P ersetze durch Bild XY"
 
Was ich mich aber frage ist, wenn du sowieso als Emoteart nur so codes wie 555555560 bekommst, dann musst du ja irgendwo eine Liste der Codes mit entsprechendem Bild haben.

Warum sanitized du also nicht zuerst und suchst dann nach z.B. :D oder KEKW oder :P und ersetzt es durch Bilder?
Ich frage den Server von der Twitch API wie folgt ab:
Ist das Emote unter der ID "https://static-cdn.jtvnw.net/emoticons/v2/{id}/animated/dark/1.0" nicht gleich 404 dann ist es ein GIF Emote und wenn es 404 ist dann ist es ein Static Emote und erreichbar unter "https://static-cdn.jtvnw.net/emoticons/v2/{id}/static/dark/1.0". Daher erhalte ich also den erfolgreichen Emote img-tag.

Bei BetterTV Emotes (falls du nicht in der Twitch Thematik drin bist, dass sind noch mal extra emotes außerhalb von Twitch) ist das noch mal ne ganz andere Geschichte:

Da läuft das quasi so ähnlich wie du sagst... Die Liste fragen "gibt es dich" und dann ersetzen.
Diese API "https://api.betterttv.net/3/cached/emotes/global" muss mit curl aufgerufen werden um die Liste zu erhalten.
 
Dann bleiben dir wohl nur die komplizierten Wege. Aber so richtig gefällt mir das alles nicht.

Vielleicht sehe ich den Wald auch vor lauter Bäumen nicht und andere Mitglieder haben noch andere Ansätze?
 
Werbung:
Hey @Aaron3219 ... ich habe das Problem jetzt gelöst und dass mit weniger Code als ich erwartet hätte.
Ich weiß, dass ist zwar nicht der beste Ansatz, aber es löst zumindest aktuell das Problem was ich hab. *lach* Alsoooo...

Ich habe ja ein Array an Emotes und weiß wo die an welcher Position sind.
Wenn ich jetzt die erhaltene Nachricht mit " .split("") " einfach an jedem Zeichen splitte, kann ich einfach mit " for() " jedes Zeichen nach und nach prüfen ob es ein " < " oder ein " > " ist. Außerdem kann ich direkt an Position 1 des Emotes den " <img src='' /> " kram setzen und bis Position 2 die Zeichen einfach alle leeren.

Wenn das alles durchgelaufen ist, wird der String direkt wieder zusammengesetzt.
Dadurch das ein Array mit so einem Inhalt => "" <= ja einfach "nix" ist, ist der String am ende auch die gewünschte Ausgabe.

Jetzt habe ich aktuell aber leider noch ein Problem mit der Sortierung der Reihenfolge der Emotes.
Da die Twitch API nicht in der richtigen Reihenfolge die Positionen ausgibt sondern ehr sowas hier macht:
"1-2",
"10-15",
"4-8",
"88-90"
Habe ich gedacht, ich mache eine Sortierung mit folgendem Code:
Javascript:
emotePos.sort(function(a, b) {
            if (a[a.length - 2] > b[b.length - 2])
                return 1;
            else if (a[a.length - 2] < b[b.length - 2])
                return -1;
            return 0;
        });
Das ganze klappt auch bis Position 99... Ab 100 sortiert er genau so bekloppt wie die Twitch API.
Weil er dann nämlich irgendwie die 00 nicht sieht und nur ne 1 hat oder bei 109 sieht er ne 09 und somit nur ne 9. Also alles ganz komisch. Ich glaube das liegt auch irgendwie daran, dass der die letzten 2 Zeichen sich anguckt und sortiert. Mach ich aber -3 oder -1, was auch immer, dann geht es auch nicht mehr in der richtigen Reihenfolge.

Ich weiß also nicht wie ich es schaffe, einen String im Array der so aussieht bspw. " {id}:{pos1}-{pos2} ", so zu sortieren, dass alles im Array von kleinen nach großen Zahlen sortiert ist.
Es müssen auch nur die letzten 2-3 Zahlen sein.




EDIT: Omg... hat sich erledigt glaub ich. *lach* ... Ich musste das noch mit paseInt() machen. Also so:
Javascript:
emotePos.sort(function(a, b) {
            if (parseInt(a[a.length - 2]) > parseInt(b[b.length - 2]))
                return 1;
            else if (parseInt(a[a.length - 2]) < parseInt(b[b.length - 2]))
                return -1;
            return 0;
        });
 
Zuletzt bearbeitet:
Hey @Aaron3219 ... ich habe das Problem jetzt gelöst und dass mit weniger Code als ich erwartet hätte.
Ich weiß, dass ist zwar nicht der beste Ansatz, aber es löst zumindest aktuell das Problem was ich hab. *lach* Alsoooo...

Ich habe ja ein Array an Emotes und weiß wo die an welcher Position sind.
Wenn ich jetzt die erhaltene Nachricht mit " .split("") " einfach an jedem Zeichen splitte, kann ich einfach mit " for() " jedes Zeichen nach und nach prüfen ob es ein " < " oder ein " > " ist. Außerdem kann ich direkt an Position 1 des Emotes den " <img src='' /> " kram setzen und bis Position 2 die Zeichen einfach alle leeren.

Wenn das alles durchgelaufen ist, wird der String direkt wieder zusammengesetzt.
Dadurch das ein Array mit so einem Inhalt => "" <= ja einfach "nix" ist, ist der String am ende auch die gewünschte Ausgabe.
Wie du schon sagtest, schön ist es nicht, aber die Twitch API macht es einem auch nicht einfach... Herzlichen Glückwunsch zu deiner Lösung.

Jetzt habe ich aktuell aber leider noch ein Problem mit der Sortierung der Reihenfolge der Emotes.
Da die Twitch API nicht in der richtigen Reihenfolge die Positionen ausgibt sondern ehr sowas hier macht:
"1-2",
"10-15",
"4-8",
"88-90"
Habe ich gedacht, ich mache eine Sortierung mit folgendem Code:
Eine Sortierung ist hier schlichtweg nicht notwendig.
Ich weiß nicht genau, wie die API-Antwort aussieht, wenn du mehrere verschiedene Emojis hast, aber angenommen das sieht dann so aus:
Code:
555555560:17-18,73-74;111173885:34-36,12-17

Dann könntest du einfach folgendes machen:
Javascript:
let emotesRaw = '555555560:17-18,73-74;111173885:34-36,12-17';
let emotesProccessed = {};

emotesRaw = emotesRaw.split(";");

emotesRaw.forEach(emoteRaw => {
  emoteRaw = emoteRaw.split(":");
  if (!emotesProccessed[emoteRaw[0]]) {
      emotesProccessed[emoteRaw[0]] = []
  }
  let emojiPositionsRaw = emoteRaw[1].split(",");
 
  emojiPositionsRaw.forEach(emojiPositionRaw => {
      let emojiPosition = emojiPositionRaw.split("-");
      emojiPosition[0] = parseInt(emojiPosition[0])
      emojiPosition[1] = parseInt(emojiPosition[1])
     emotesProccessed[emoteRaw[0]].push(emojiPosition)
  });
 
});

console.log(emotesProccessed);
Was darin resultieren würde, dass emotesRaw umgewandelt wird in
Code:
{
  111173885: [[34, 36], [12, 17]],
  555555560: [[17, 18], [73, 74]]
}

Jetzt kannst du einfach sagen:
Javascript:
let emotesProccessed = {

  111173885: [
    [34, 36],
    [12, 17]
  ],

  555555560: [
    [17, 18],
    [73, 74]
  ]

}

let message = "...";

message = message.split("");

Object.keys(emotesProccessed).forEach(key => {
  emotesProccessed[key].forEach(emotePosition => {
    let from = emotePosition[0];
    let to = emotePosition[1];
    message[from] = `<img src="" id="${key}" />`
    from++;
    while (from < to) {
        message[from] = "";
      from++;
    }
  })
})

message = message.join("");

console.log(message);

Ich kann es leider nicht testen und musste es Freihand schreiben, kann also sein, dass da noch Fehler im Code sind. Aber das Grundprinzip sollte klar sein.

message[from] = `<img src="" id="${key}" />` Da muss dann natürlich das richtige Bild rein.
 
Zurück
Oben