XSS-Prävention in der JSP / Servlet-Webanwendung


Antworten:


111

XSS kann in JSP durch Verwendung des JSTL- <c:out> Tags oder der fn:escapeXml()EL-Funktion verhindert werden, wenn benutzergesteuerte Eingaben (erneut) angezeigt werden . Dies umfasst Anforderungsparameter, Header, Cookies, URL, Text usw. Alles, was Sie aus dem Anforderungsobjekt extrahieren. Auch die benutzergesteuerte Eingabe von früheren Anforderungen, die in einer Datenbank gespeichert sind, muss während der erneuten Anzeige maskiert werden.

Zum Beispiel:

<p><c:out value="${bean.userControlledValue}"></p>
<p><input name="foo" value="${fn:escapeXml(param.foo)}"></p>

Dies wird Escape - Zeichen , die das gerenderte HTML malform können wie <, >, ", 'und &in HTML / XML - Entitäten wie &lt;, &gt;, &quot;, &apos;und &amp;.

Beachten Sie, dass Sie sie nicht im Java-Code (Servlet) maskieren müssen, da sie dort harmlos sind. Einige können sich entscheiden , sie während zu entkommen Anfrage Verarbeitung (wie Sie in Servlet oder Filtern zu tun) statt Antwortverarbeitung (wie Sie in JSP zu tun), aber auf diese Weise riskieren Sie, dass die Daten unnötig bekommen doppelten Escape (zB &wird &amp;amp;statt &amp;und letztendlich würde der Endbenutzer sehen&amp;angezeigt werden) oder dass die in der DB gespeicherten Daten nicht mehr portierbar sind (z. B. beim Exportieren von Daten nach JSON, CSV, XLS, PDF usw., für die überhaupt kein HTML-Escape erforderlich ist). Sie verlieren auch die soziale Kontrolle, weil Sie nicht mehr wissen, was der Benutzer tatsächlich ausgefüllt hat. Als Site-Administrator möchten Sie wirklich wissen, welche Benutzer / IPs versuchen, XSS auszuführen, damit Sie leicht nachverfolgen können sie und ergreifen Maßnahmen entsprechend. Das Entkommen während der Anforderungsverarbeitung sollte nur dann und nur dann als neuester Ausweg verwendet werden, wenn Sie wirklich ein Zugunglück einer schlecht entwickelten Legacy-Webanwendung in kürzester Zeit beheben müssen. Dennoch sollten Sie Ihre JSP-Dateien letztendlich neu schreiben, um XSS-sicher zu werden.

Wenn Sie möchten , benutzergesteuerte Eingabe als HTML erneut anzuzeigen , wobei Sie nur eine bestimmte Untergruppe von HTML - Tags wie ermöglichen möchten <b>, <i>, <u>usw, dann müssen Sie die Eingabe mit einer weißen Liste sanieren. Sie können hierfür einen HTML-Parser wie Jsoup verwenden . Viel besser ist es jedoch, eine benutzerfreundliche Markup-Sprache wie Markdown einzuführen (die auch hier bei Stack Overflow verwendet wird). Dann können Sie hierfür einen Markdown-Parser wie CommonMark verwenden . Es hat auch HTML-Bereinigungsfunktionen eingebaut. Siehe auch Markdown oder HTML .

Das einzige Problem auf der Serverseite in Bezug auf Datenbanken ist die Verhinderung der SQL-Injektion . Sie müssen sicherstellen, dass Sie niemals benutzergesteuerte Eingaben direkt in der SQL- oder JPQL-Abfrage mit Zeichenfolgen verketten und dass Sie vollständig parametrisierte Abfragen verwenden. In JDBC-Begriffen bedeutet dies, dass Sie PreparedStatementanstelle von verwenden sollten Statement. Verwenden Sie in JPA-Begriffen Query.


Eine Alternative wäre die Migration von JSP / Servlet auf das MVC-Framework JSF von Java EE . Es hat überall XSS (und CSRF!) Prävention eingebaut. Siehe auch CSRF-, XSS- und SQL Injection-Angriffsprävention in JSF .


1
Nur weil Sie den Ruhezustand verwenden, bedeutet dies nicht, dass Sie vor SQL-Injection sicher sind. Siehe zum Beispiel blog.harpoontech.com/2008/10/… .
MatrixFrog

@chad: das stimmt nicht. Dies ist nur dann der Fall, wenn Sie benutzergesteuerte Eingaben direkt in der SQL / HQL / JPQL-Abfrage wie folgt verketten, "SELECT ... WHERE SOMEVAL = " + somevalanstatt wie gezeigt parametrisierte Abfragen zu verwenden. Kein ORM kann sich vor solchen Entwicklerfehlern schützen.
BalusC

5
Ich denke, Sie müssen auch auf dem Server validieren. Die gesamte Validierung kann durch Ändern der HTTP-Parameter umgangen werden. Und manchmal können die Daten, die Sie beibehalten, von anderen Anwendungen in einer Unternehmensanwendung verwendet werden. Manchmal haben Sie keinen Zugriff auf die Ansichten der anderen Anwendungen, daher müssen Sie die Eingabe bereinigen, bevor Sie in der Datenbank verbleiben.
Guido Celada

1
@ Guido: Du verstehst das Problem nicht.
BalusC

2
@peater: Ja, wenn Sie nicht vertrauenswürdige Daten in JS-Code einfügen, müssen Sie JS-Codierung anstelle von HTML-Codierung verwenden. Wenn Sie nicht vertrauenswürdige Daten in CSS-Code einfügen, müssen Sie CSS-Codierung anstelle von HTML-Codierung verwenden. Wenn Sie nicht vertrauenswürdige Daten in URLs einfügen, müssen Sie die URL-Codierung anstelle der HTML-Codierung verwenden. Die HTML-Codierung sollte nur verwendet werden, um nicht vertrauenswürdige Daten in HTML-Code einzufügen.
BalusC

12

Das How-to-Prevent-xss wurde mehrmals gefragt. In StackOverflow finden Sie viele Informationen. Außerdem enthält die OWASP-Website einen XSS-Präventions-Spickzettel , den Sie durchgehen sollten.

In den zu verwendenden Bibliotheken hat die ESAPI-Bibliothek von OWASP eine Java -Variante . Das solltest du ausprobieren. Außerdem bietet jedes Framework, das Sie verwenden, einen gewissen Schutz gegen XSS. Auch hier enthält die OWASP-Website Informationen zu den beliebtesten Frameworks. Ich würde daher empfehlen, deren Website zu durchsuchen.


Die OWASP-Spickzettel wurden auf GitHub verschoben. Hier ist der Link für das XSS Prevention Cheat Sheet github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/…
peater

12

Ich hatte großes Glück mit OWASP Anti-Samy und einem AspectJ-Berater für alle meine Federregler, die XSS daran hindern, einzusteigen.

public class UserInputSanitizer {

    private static Policy policy;
    private static AntiSamy antiSamy;

    private static AntiSamy getAntiSamy() throws PolicyException  {
        if (antiSamy == null) {
            policy = getPolicy("evocatus-default");
            antiSamy = new AntiSamy();
        }
        return antiSamy;

    }

    public static String sanitize(String input) {
        CleanResults cr;
        try {
            cr = getAntiSamy().scan(input, policy);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return cr.getCleanHTML();
    }

    private static Policy getPolicy(String name) throws PolicyException {
        Policy policy = 
            Policy.getInstance(Policy.class.getResourceAsStream("/META-INF/antisamy/" + name + ".xml"));
        return policy;
    }

}

Den AspectJ-Advisor erhalten Sie in diesem Stackoverflow-Beitrag

Ich denke, dies ist ein besserer Ansatz als c: out, insbesondere wenn Sie viel Javascript verwenden.


Die übliche Praxis besteht darin, benutzergesteuerte Daten während der erneuten Anzeige HTML-maskiert zu entfernen, weder während der Verarbeitung der übermittelten Daten im Servlet noch während der Speicherung in der Datenbank. Wenn Sie es während der Verarbeitung der übermittelten Daten und / oder der Speicherung in der Datenbank HTML-maskieren, ist alles über den Geschäftscode und / oder in der Datenbank verteilt. Dies ist nur ein Wartungsproblem, und Sie riskieren Doppelfluchten oder mehr, wenn Sie dies an verschiedenen Orten tun. Der Geschäftscode und die Datenbank sind wiederum für XSS nicht vertraulich. Nur die Ansicht ist. Sie sollten es dann nur genau dort in Sichtweite entkommen.
Shubham Maheshwari

1
Ja und nein. Obwohl die allgemeine Praxis darin besteht, auf dem Display zu entkommen, gibt es viele Gründe, die Sie beim Schreiben möglicherweise bereinigen möchten. In einigen Fällen möchten Sie, dass Ihre Benutzer eine Teilmenge von HTML eingeben, und obwohl Sie die Anzeige bereinigen können, ist dies für Benutzer tatsächlich ziemlich langsam und sogar verwirrend. Zweitens, wenn Sie die Daten mit Diensten von Drittanbietern wie externen APIs teilen, können diese Dienste die ordnungsgemäße Bereinigung selbst durchführen oder nicht.
Adam Gent

Wie Sie und ich beide erwähnt haben, besteht die "normale Praxis" darin, auf dem Display zu entkommen. Was Sie in Ihrem obigen Kommentar erwähnt haben, sind spezifischere Anwendungsfälle und würden daher angenehm spezifische Lösungen erfordern.
Shubham Maheshwari

Ja, ich sollte vielleicht meinen Anwendungsfall klarer machen. Ich beschäftige mich hauptsächlich mit Content Management (HTML-Bearbeitung).
Adam Gent

8

Die Verwaltung von XSS erfordert mehrere Überprüfungen, Daten von der Clientseite.

  1. Eingabevalidierungen (Formularvalidierung) auf der Serverseite. Es gibt mehrere Möglichkeiten, dies zu tun. Sie können die JSR 303-Bean-Validierung ( Hibernate Validator ) oder das ESAPI Input Validation Framework ausprobieren . Obwohl ich es (noch) nicht selbst ausprobiert habe, gibt es eine Anmerkung, die nach sicherem HTML (@SafeHtml) sucht . Sie könnten tatsächlich den Hibernate-Validator mit Spring MVC für Bean-Validierungen verwenden -> Ref
  2. Escape-URL-Anforderungen - Verwenden Sie für alle Ihre HTTP-Anforderungen eine Art XSS-Filter. Ich habe Folgendes für unsere Web-App verwendet und sie kümmert sich um die Bereinigung der HTTP-URL-Anforderung - http://www.servletsuite.com/servlets/xssflt.htm
  3. Escape-Daten / HTML werden an den Client zurückgegeben (siehe oben unter @BalusC-Erklärung).

3

Ich würde empfehlen, regelmäßig mit einem automatisierten Tool auf Schwachstellen zu testen und alle gefundenen Probleme zu beheben. Es ist viel einfacher, eine Bibliothek vorzuschlagen, um bei einer bestimmten Sicherheitsanfälligkeit zu helfen, als bei allen XSS-Angriffen im Allgemeinen.

Skipfish ist ein Open-Source-Tool von Google, das ich untersucht habe: Es findet ziemlich viel Material und scheint es wert zu sein, es zu verwenden.


Prävention ist besser als Diagnose (z. B. Skipfish), gefolgt von nachfolgenden Schnellkorrekturen.
Sripathi Krishnan

2
Ich stimme dir nicht zu. Prävention ohne Diagnose ist nur ein Dogma. Führen Sie die Diagnose als Teil Ihres CI-Zyklus aus, um das Problem der "schnellen Lösung" zu vermeiden.
Sean Reilly

3

Es gibt keine einfache, sofort einsatzbereite Lösung gegen XSS. Die OWASP ESAPI-API bietet Unterstützung für das Escape, was sehr nützlich ist, und sie verfügt über Tag-Bibliotheken.

Mein Ansatz war es, die Stuts 2-Tags auf folgende Weise zu erweitern.

  1. Ändern Sie das s: property-Tag so, dass zusätzliche Attribute erforderlich sind, die angeben, welche Art von Escape erforderlich ist (EscapeHtmlAttribute = "true" usw.). Dazu müssen neue Property- und PropertyTag-Klassen erstellt werden. Die Property-Klasse verwendet die OWASP ESAPI-API für das Escapezeichen.
  2. Ändern Sie die Freemarker-Vorlagen, um die neue Version der Eigenschaft s: zu verwenden, und legen Sie das Escapezeichen fest.

Wenn Sie die Klassen in Schritt 1 nicht ändern möchten, besteht ein anderer Ansatz darin, die ESAPI-Tags in die Freemarker-Vorlagen zu importieren und bei Bedarf zu maskieren. Wenn Sie dann als: property-Tag in Ihrer JSP verwenden müssen, schließen Sie es mit und ESAPI-Tag ein.

Ich habe hier eine ausführlichere Erklärung geschrieben.

http://www.nutshellsoftware.org/software/securing-struts-2-using-esapi-part-1-securing-outputs/

Ich bin damit einverstanden, dass es nicht ideal ist, Eingaben zu entkommen.


2

Meine persönliche Meinung ist, dass Sie die Verwendung von JSP / ASP / PHP / etc-Seiten vermeiden sollten. Ausgabe stattdessen an eine SAX-ähnliche API (nur zum Aufrufen und nicht zum Behandeln vorgesehen). Auf diese Weise gibt es eine einzelne Schicht, die eine wohlgeformte Ausgabe erzeugen muss.


2

Wenn Sie automatisch alle JSP-Variablen maskieren möchten, ohne jede Variable explizit umbrechen zu müssen, können Sie einen EL-Resolver verwenden, wie hier beschrieben mit vollständiger Quelle und einem Beispiel (JSP 2.0 oder neuer) , der hier ausführlicher erläutert wird :

Wenn Sie beispielsweise den oben genannten EL-Resolver verwenden, bleibt Ihr JSP-Code unverändert, aber jede Variable wird vom Resolver automatisch maskiert

...
<c:forEach items="${orders}" var="item">
  <p>${item.name}</p>
  <p>${item.price}</p>
  <p>${item.description}</p>
</c:forEach>
...

Wenn Sie im Frühjahr standardmäßig das Escape erzwingen möchten, können Sie dies ebenfalls berücksichtigen, aber es entgeht nicht den EL-Ausdrücken, sondern nur der Tag-Ausgabe.

http://forum.springsource.org/showthread.php?61418-Spring-cross-site-scripting&p=205646#post205646

Hinweis: Ein weiterer Ansatz zur EL-Escape-Methode, bei dem XSL-Transformationen zur Vorverarbeitung von JSP-Dateien verwendet werden, finden Sie hier:

http://therning.org/niklas/2007/09/preprocessing-jsp-files-to-automatically-escape-el-expressions/


Hallo Brad, der oben genannte Anwendungsfall ist das, wonach ich suche. Könnten Sie bitte erklären, wie Sie xss im Fall des obigen Szenarios (für
jedes

Das einzige, an das ich mich jetzt wirklich erinnere, ist die Verwendung des EL-Resolvers - das haben wir letztendlich in unserem Unternehmen verwendet. Grundsätzlich entgeht es automatisch allem, und wenn Sie wirklich nicht möchten, dass etwas entweicht, können Sie es <enhance:out escapeXml="false">wie im Artikel beschrieben einpacken.
Brad Parks

0

Wenn Sie sicherstellen möchten, dass Ihr $Operator nicht unter XSS-Hack leidet, können ServletContextListenerSie dort einige Überprüfungen durchführen.

Die vollständige Lösung finden Sie unter: http://pukkaone.github.io/2011/01/03/jsp-cross-site-scripting-elresolver.html

@WebListener
public class EscapeXmlELResolverListener implements ServletContextListener {
    private static final Logger LOG = LoggerFactory.getLogger(EscapeXmlELResolverListener.class);


    @Override
    public void contextInitialized(ServletContextEvent event) {
        LOG.info("EscapeXmlELResolverListener initialized ...");        
        JspFactory.getDefaultFactory()
                .getJspApplicationContext(event.getServletContext())
                .addELResolver(new EscapeXmlELResolver());

    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        LOG.info("EscapeXmlELResolverListener destroyed");
    }


    /**
     * {@link ELResolver} which escapes XML in String values.
     */
    public class EscapeXmlELResolver extends ELResolver {

        private ThreadLocal<Boolean> excludeMe = new ThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return Boolean.FALSE;
            }
        };

        @Override
        public Object getValue(ELContext context, Object base, Object property) {

            try {
                    if (excludeMe.get()) {
                        return null;
                    }

                    // This resolver is in the original resolver chain. To prevent
                    // infinite recursion, set a flag to prevent this resolver from
                    // invoking the original resolver chain again when its turn in the
                    // chain comes around.
                    excludeMe.set(Boolean.TRUE);
                    Object value = context.getELResolver().getValue(
                            context, base, property);

                    if (value instanceof String) {
                        value = StringEscapeUtils.escapeHtml4((String) value);
                    }
                    return value;
            } finally {
                excludeMe.remove();
            }
        }

        @Override
        public Class<?> getCommonPropertyType(ELContext context, Object base) {
            return null;
        }

        @Override
        public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base){
            return null;
        }

        @Override
        public Class<?> getType(ELContext context, Object base, Object property) {
            return null;
        }

        @Override
        public boolean isReadOnly(ELContext context, Object base, Object property) {
            return true;
        }

        @Override
        public void setValue(ELContext context, Object base, Object property, Object value){
            throw new UnsupportedOperationException();
        }

    }

}

Nochmals: Dies schützt nur die $. Bitte beachten Sie auch andere Antworten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.